Je jouais avec Java 8 lambdas pour filtrer facilement les collections. Mais je n'ai pas trouvé de moyen concis pour récupérer le résultat sous la forme d'une nouvelle liste dans la même déclaration. Voici mon approche la plus concise à ce jour:
List<Long> sourceLongList = Arrays.asList(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
List<Long> targetLongList = new ArrayList<>();
sourceLongList.stream().filter(l -> l > 100).forEach(targetLongList::add);
Les exemples sur le net n'ont pas répondu à ma question car ils arrêtaient sans générer une nouvelle liste de résultats. Il doit y avoir un moyen plus concis. J'aurais pensé que la classe Stream
avait des méthodes telles que toList()
, toSet()
,…
Existe-t-il un moyen de pouvoir affecter directement les variables targetLongList
par la troisième ligne?
Ce que vous faites peut être la manière la plus simple, à condition que votre flux reste séquentiel - sinon vous devrez passer un appel à sequential () avant forEach
.
[Éditer plus tard: la raison pour l’appel de sequential () est nécessaire, c’est que le code tel qu’il est (forEach(targetLongList::add)
) serait trop volumineux si le flux était parallèle. Même dans ce cas, il n'atteindra pas l'effet escompté, car forEach
est explicitement non déterministe - même dans un flux séquentiel, l'ordre de traitement des éléments n'est pas garanti. Vous devez utiliser forEachOrdered
pour assurer un bon ordre. L'intention des concepteurs de l'API Stream est que vous utilisiez le collecteur dans cette situation, comme ci-dessous.]
Une alternative est
targetLongList = sourceLongList.stream()
.filter(l -> l > 100)
.collect(Collectors.toList());
Mis à jour:
Une autre approche consiste à utiliser Collectors.toList
:
targetLongList =
sourceLongList.stream().
filter(l -> l > 100).
collect(Collectors.toList());
Solution précédente:
Une autre approche consiste à utiliser Collectors.toCollection
:
targetLongList =
sourceLongList.stream().
filter(l -> l > 100).
collect(Collectors.toCollection(ArrayList::new));
J'aime utiliser une méthode util qui renvoie un collecteur pour ArrayList
lorsque c'est ce que je veux.
Je pense que la solution utilisant Collectors.toCollection(ArrayList::new)
est un peu trop bruyante pour une opération aussi courante.
Exemple:
ArrayList<Long> result = sourceLongList.stream()
.filter(l -> l > 100)
.collect(toArrayList());
public static <T> Collector<T, ?, ArrayList<T>> toArrayList() {
return Collectors.toCollection(ArrayList::new);
}
Avec cette réponse, je souhaite également montrer à quel point il est simple de créer et d'utiliser des collecteurs personnalisés, ce qui est très utile en général.
Si vous avez un tableau de primitives, vous pouvez utiliser les collections de primitives disponibles dans Eclipse Collections .
LongList sourceLongList = LongLists.mutable.of(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
LongList targetLongList = sourceLongList.select(l -> l > 100);
Si vous ne pouvez pas modifier la sourceLongList à partir de List
:
List<Long> sourceLongList = Arrays.asList(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
List<Long> targetLongList =
ListAdapter.adapt(sourceLongList).select(l -> l > 100, new ArrayList<>());
Si vous voulez utiliser LongStream
:
long[] sourceLongs = new long[]{1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L};
LongList targetList =
LongStream.of(sourceLongs)
.filter(l -> l > 100)
.collect(LongArrayList::new, LongArrayList::add, LongArrayList::addAll);
Remarque: je contribue aux collections Eclipse.
Un moyen un peu plus efficace (évitez la création de la liste source et le désemballage automatique par le filtre):
List<Long> targetLongList = LongStream.of(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L)
.filter(l -> l > 100)
.boxed()
.collect(Collectors.toList());
collect(Collectors.toList());
C'est l'appel que vous pouvez utiliser pour convertir n'importe quel flux en liste.
Si vous voulez bien utiliser des bibliothèques tierces, les cyclops-react lib d'AOL (divulguer que je suis un contributeur) possède des extensions pour tous les types JDK Collection , y compris Liste . L'interface ListX étend Java.util.List et ajoute un grand nombre d'opérateurs utiles, y compris un filtre.
Vous pouvez simplement écrire
ListX<Long> sourceLongList = ListX.of(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
ListX<Long> targetLongList = sourceLongList.filter(l -> l > 100);
ListX peut également être créé à partir d'une liste existante (via ListX.fromIterable)
Il existe une autre variante de la méthode collect fournie par la classe LongStream et, de la même manière, par les classes IntStream et DoubleStream.
<R> R collect(Supplier<R> supplier,
ObjLongConsumer<R> accumulator,
BiConsumer<R,R> combiner)
Effectue une opération de réduction mutable sur les éléments de ce flux. Une réduction modifiable est une réduction dans laquelle la valeur réduite est un conteneur de résultat modifiable, tel qu'un ArrayList, et les éléments sont incorporés en mettant à jour l'état du résultat plutôt qu'en remplaçant le résultat. Cela produit un résultat équivalent à:
R result = supplier.get();
for (long element : this stream)
accumulator.accept(result, element);
return result;
Tout comme reduction (long, LongBinaryOperator), les opérations de collecte peuvent être parallélisées sans nécessiter de synchronisation supplémentaire. Il s'agit d'une opération de terminal.
Et répondre à votre question avec cette méthode de collecte est comme ci-dessous:
LongStream.of(1L, 2L, 3L, 3L).filter(i -> i > 2)
.collect(ArrayList::new, (list, value) -> list.add(value)
, (list1, list2) -> list1.addAll(list2));
Vous trouverez ci-dessous la variante de référence de la méthode qui est assez intelligente, mais qui est difficile à comprendre:
LongStream.of(1L, 2L, 3L, 3L).filter(i -> i > 2)
.collect(ArrayList::new, List::add , List::addAll);
Vous trouverez ci-dessous la variante HashSet:
LongStream.of(1L, 2L, 3L, 3).filter(i -> i > 2)
.collect(HashSet::new, HashSet::add, HashSet::addAll);
De même la variante LinkedList est comme ceci:
LongStream.of(1L, 2L, 3L, 3L)
.filter(i -> i > 2)
.collect(LinkedList::new, LinkedList::add, LinkedList::addAll);
Vous pouvez réécrire le code comme ci-dessous:
List<Long> sourceLongList = Arrays.asList(1L, 10L, 50L, 80L, 100L, 120L, 133L, 333L);
List<Long> targetLongList = sourceLongList.stream().filter(l -> l > 100).collect(Collectors.toList());
Si quelqu'un (comme moi) cherche des moyens de traiter les objets au lieu de types primitifs, utilisez mapToObj()
String ss = "An alternative way is to insert the following VM option before "
+ "the -vmargs option in the Eclipse shortcut properties(edit the "
+ "field Target inside the Shortcut tab):";
List<Character> ll = ss
.chars()
.mapToObj(c -> new Character((char) c))
.collect(Collectors.toList());
System.out.println("List type: " + ll.getClass());
System.out.println("Elem type: " + ll.get(0).getClass());
ll.stream().limit(50).forEach(System.out::print);
impressions:
List type: class Java.util.ArrayList
Elem type: class Java.lang.Character
An alternative way is to insert the following VM o
String joined =
Stream.of(isRead?"read":"", isFlagged?"flagged":"", isActionRequired?"action":"", isHide?"hide":"")
.filter(s -> s != null && !s.isEmpty())
.collect(Collectors.joining(","));
Voici le code par AbacusUtil
LongStream.of(1, 10, 50, 80, 100, 120, 133, 333).filter(e -> e > 100).toList();
Divulgation: Je suis le développeur de AbacusUtil.