Dans les flux Java8, suis-je autorisé à modifier / mettre à jour des objets à l'intérieur? Pour par exemple. List<User> users
:
users.stream().forEach(u -> u.setProperty("value"))
Dans les flux Java8, suis-je autorisé à modifier / mettre à jour des objets à l'intérieur? Pour par exemple. List<User> users
:
users.stream().forEach(u -> u.setProperty("value"))
Réponses:
Oui, vous pouvez modifier l'état des objets à l'intérieur de votre flux, mais le plus souvent, vous devez éviter de modifier l'état de la source du flux. De la section non-interférence de la documentation du package de flux, nous pouvons lire que:
Pour la plupart des sources de données, éviter les interférences signifie s'assurer que la source de données n'est pas du tout modifiée pendant l'exécution du pipeline de flux. L'exception notable à cela concerne les flux dont les sources sont des collections simultanées, spécifiquement conçues pour gérer les modifications simultanées. Les sources de flux simultanées sont celles dont les
Spliterator
rapports laCONCURRENT
caractéristique.
Alors ça va
List<User> users = getUsers();
users.stream().forEach(u -> u.setProperty(value));
// ^ ^^^^^^^^^^^^^
mais ce n'est pas dans la plupart des cas
users.stream().forEach(u -> users.remove(u));
//^^^^^ ^^^^^^^^^^^^
et peut lancer ConcurrentModificationException
ou même d'autres exceptions inattendues comme NPE:
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
ce qui empêche de modifier la liste (supprimer / ajouter de nouveaux éléments) pendant l'itération et que les flux et les boucles for-each l'utilisent. Vous pouvez essayer d'utiliser une boucle simple comme celle for(int i=0; ..; ..)
qui n'a pas ce problème (mais ne vous arrêtera pas quand un autre thread modifiera votre liste). Vous pouvez également utiliser des méthodes telles que list.removeAll(Collection)
, list.removeIf(Predicate)
. Les java.util.Collections
classes ont également quelques méthodes qui pourraient être utiles comme addAll(CollectionOfNewElements,list)
.
filter()
La manière fonctionnelle serait à mon humble avis:
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]
}
}
Dans cette solution, la liste d'origine n'est pas modifiée, mais doit contenir votre résultat attendu dans une nouvelle liste accessible sous la même variable que l'ancienne
Pour faire une modification structurelle sur la source du flux, comme Pshemo l'a mentionné dans sa réponse, une solution est de créer une nouvelle instance d'un Collection
like ArrayList
avec les éléments à l'intérieur de votre liste principale; parcourez la nouvelle liste et effectuez les opérations sur la liste principale.
new ArrayList<>(users).stream().forEach(u -> users.remove(u));
Pour se débarrasser de ConcurrentModificationException, utilisez CopyOnWriteArrayList
List<Something>
- vous n'avez aucune idée s'il s'agit d'un CopyOnWriteArrayList. Donc, cela ressemble plus à un commentaire médiocre, mais pas à une vraie réponse.
Au lieu de créer des choses étranges, vous pouvez juste filter()
et après map()
votre résultat.
C'est beaucoup plus lisible et sûr. Les flux le feront en une seule boucle.
Oui, vous pouvez modifier ou mettre à jour les valeurs des objets de la liste dans votre cas de la même manière:
users.stream().forEach(u -> u.setProperty("some_value"))
Cependant, l'instruction ci-dessus apportera des mises à jour sur les objets source. Ce qui peut ne pas être acceptable dans la plupart des cas.
Heureusement, nous avons un autre moyen comme:
List<Users> updatedUsers = users.stream().map(u -> u.setProperty("some_value")).collect(Collectors.toList());
Ce qui renvoie une liste mise à jour, sans gêner l'ancienne.
Comme mentionné précédemment, vous ne pouvez pas modifier la liste d'origine, mais vous pouvez diffuser, modifier et collecter des éléments dans une nouvelle liste. Voici un exemple simple de modification d'un élément de chaîne.
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]
}
}
Vous pouvez utiliser removeIf
pour supprimer des données d'une liste de manière conditionnelle.
Par exemple: - Si vous souhaitez supprimer tous les nombres pairs d'une liste, vous pouvez le faire comme suit.
final List<Integer> list = IntStream.range(1,100).boxed().collect(Collectors.toList());
list.removeIf(number -> number % 2 == 0);
Cela pourrait être un peu tard. Mais voici l'un des usages. Ceci pour obtenir le décompte du nombre de fichiers.
Créez un pointeur vers la mémoire (un nouvel obj dans ce cas) et faites modifier la propriété de l'objet. Le flux Java 8 ne permet pas de modifier le pointeur lui-même et, par conséquent, si vous déclarez simplement compter comme une variable et essayez d'incrémenter dans le flux, cela ne fonctionnera jamais et lèvera une exception du compilateur en premier lieu
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 + "]";
}
}