Подтвердить что ты не робот

Как я могу собрать поток Java 8 в Guava ImmutableCollection?

Я хотел бы сделать следующее:

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

но таким образом, что результирующий список представляет собой реализацию Guava ImmutableList.

Я знаю, что мог бы сделать

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

но я хотел бы получить его непосредственно. Я пробовал

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

но он исключил исключение:

java.lang.UnsupportedOperationException     на com.google.common.collect.ImmutableCollection.add(ImmutableCollection.java:96)

4b9b3361

Ответ 1

Здесь полезен сборщик collectingAndThen:

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

Он применяет преобразование к List, который вы только что создали; в результате получилось ImmutableList.


Или вы можете напрямую собрать в Builder и вызвать build() в конце:

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

Если эта опция для вас многословна и вы хотите использовать ее во многих местах, вы можете создать свой собственный коллекционер:

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

а затем:

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

На всякий случай ссылка исчезает в комментариях; мой второй подход можно определить в статическом утилите, который просто использует Collector.of. Это проще, чем создание собственного класса 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);
}

и использование:

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

Ответ 2

Метод toImmutableList() в принятом ответе Алексиса теперь включен в Guava 21 и может использоваться как:

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

Ответ 3

Не будучи прямым ответом на мой вопрос (он не использует сборщиков), это довольно элегантный подход, который не использует промежуточные коллекции:

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

Источник.

Ответ 4

FYI, там разумный способ сделать это в Guava без Java 8:

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

Если вам действительно не нужна семантика List и может просто использовать NavigableSet, это еще лучше, так как ContiguousSet не должен фактически хранить все элементы в нем (просто Range и DiscreteDomain).