Jak mogę zebrać strumień Java 8 do Guava ImmutableCollection?

Chciałbym wykonać następujące czynności:

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

Ale w taki sposób, że wynikowa lista jest implementacją ImmutableList.

I know I could do

List<Integer> list = IntStream.range(0, 7).collect(Collectors.toList());
List<Integer> immutableList = ImmutableList.copyOf(list);
/ Align = "left" / Próbowałem
List<Integer> list = IntStream.range(0, 7)
    .collect(Collectors.toCollection(ImmutableList::of));

Ale wyrzuciło wyjątek:

Java.lang.UnsupportedOperationException na kom.google.pospolite.zbieraj.ImmutableCollection.add (ImmutableCollection.java: 96)

Author: Jeffrey Bosboom, 2015-03-12

5 answers

Tutaj przydaje się kolektor collectingAndThen:

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

Stosuje transformację do List, które właśnie zbudowałeś; w rezultacie otrzymujesz ImmutableList.


Lub możesz odebrać bezpośrednio w Builder i zadzwonić build() na końcu:

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

Jeśli ta opcja jest dla ciebie trochę słowna i chcesz jej używać w wielu miejscach, możesz utworzyć własny kolektor:

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();
    }
}

A potem:

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

Na wszelki wypadek link zniknie w komentarzach; mój drugi podejście może być zdefiniowane w statycznej metodzie użytkowej, która po prostu używa Collector.of. To prostsze niż tworzenie własnej klasy 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);
}

I użycie:

 List<Integer> list = IntStream.range(0, 7)
                               .boxed()
                               .collect(toImmutableList());
 58
Author: Alexis C.,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-12 16:21:56

Metoda toImmutableList() w zaakceptowanej odpowiedzi Alexisa jest teraz włączona do Guava 21 i może być używana jako:

ImmutableList<Integer> list = IntStream.range(0, 7)
    .boxed()
    .collect(ImmutableList.toImmutableList());
 60
Author: Ritesh,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-06-15 00:18:03

Chociaż nie jest to bezpośrednia odpowiedź na moje pytanie( nie używa kolekcjonerów), jest to dość eleganckie podejście, które nie wykorzystuje zbiorów pośrednich:

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

Źródło .

 15
Author: Zoltán,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2016-07-15 12:38:16

Dla twojej wiadomości, jest rozsądny sposób, aby to zrobić w Guava bez Java 8:

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

Jeśli nie potrzebujesz semantyki List i możesz po prostu użyć NavigableSet, to nawet lepiej, ponieważ ContiguousSet nie musi przechowywać wszystkich elementów w nim (tylko Range i DiscreteDomain).

 3
Author: ColinD,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2015-03-12 18:15:17

BTW: od JDK 10 można to zrobić w czystej Javie:

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

Również toUnmodifiableSet i toUnmodifiableMap dostępne.

Wewnątrz kolektora wykonano przez List.of(list.toArray())

 0
Author: GKislin,
Warning: date(): Invalid date.timezone value 'Europe/Kyiv', we selected the timezone 'UTC' for now. in /var/www/agent_stack/data/www/doraprojects.net/template/agent.layouts/content.php on line 54
2018-09-24 02:11:25