web-dev-qa-db-fra.com

Comment puis-je collecter un flux Java 8 dans une Guava ImmutableCollection?

Je voudrais faire ce qui suit:

List<Integer> list = IntStream.range(0, 7).collect(Collectors.toList());

mais d'une manière que la liste résultante est une implémentation de ImmutableList de Guava.

Je sais que je pourrais faire

List<Integer> list = IntStream.range(0, 7).collect(Collectors.toList());
List<Integer> immutableList = ImmutableList.copyOf(list);

mais je voudrais y recueillir directement. J'ai essayé

List<Integer> list = IntStream.range(0, 7)
    .collect(Collectors.toCollection(ImmutableList::of));

mais il a jeté une exception:

Java.lang.UnsupportedOperationException sur com.google.common.collect.ImmutableCollection.add (ImmutableCollection.Java:96)

72
Zoltán

La méthode toImmutableList() dans la réponse acceptée d'Alexis est maintenant incluse dans Guava 21 et peut être utilisée comme:

ImmutableList<Integer> list = IntStream.range(0, 7)
    .boxed()
    .collect(ImmutableList.toImmutableList());
73
Ritesh

C'est là que le collecteur collectingAndThen est utile:

List<Integer> list = IntStream.range(0, 7).boxed()
                .collect(collectingAndThen(toList(), ImmutableList::copyOf));

Il applique la transformation au List que vous venez de construire; résultant en un ImmutableList.


Ou vous pouvez collecter directement dans le Builder et appeler build() à la fin:

List<Integer> list = IntStream.range(0, 7)
                .collect(Builder<Integer>::new, Builder<Integer>::add, (builder1, builder2) -> builder1.addAll(builder2.build()))
                .build();

Si cette option est un peu bavarde pour vous et que vous souhaitez l'utiliser à de nombreux endroits, vous pouvez créer votre propre collecteur:

class ImmutableListCollector<T> implements Collector<T, Builder<T>, ImmutableList<T>> {
    @Override
    public Supplier<Builder<T>> supplier() {
        return Builder::new;
    }

    @Override
    public BiConsumer<Builder<T>, T> accumulator() {
        return (b, e) -> b.add(e);
    }

    @Override
    public BinaryOperator<Builder<T>> combiner() {
        return (b1, b2) -> b1.addAll(b2.build());
    }

    @Override
    public Function<Builder<T>, ImmutableList<T>> finisher() {
        return Builder::build;
    }

    @Override
    public Set<Characteristics> characteristics() {
        return ImmutableSet.of();
    }
}

et alors:

List<Integer> list = IntStream.range(0, 7)
                              .boxed()
                              .collect(new ImmutableListCollector<>());

Juste au cas où le lien disparaît dans les commentaires; ma deuxième approche pourrait être définie dans une méthode utilitaire statique qui utilise simplement Collector.of. C'est plus simple que de créer votre propre classe Collector.

public static <T> Collector<T, Builder<T>, ImmutableList<T>> toImmutableList() {
    return Collector.of(Builder<T>::new, Builder<T>::add, (l, r) -> l.addAll(r.build()), Builder<T>::build);
}

et l'utilisation:

 List<Integer> list = IntStream.range(0, 7)
                               .boxed()
                               .collect(toImmutableList());
63
Alexis C.

Bien que ce ne soit pas une réponse directe à ma question (il n'utilise pas de collectionneurs), c'est une approche assez élégante qui n'utilise pas de collections intermédiaires:

Stream<Integer> stream = IntStream.range(0, 7).boxed();
List<Integer> list = ImmutableList.copyOf(stream.iterator());

Source .

16
Zoltán

BTW: depuis JDK 10 cela peut se faire en Java pur:

List<Integer> list = IntStream.range(0, 7)
    .collect(Collectors.toUnmodifiableList());

toUnmodifiableSet et toUnmodifiableMap sont également disponibles.

Dans le collecteur, cela a été fait via List.of(list.toArray())

5
GKislin

Pour info, il existe un moyen raisonnable de le faire en goyave sans Java 8:

ImmutableSortedSet<Integer> set = ContiguousSet.create(
    Range.closedOpen(0, 7), DiscreteDomain.integers());
ImmutableList<Integer> list = set.asList();

Si vous n'avez pas réellement besoin de la sémantique List et que vous pouvez simplement utiliser un NavigableSet, c'est encore mieux puisqu'un ContiguousSet n'a pas besoin de stocker réellement tous les éléments qu'il contient (juste le Range et DiscreteDomain).

3
ColinD