it-swarm.com.de

Kann Java 8 Streams mit einem Element in einer Sammlung arbeiten und es dann entfernen?

Wie fast jeder lerne ich immer noch die Feinheiten der neuen Java 8 Streams-API. Ich habe eine Frage zur Verwendung von Streams. Ich werde ein vereinfachtes Beispiel geben.

Java Streams ermöglicht es uns, eine Collection zu nehmen und die stream()-Methode zu verwenden, um einen Stream aller seiner Elemente zu erhalten. Es gibt eine Reihe nützlicher Methoden wie filter(), map() und forEach(), die es uns ermöglichen, Lambda-Operationen auf den Inhalt anzuwenden.

Ich habe Code, der ungefähr so ​​aussieht (vereinfacht):

set.stream().filter(item -> item.qualify())
    .map(item -> (Qualifier)item).forEach(item -> item.operate());
set.removeIf(item -> item.qualify());

Die Idee ist, eine Zuordnung aller Elemente im Satz zu erhalten, die einem bestimmten Qualifikationsmerkmal entsprechen, und diese dann durchzuarbeiten. Nach der Operation haben sie keinen weiteren Zweck und sollten aus dem ursprünglichen Satz entfernt werden. Der Code funktioniert gut, aber ich kann das Gefühl nicht abschütteln, dass es in Stream eine Operation gibt, die dies in einer einzigen Zeile für mich tun könnte.

Wenn es in den Javadocs ist, kann ich es übersehen.

Hat jemand, der mit der API mehr vertraut ist, so etwas zu sehen?

60

Sie können es so machen:

set.removeIf(item -> {
    if (!item.qualify())
        return false;
    item.operate();
    return true;
});

Wenn item.operate() immer true zurückgibt, können Sie dies sehr kurz tun.

set.removeIf(item -> item.qualify() && item.operate());

Ich mag diese Ansätze jedoch nicht, da nicht sofort klar ist, was los ist. Persönlich würde ich weiterhin eine for-Schleife und eine Iterator verwenden.

for (Iterator<Item> i = set.iterator(); i.hasNext();) {
    Item item = i.next();
    if (item.qualify()) {
        item.operate();
        i.remove();
    }
}
112
Paul Boddington

In einer Zeile nein, aber vielleicht könnten Sie den partitioningBy-Collector verwenden:

Map<Boolean, Set<Item>> map = 
    set.stream()
       .collect(partitioningBy(Item::qualify, toSet()));

map.get(true).forEach(i -> ((Qualifier)i).operate());
set = map.get(false);

Es ist möglicherweise effizienter, da es das zweimalige Durchlaufen des Satzes vermeidet, einmal zum Filtern des Streams und dann zum Entfernen entsprechender Elemente.

Ansonsten denke ich, dass Ihr Ansatz relativ gut ist.

4
Alexis C.

Es gibt viele Ansätze. Bei Verwendung von myList.remove (Element) muss equals () überschrieben werden. Zweitens, was ich bevorzuge, ist:

allList.removeIf(item -> item.getId().equals(elementToDelete.getId()));

Viel Glück und glückliche Codierung :) 

3

Was Sie wirklich tun möchten, ist die Partitionierung Ihres Sets. Leider ist die Partitionierung in Java 8 nur über die "Collect" -Methode des Terminals möglich. Sie enden mit etwas wie diesem:

// test data set
Set<Integer> set = ImmutableSet.of(1, 2, 3, 4, 5);
// predicate separating even and odd numbers
Predicate<Integer> evenNumber = n -> n % 2 == 0;

// initial set partitioned by the predicate
Map<Boolean, List<Integer>> partitioned = set.stream().collect(Collectors.partitioningBy(evenNumber));

// print even numbers
partitioned.get(true).forEach(System.out::println);
// do something else with the rest of the set (odd numbers)
doSomethingElse(partitioned.get(false))

Aktualisierte:

Scala-Version des obigen Codes

val set = Set(1, 2, 3, 4, 5)
val partitioned = set.partition(_ % 2 == 0)
partitioned._1.foreach(println)
doSomethingElse(partitioned._2)`
2

wenn ich deine frage richtig verstehe:

set = set.stream().filter(item -> {
    if (item.qualify()) {
        ((Qualifier) item).operate();
        return false;
    }
    return true;
}).collect(Collectors.toSet());
1
user_3380739

Nein, Ihre Implementierung ist wahrscheinlich die einfachste. Sie können etwas tief Schlechtes tun, indem Sie den Status im Prädikat removeIf ändern, aber bitte nicht. Andererseits kann es sinnvoll sein, tatsächlich zu einer auf Iteratoren basierenden Implementierung zu wechseln, die für diesen Anwendungsfall tatsächlich angemessener und effizienter ist.

1
Louis Wasserman

Nach der Operation haben sie keinen weiteren Zweck und sollten aus dem ursprünglichen Satz entfernt werden. Der Code funktioniert gut, aber ich kann das Gefühl nicht schütteln, dass es eine Operation in Stream gibt, die dies für mich in einer einzigen Zeile tun könnte.

Mit dem Stream können Sie keine Elemente aus der Quelle des Streams entfernen. Aus dem Javadoc :

Die meisten Stream-Vorgänge akzeptieren Parameter, die das vom Benutzer angegebene Verhalten beschreiben. Um das korrekte Verhalten zu erhalten, können Sie die folgenden Verhaltensparameter verwenden:

  • muss nicht störend sein (sie ändern nicht die Stream-Quelle); und
  • in den meisten Fällen muss es zustandslos sein (ihr Ergebnis sollte nicht von einem Status abhängen, der sich während der Ausführung der Stream-Pipeline ändern kann).
1
user7502825

Ich sehe die Besorgnis des Paulus bei der Verwendung von Streams, wie in der Antwort angegeben. Wenn Sie eine erklärende Variable hinzufügen, werden möglicherweise die Absichten etwas klarer.

set.removeIf(item -> {
  boolean removeItem=item.qualify();
  if (removeItem){
    item.operate();
  }
  return removeItem;
});
0
vacant78