Étant donné une liste d'éléments, je veux obtenir l'élément avec une propriété donnée et le supprimer de la liste. La meilleure solution que j'ai trouvée est:
ProducerDTO p = producersProcedureActive
.stream()
.filter(producer -> producer.getPod().equals(pod))
.findFirst()
.get();
producersProcedureActive.remove(p);
Est-il possible de combiner get et remove dans une expression lambda?
java
lambda
java-8
java-stream
Marco Stramezzi
la source
la source
get()
ici! Vous ne savez pas si c'est vide ou non. Vous lancerez une exception si l'élément n'était pas là. À la place, utilisez l'une des méthodes sûres comme ifPresent, orElse, orElseGet ou orElseThrow.list
pour lesquels lePredicate
est vrai ou seulement le premier (d'un éventuellement zéro, un ou plusieurs éléments)?Réponses:
Pour supprimer un élément de la liste
par exemple:
objectA.removeIf(x -> blockedWorkerIds.contains(x)); List<String> str1 = new ArrayList<String>(); str1.add("A"); str1.add("B"); str1.add("C"); str1.add("D"); List<String> str2 = new ArrayList<String>(); str2.add("D"); str2.add("E"); str1.removeIf(x -> str2.contains(x)); str1.forEach(System.out::println);
SORTIE: A B C
la source
removeIf
est une solution élégante pour supprimer des éléments d'une collection, mais elle ne renvoie pas l'élément supprimé.Bien que le fil soit assez ancien, il est toujours pensé pour fournir une solution
Java8
.Utilisez la
removeIf
fonction. La complexité du temps estO(n)
Référence API: removeIf docs
Hypothèse:
producersProcedureActive
est unList
REMARQUE: Avec cette approche, vous ne serez pas en mesure de récupérer l'élément supprimé.
la source
Pensez à utiliser des itérateurs Java vanilla pour effectuer la tâche:
public static <T> T findAndRemoveFirst(Iterable<? extends T> collection, Predicate<? super T> test) { T value = null; for (Iterator<? extends T> it = collection.iterator(); it.hasNext();) if (test.test(value = it.next())) { it.remove(); return value; } return null; }
Avantages :
Iterable
même sansstream()
support (au moins ceux qui implémententremove()
sur leur itérateur) .Inconvénients :
En ce qui concerne la
d'autres réponses montrent clairement que c'est possible, mais vous devez être conscient
ConcurrentModificationException
peut être lancé lors de la suppression d'un élément de la liste en cours d'itérationla source
remove()
méthodes qui lancent UOE. (Pas ceux pour les collections JDK, bien sûr, mais je pense qu'il est injuste de dire "fonctionne sur n'importe quel Iterable".)default
mise en œuvre deremoveIf
fait la même hypothèse, mais, bien sûr, elle est définie surCollection
plutôt queIterable
…La solution directe serait d'appeler
ifPresent(consumer)
sur l'Optionnel renvoyé parfindFirst()
. Ce consommateur sera appelé lorsque l'option facultative n'est pas vide. L'avantage est également qu'il ne lèvera pas d'exception si l'opération de recherche retournait une option vide, comme le ferait votre code actuel; au lieu de cela, rien ne se passera.Si vous souhaitez renvoyer la valeur supprimée, vous pouvez
map
accéderOptional
au résultat de l'appelremove
:producersProcedureActive.stream() .filter(producer -> producer.getPod().equals(pod)) .findFirst() .map(p -> { producersProcedureActive.remove(p); return p; });
Mais notez que l'
remove(Object)
opération parcourra à nouveau la liste pour trouver l'élément à supprimer. Si vous avez une liste à accès aléatoire, comme unArrayList
, il serait préférable de faire un Stream sur les index de la liste et de trouver le premier index correspondant au prédicat:IntStream.range(0, producersProcedureActive.size()) .filter(i -> producersProcedureActive.get(i).getPod().equals(pod)) .boxed() .findFirst() .map(i -> producersProcedureActive.remove((int) i));
Avec cette solution, l'
remove(int)
opération opère directement sur l'index.la source
LinkedList
vous ne devriez peut-être pas utiliser l'API de flux car il n'y a pas de solution sans traverser au moins deux fois. Mais je ne connais aucun scénario réel dans lequel l'avantage académique d'une liste chaînée peut compenser ses frais généraux réels. La solution simple est donc de ne jamais utiliserLinkedList
.remove(Object)
ne fait queboolean
savoir s'il y avait un élément à supprimer ou non.boxed()
vous obtenez unOptionalInt
qui ne peut quemap
deint
àint
. Contrairement àIntStream
, il n'y a pas demapToObj
méthode. Avecboxed()
, vous obtiendrez unOptional<Integer>
qui permet d' accédermap
à un objet arbitraire, c'est à dire leProducerDTO
renvoyé parremove(int)
. La distribution deInteger
àint
est nécessaire pour lever l'ambiguïté entreremove(int)
etremove(Object)
.Utilisez peut utiliser le filtre de Java 8, et créer une autre liste si vous ne voulez pas changer l'ancienne liste:
la source
Je suis sûr que ce sera une réponse impopulaire, mais cela fonctionne ...
ProducerDTO[] p = new ProducerDTO[1]; producersProcedureActive .stream() .filter(producer -> producer.getPod().equals(pod)) .findFirst() .ifPresent(producer -> {producersProcedureActive.remove(producer); p[0] = producer;}
p[0]
contiendra l'élément trouvé ou sera nul.Le "truc" ici est de contourner le problème "effectivement final" en utilisant une référence de tableau qui est effectivement finale, mais en définissant son premier élément.
la source
.orElse(null)
pour obtenir leProducerDTO
ounull
….orElse(null)
et d'avoir unif
, non?remove()
aussi en utilisantorElse(null)
?if(p!=null) producersProcedureActive.remove(p);
c'est encore plus court que l'expression lambda de votreifPresent
appel.Avec les collections Eclipse, vous pouvez utiliser
detectIndex
avecremove(int)
sur n'importe quel java.util.List.List<Integer> integers = Lists.mutable.with(1, 2, 3, 4, 5); int index = Iterate.detectIndex(integers, i -> i > 2); if (index > -1) { integers.remove(index); } Assert.assertEquals(Lists.mutable.with(1, 2, 4, 5), integers);
Si vous utilisez le
MutableList
type d'Eclipse Collections, vous pouvez appeler ladetectIndex
méthode directement dans la liste.MutableList<Integer> integers = Lists.mutable.with(1, 2, 3, 4, 5); int index = integers.detectIndex(i -> i > 2); if (index > -1) { integers.remove(index); } Assert.assertEquals(Lists.mutable.with(1, 2, 4, 5), integers);
Remarque: je suis un committer pour les collections Eclipse
la source
Lorsque nous voulons obtenir plusieurs éléments d'une liste dans une nouvelle liste (filtre à l'aide d'un prédicat) et les supprimer de la liste existante , je ne trouve nulle part de réponse correcte.
Voici comment nous pouvons le faire en utilisant le partitionnement de l'API Java Streaming.
Map<Boolean, List<ProducerDTO>> classifiedElements = producersProcedureActive .stream() .collect(Collectors.partitioningBy(producer -> producer.getPod().equals(pod))); // get two new lists List<ProducerDTO> matching = classifiedElements.get(true); List<ProducerDTO> nonMatching = classifiedElements.get(false); // OR get non-matching elements to the existing list producersProcedureActive = classifiedElements.get(false);
De cette façon, vous supprimez efficacement les éléments filtrés de la liste d'origine et les ajoutez à une nouvelle liste.
Reportez-vous au 5.2. Collectors.partitioningBy section de cet article .
la source
Comme d'autres l'ont suggéré, cela pourrait être un cas d'utilisation pour les boucles et les itérables. À mon avis, c'est l'approche la plus simple. Si vous souhaitez modifier la liste sur place, elle ne peut de toute façon pas être considérée comme une programmation fonctionnelle "réelle". Mais vous pouvez utiliser
Collectors.partitioningBy()
pour obtenir une nouvelle liste avec des éléments qui satisfont votre condition, et une nouvelle liste de ceux qui ne le font pas. Bien sûr, avec cette approche, si vous avez plusieurs éléments satisfaisant à la condition, tous seront dans cette liste et pas seulement le premier.la source
La logique ci-dessous est la solution sans modifier la liste d'origine
List<String> str1 = new ArrayList<String>(); str1.add("A"); str1.add("B"); str1.add("C"); str1.add("D"); List<String> str2 = new ArrayList<String>(); str2.add("D"); str2.add("E"); List<String> str3 = str1.stream() .filter(item -> !str2.contains(item)) .collect(Collectors.toList()); str1 // ["A", "B", "C", "D"] str2 // ["D", "E"] str3 // ["A", "B", "C"]
la source
En combinant mon idée initiale et vos réponses, j'ai atteint ce qui semble être la solution à ma propre question:
public ProducerDTO findAndRemove(String pod) { ProducerDTO p = null; try { p = IntStream.range(0, producersProcedureActive.size()) .filter(i -> producersProcedureActive.get(i).getPod().equals(pod)) .boxed() .findFirst() .map(i -> producersProcedureActive.remove((int)i)) .get(); logger.debug(p); } catch (NoSuchElementException e) { logger.error("No producer found with POD [" + pod + "]"); } return p; }
Il permet de supprimer l'objet en utilisant
remove(int)
qui ne parcourt pas à nouveau la liste (comme suggéré par @Tunaki) et il permet de renvoyer l'objet supprimé à l'appelant de la fonction.J'ai lu vos réponses qui me suggèrent de choisir des méthodes sûres comme
ifPresent
au lieu deget
mais je ne trouve pas de moyen de les utiliser dans ce scénario.Y a-t-il un inconvénient important dans ce type de solution?
Modifier en suivant les conseils de @Holger
Cela devrait être la fonction dont j'avais besoin
public ProducerDTO findAndRemove(String pod) { return IntStream.range(0, producersProcedureActive.size()) .filter(i -> producersProcedureActive.get(i).getPod().equals(pod)) .boxed() .findFirst() .map(i -> producersProcedureActive.remove((int)i)) .orElseGet(() -> { logger.error("No producer found with POD [" + pod + "]"); return null; }); }
la source
get
et intercepter l'exception. Ce n'est pas seulement un mauvais style, mais cela peut également entraîner de mauvaises performances. La solution propre est encore plus simple,return /* stream operation*/.findFirst() .map(i -> producersProcedureActive.remove((int)i)) .orElseGet(() -> { logger.error("No producer found with POD [" + pod + "]"); return null; });
la tâche est: obtenir get et ✶ supprimer l'élément de la liste
p.stream().collect( Collectors.collectingAndThen( Collector.of( ArrayDeque::new, (a, producer) -> { if( producer.getPod().equals( pod ) ) a.addLast( producer ); }, (a1, a2) -> { return( a1 ); }, rslt -> rslt.pollFirst() ), (e) -> { if( e != null ) p.remove( e ); // remove return( e ); // get } ) );
la source