Darf ich in Java8-Streams Objekte in ändern / aktualisieren? Zum Beispiel. List<User> users
::
users.stream().forEach(u -> u.setProperty("value"))
Darf ich in Java8-Streams Objekte in ändern / aktualisieren? Zum Beispiel. List<User> users
::
users.stream().forEach(u -> u.setProperty("value"))
Antworten:
Ja, Sie können den Status von Objekten in Ihrem Stream ändern. In den meisten Fällen sollten Sie jedoch den Status der Quelle des Streams nicht ändern . Aus dem Abschnitt über die störungsfreie Dokumentation des Stream-Pakets können wir Folgendes lesen:
Für die meisten Datenquellen bedeutet das Verhindern von Interferenzen, dass sichergestellt wird, dass die Datenquelle während der Ausführung der Stream-Pipeline überhaupt nicht geändert wird . Die bemerkenswerte Ausnahme bilden Streams, deren Quellen gleichzeitige Sammlungen sind, die speziell für die gleichzeitige Änderung entwickelt wurden. Gleichzeitige Stream-Quellen sind solche, deren
Spliterator
Berichte dasCONCURRENT
Merkmal sind.
Das ist also in Ordnung
List<User> users = getUsers();
users.stream().forEach(u -> u.setProperty(value));
// ^ ^^^^^^^^^^^^^
Dies ist jedoch in den meisten Fällen nicht der Fall
users.stream().forEach(u -> users.remove(u));
//^^^^^ ^^^^^^^^^^^^
und kann ConcurrentModificationException
oder sogar andere unerwartete Ausnahmen wie NPE werfen :
List<Integer> list = IntStream.range(0, 10).boxed().collect(Collectors.toList());
list.stream()
.filter(i -> i > 5)
.forEach(i -> list.remove(i)); //throws NullPointerException
Iterator
, dass das Ändern der Liste (Entfernen / Hinzufügen neuer Elemente) während der Iteration verhindert wird und sowohl Streams als auch for-each-Schleifen diese verwenden. Sie könnten versuchen, eine einfache Schleife zu for(int i=0; ..; ..)
verwenden, die dieses Problem nicht hat (aber Sie nicht aufhält, wenn ein anderer Thread Ihre Liste ändert). Sie könnten auch Methoden wie list.removeAll(Collection)
, list.removeIf(Predicate)
. Auch java.util.Collections
Klasse haben wenige Methoden, die nützlich sein könnten addAll(CollectionOfNewElements,list)
.
filter()
Der funktionale Weg wäre imho:
import static java.util.stream.Collectors.toList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Predicate;
public class PredicateTestRun {
public static void main(String[] args) {
List<String> lines = Arrays.asList("a", "b", "c");
System.out.println(lines); // [a, b, c]
Predicate<? super String> predicate = value -> "b".equals(value);
lines = lines.stream().filter(predicate.negate()).collect(toList());
System.out.println(lines); // [a, c]
}
}
In dieser Lösung wird die ursprüngliche Liste nicht geändert, sondern sollte Ihr erwartetes Ergebnis in einer neuen Liste enthalten, auf die unter derselben Variablen wie die alte zugegriffen werden kann
Um strukturelle Änderungen an der Quelle des Streams vorzunehmen, wie Pshemo in seiner Antwort erwähnt hat, besteht eine Lösung darin, eine neue Instanz eines Collection
Like ArrayList
mit den Elementen in Ihrer primären Liste zu erstellen . Durchlaufen Sie die neue Liste und führen Sie die Operationen für die primäre Liste aus.
new ArrayList<>(users).stream().forEach(u -> users.remove(u));
Verwenden Sie CopyOnWriteArrayList , um ConcurrentModificationException zu entfernen
List<Something>
haben , haben Sie keine Ahnung, ob dies eine CopyOnWriteArrayList ist. Das ist also eher ein mittelmäßiger Kommentar, aber keine echte Antwort.
Anstatt seltsame Dinge zu erschaffen, können Sie einfach filter()
und dann map()
Ihr Ergebnis erzielen.
Dies ist viel lesbarer und sicherer. Streams schaffen es in nur einer Schleife.
Wie bereits erwähnt, können Sie die ursprüngliche Liste nicht ändern, aber Sie können Elemente streamen, ändern und in einer neuen Liste sammeln. Hier ist ein einfaches Beispiel zum Ändern eines Zeichenfolgenelements.
public class StreamTest {
@Test
public void replaceInsideStream() {
List<String> list = Arrays.asList("test1", "test2_attr", "test3");
List<String> output = list.stream().map(value -> value.replace("_attr", "")).collect(Collectors.toList());
System.out.println("Output: " + output); // Output: [test1, test2, test3]
}
}
Sie können das verwenden removeIf
, um Daten bedingt aus einer Liste zu entfernen.
Beispiel: - Wenn Sie alle geraden Zahlen aus einer Liste entfernen möchten, können Sie dies wie folgt tun.
final List<Integer> list = IntStream.range(1,100).boxed().collect(Collectors.toList());
list.removeIf(number -> number % 2 == 0);
Ja, Sie können die Werte der Objekte in der Liste in Ihrem Fall ebenfalls ändern oder aktualisieren:
users.stream().forEach(u -> u.setProperty("some_value"))
Mit der obigen Anweisung werden jedoch Aktualisierungen an den Quellobjekten vorgenommen. Was in den meisten Fällen möglicherweise nicht akzeptabel ist.
Zum Glück haben wir einen anderen Weg wie:
List<Users> updatedUsers = users.stream().map(u -> u.setProperty("some_value")).collect(Collectors.toList());
Dadurch wird eine aktualisierte Liste zurückgegeben, ohne die alte zu beeinträchtigen.
Dies könnte etwas spät sein. Aber hier ist eine der Verwendung. Dies, um die Anzahl der Dateien zu ermitteln.
Erstellen Sie einen Zeiger auf den Speicher (in diesem Fall ein neues Objekt) und ändern Sie die Eigenschaft des Objekts. Der Java 8-Stream erlaubt es nicht, den Zeiger selbst zu ändern. Wenn Sie also nur als Variable zählen und versuchen, innerhalb des Streams zu inkrementieren, funktioniert dies niemals und löst überhaupt keine Compiler-Ausnahme aus
Path path = Paths.get("/Users/XXXX/static/test.txt");
Count c = new Count();
c.setCount(0);
Files.lines(path).forEach(item -> {
c.setCount(c.getCount()+1);
System.out.println(item);});
System.out.println("line count,"+c);
public static class Count{
private int count;
public int getCount() {
return count;
}
public void setCount(int count) {
this.count = count;
}
@Override
public String toString() {
return "Count [count=" + count + "]";
}
}