wake-up-neo.net

Java 8-Lambda-Element abrufen und aus der Liste entfernen

Bei einer Liste von Elementen möchte ich das Element mit einer bestimmten Eigenschaft und 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, in einem Lambda-Ausdruck „Holen“ und „Entfernen“ zu kombinieren?

43
Marco Stramezzi

Um ein Element aus der Liste zu entfernen  

objectA.removeIf (x -> Bedingungen);

zB: objectA.removeIf (x -> disabledWorkerIds.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

74
uma shankar

Obwohl der Thread schon recht alt ist, wird angenommen, dass er eine Lösung bietet - mit Java8.

Nutzen Sie die removeIf-Funktion. Zeitaufwand ist O(n)

producersProcedureActive.removeIf(producer -> producer.getPod().equals(pod));

API-Referenz: removeIf docs

Annahme: producersProcedureActive ist eine List

HINWEIS: Mit diesem Ansatz können Sie das gelöschte Element nicht mehr erreichen.

17
asifsid88

Verwenden Sie 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 :

  1. Es ist offensichtlich und offensichtlich.
  2. Es fährt nur einmal und nur bis zum passenden Element.
  3. Sie können dies für jede Iterable auch ohne stream()-Unterstützung tun (zumindest für diejenigen, die remove() auf ihrem Iterator implementieren).

Nachteile :

  1. Sie können es nicht als Einzelausdruck vor Ort ausführen (Hilfsmethode oder -variable erforderlich).

Wie für die

Ist es möglich, in einem Lambda-Ausdruck „Holen“ und „Entfernen“ zu kombinieren?

andere Antworten zeigen deutlich, dass dies möglich ist, aber Sie sollten sich dessen bewusst sein

  1. Suchen und Entfernen durchlaufen die Liste möglicherweise zweimal
  2. ConcurrentModificationException kann ausgelöst werden, wenn ein Element aus der Liste, die iteriert wird, entfernt wird
15

Die direkte Lösung wäre das Aufrufen von ifPresent(consumer) auf dem von findFirst() zurückgegebenen Optional. Dieser Consumer wird aufgerufen, wenn die Option nicht leer ist. Der Vorteil ist auch, dass es keine Ausnahme auslöst, wenn der Suchvorgang ein leeres optionales Ergebnis 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 die Optional zum Ergebnis des Aufrufs von remove hinzufügen:

producersProcedureActive.stream()
                        .filter(producer -> producer.getPod().equals(pod))
                        .findFirst()
                        .map(p -> {
                            producersProcedureActive.remove(p);
                            return p;
                        });

Beachten Sie jedoch, dass die Operation remove(Object) erneut die Liste durchläuft, um das zu entfernende Element zu finden. Wenn Sie über eine Liste mit wahlfreiem Zugriff verfügen, z. B. eine ArrayList, 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 Operation remove(int) direkt auf dem Index.

9
Tunaki

Verwenden Sie einen Filter für Java 8 und erstellen Sie eine weitere Liste, wenn Sie die alte Liste nicht ändern möchten:

List<ProducerDTO> result = producersProcedureActive
                            .stream()
                            .filter(producer -> producer.getPod().equals(pod))
                            .collect(Collectors.toList());
5
Toi Nguyen

Ich bin mir 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] enthält entweder das gefundene Element oder ist NULL.

Der "Trick" hier besteht darin, das "effektiv endgültige" Problem zu umgehen, indem eine Array - Referenz verwendet wird, die effektiv final ist, aber das erste Element setzt.

3
Bohemian

Mit Eclipse Collections können Sie detectIndex zusammen mit remove(int) in 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 Typ MutableList aus Eclipse Collections verwenden, können Sie die Methode detectIndex 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

2
Donald Raab

Wie von anderen vorgeschlagen, kann dies ein Anwendungsfall für Schleifen und iterierbare Elemente sein. Dies ist meiner Meinung nach der einfachste Ansatz. Wenn Sie die Liste direkt ändern möchten, kann sie sowieso nicht als "echte" Funktionsprogrammierung betrachtet werden. Sie können jedoch Collectors.partitioningBy() verwenden, um eine neue Liste mit Elementen zu erhalten, die Ihre Bedingung erfüllen, und eine neue Liste derjenigen, die dies nicht tun. Wenn Sie bei diesem Ansatz mehrere Elemente haben, die die Bedingung erfüllen, werden alle diese Elemente in dieser Liste aufgeführt und nicht nur das erste Element.

1
user140547

die Aufgabe lautet: get nd ✶ Element aus Liste entfernen

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
  } ) );
0
Kaplan

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"]
0
KimchiMan

Indem ich meine ursprüngliche Idee und Ihre Antworten kombinierte, erreichte ich die scheinbar Lösung. Zu meiner eigenen 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;
}

Das Objekt kann mit remove(int) entfernt werden, damit die -Liste nicht erneut durchlaufen wird (wie von @Tunaki vorgeschlagen) und und das entfernte Objekt an den Funktionsaufrufer _ zurückgegeben wird.

Ich habe Ihre Antworten gelesen, die mich dazu anhalten, sichere Methoden wie ifPresent anstelle von get zu wählen, 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; 
            });
}
0
Marco Stramezzi

Wenn wir mehrere Elemente aus einer Liste in eine neue Liste aufnehmen möchten (mithilfe eines Prädikats filtern) und sie aus der vorhandenen Liste entfernen möchten , konnte ich nicht finden überall eine richtige Antwort.

Hier ist, wie wir es mit der Java Streaming-API-Partitionierung machen 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 von diesem Artikel .

0