In einer vorgegebenen Liste von Elementen, mag ich das Element mit einer bestimmten Eigenschaft erhalten und sie aus der Liste entfernen. Die beste Lösung, die ich gefunden habe, ist:
ProducerDTO p = producersProcedureActive
.stream()
.filter(producer -> producer.getPod().equals(pod))
.findFirst()
.get();
producersProcedureActive.remove(p);
Ist es möglich, get und remove in einem Lambda-Ausdruck zu kombinieren?
java
lambda
java-8
java-stream
Marco Stramezzi
quelle
quelle
get()
hier an! Sie haben keine Ahnung, ob es leer ist oder nicht. Sie werden eine Ausnahme auslösen, wenn das Element nicht vorhanden war. Verwenden Sie stattdessen eine der sicheren Methoden wie ifPresent, orElse, orElseGet oder orElseThrow.list
für die dasPredicate
wahr ist, oder nur das erste (von möglicherweise null, einem oder mehreren Elementen)?Antworten:
Element aus der Liste entfernen
z.B:
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);
AUSGABE: A B C.
quelle
removeIf
ist eine elegante Lösung zum Entfernen von Elementen aus einer Sammlung, gibt jedoch das entfernte Element nicht zurück.Obwohl der Thread ziemlich alt ist, wird immer noch angenommen, dass er eine Lösung bietet
Java8
.Nutzen Sie die
removeIf
Funktion. Zeitliche Komplexität istO(n)
API-Referenz: removeIf docs
Annahme:
producersProcedureActive
ist aList
HINWEIS: Mit diesem Ansatz können Sie das gelöschte Element nicht mehr erfassen.
quelle
Erwägen Sie die Verwendung von Vanilla Java-Iteratoren, um die Aufgabe auszuführen:
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; }
Vorteile :
Iterable
auch ohnestream()
Unterstützung tun (zumindest diejenigen, dieremove()
auf ihrem Iterator implementiert sind ) .Nachteile :
Wie für die
Andere Antworten zeigen deutlich, dass dies möglich ist, aber Sie sollten sich dessen bewusst sein
ConcurrentModificationException
kann ausgelöst werden, wenn ein Element aus der Liste entfernt wird, die iteriert wirdquelle
remove()
Methoden, die UOE auslösen . (Natürlich nicht für JDK-Sammlungen, aber ich denke, es ist unfair zu sagen, dass "auf jedem Iterable funktioniert".)default
Implementierung vonremoveIf
macht die gleiche Annahme, aber natürlich ist esCollection
eher definiert alsIterable
...Die direkte Lösung wäre, die
ifPresent(consumer)
von zurückgegebene Option aufzurufenfindFirst()
. Dieser Consumer wird aufgerufen, wenn das optionale nicht leer ist. Der Vorteil ist auch, dass keine Ausnahme ausgelöst wird, wenn die Suchoperation ein leeres optionales Element zurückgibt, wie es Ihr aktueller Code tun würde. Stattdessen wird nichts passieren.Wenn Sie den entfernten Wert zurückgeben möchten, können Sie
map
dasOptional
zum Ergebnis des Aufrufs führenremove
:producersProcedureActive.stream() .filter(producer -> producer.getPod().equals(pod)) .findFirst() .map(p -> { producersProcedureActive.remove(p); return p; });
Beachten Sie jedoch, dass die
remove(Object)
Operation die Liste erneut durchläuft, um das zu entfernende Element zu finden. Wenn Sie eine Liste mit wahlfreiem Zugriff haben, wie z. B. eineArrayList
, ist es besser, einen Stream über die Indizes der Liste zu erstellen und den ersten Index zu finden, der dem Prädikat entspricht:IntStream.range(0, producersProcedureActive.size()) .filter(i -> producersProcedureActive.get(i).getPod().equals(pod)) .boxed() .findFirst() .map(i -> producersProcedureActive.remove((int) i));
Bei dieser Lösung arbeitet die
remove(int)
Operation direkt mit dem Index.quelle
LinkedList
Sie die Stream-API möglicherweise nicht verwenden, da es keine Lösung gibt, ohne mindestens zweimal zu durchlaufen. Ich kenne jedoch kein reales Szenario, in dem der akademische Vorteil einer verknüpften Liste den tatsächlichen Aufwand kompensieren kann. Die einfache Lösung ist also, niemals zu verwendenLinkedList
.remove(Object)
nur eineboolean
Meldung zurückgegeben wird, ob ein Element entfernt werden musste oder nicht.boxed()
bekommt man einOptionalInt
was nurmap
vonint
bis kannint
. Im Gegensatz dazuIntStream
gibt es keinemapToObj
Methode. Mit erhaltenboxed()
Sie eine,Optional<Integer>
die es ermöglicht,map
zu einem beliebigen Objekt, dh demProducerDTO
von zurückgegebenenremove(int)
. Die Besetzung vonInteger
bisint
ist notwendig, um zwischenremove(int)
und zu unterscheidenremove(Object)
.Use kann den Filter von Java 8 verwenden und eine weitere Liste erstellen, wenn Sie die alte Liste nicht ändern möchten:
quelle
Ich bin sicher, dass dies eine unpopuläre Antwort sein wird, aber es funktioniert ...
ProducerDTO[] p = new ProducerDTO[1]; producersProcedureActive .stream() .filter(producer -> producer.getPod().equals(pod)) .findFirst() .ifPresent(producer -> {producersProcedureActive.remove(producer); p[0] = producer;}
p[0]
wird entweder das gefundene Element enthalten oder null sein.Der "Trick" hier besteht darin, das "effektiv endgültige" Problem zu umgehen, indem eine Array- Referenz verwendet wird, die effektiv endgültig ist, aber ihr erstes Element setzt.
quelle
.orElse(null)
, um dasProducerDTO
odernull
....orElse(null)
einif
, nein?remove()
zu , indem SieorElse(null)
?if(p!=null) producersProcedureActive.remove(p);
Das ist immer noch kürzer als der Lambda-Ausdruck in IhremifPresent
Anruf.Mit Eclipse Collections können Sie
detectIndex
zusammen mitremove(int)
jeder java.util.List verwenden.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);
Wenn Sie den
MutableList
Typ aus Eclipse-Sammlungen verwenden, können Sie diedetectIndex
Methode direkt in der Liste aufrufen .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);
Hinweis: Ich bin ein Committer für Eclipse-Sammlungen
quelle
Wenn wir wollen mehrere Elemente aus einer Liste in eine neue Liste aufnehmen (mithilfe eines Prädikats filtern) und sie aus der vorhandenen Liste entfernen , konnte ich nirgendwo eine richtige Antwort finden.
Hier erfahren Sie, wie Sie dies mithilfe der Java Streaming API-Partitionierung tun können.
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);
Auf diese Weise entfernen Sie die gefilterten Elemente effektiv aus der ursprünglichen Liste und fügen sie einer neuen Liste hinzu.
Siehe 5.2. Collectors.partitioningBy Abschnitt dieses Artikels .
quelle
Wie andere vorgeschlagen haben, könnte dies ein Anwendungsfall für Schleifen und Iterables sein. Meiner Meinung nach ist dies der einfachste Ansatz. Wenn Sie die Liste direkt ändern möchten, kann sie ohnehin nicht als "echte" Funktionsprogrammierung angesehen werden. Sie können jedoch
Collectors.partitioningBy()
eine neue Liste mit Elementen erstellen, die Ihrer Bedingung entsprechen, und eine neue Liste mit Elementen, die dies nicht tun. Wenn Sie bei diesem Ansatz mehrere Elemente haben, die die Bedingung erfüllen, befinden sich natürlich alle in dieser Liste und nicht nur die erste.quelle
Die folgende Logik ist die Lösung, ohne die ursprüngliche Liste zu ändern
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"]
quelle
Durch die Kombination meiner ursprünglichen Idee und Ihrer Antworten gelangte ich zu der Lösung für meine eigene Frage:
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; }
Damit können Sie das Objekt entfernen, indem Sie
remove(int)
die Liste nicht erneut durchlaufen (wie von @Tunaki vorgeschlagen) und das entfernte Objekt an den Funktionsaufrufer zurückgeben.Ich habe Ihre Antworten gelesen, die mir vorschlagen, sichere Methoden wie
ifPresent
anstelle von zu wählenget
aber ich finde keine Möglichkeit, sie in diesem Szenario zu verwenden.Gibt es einen wichtigen Nachteil bei dieser Art von Lösung?
Bearbeiten Sie die folgenden @ Holger-Ratschläge
Dies sollte die Funktion sein, die ich brauchte
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; }); }
quelle
get
die Ausnahme nicht verwenden und abfangen. Das ist nicht nur ein schlechter Stil, sondern kann auch zu einer schlechten Leistung führen. Die saubere Lösung ist noch einfacher,return /* stream operation*/.findFirst() .map(i -> producersProcedureActive.remove((int)i)) .orElseGet(() -> { logger.error("No producer found with POD [" + pod + "]"); return null; });
Die Aufgabe ist: Holen Sie sich ✶ und ✶ entfernen Sie das Element aus der 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 } ) );
quelle