如何将Java 8流收集到Guava ImmutableCollection中?

时间:2015-03-12 15:01:52

标签: java-8 guava java-stream

我想做以下事情:

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)

5 个答案:

答案 0 :(得分:73)

Alexis接受的答案中的toImmutableList()方法现已包含在Guava 21中,可用作:

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

答案 1 :(得分:63)

这是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 :(得分:15)

虽然不是我的问题的直接答案(它不使用收藏家),但这是一种相当优雅的方法,它不使用中间集合:

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

Source

答案 3 :(得分:3)

仅供参考,在没有Java 8的情况下,在Guava中有一种合理的方法:

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

如果您实际上不需要List语义并且只能使用NavigableSet,那就更好了,因为ContiguousSet不必实际存储其中的所有元素(仅限RangeDiscreteDomain)。

答案 4 :(得分:3)

顺便说一句:从JDK 10开始,它可以用纯Java完成:

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

还有toUnmodifiableSettoUnmodifiableMap可用。

内部收集器是通过List.of(list.toArray())

完成的