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


82

我要执行以下操作:

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

但它引发了一个例外:

com.google.common.collect.ImmutableCollection.add(ImmutableCollection.java:96)上的java.lang.UnsupportedOperationException

Answers:


89

toImmutableList()现在,Guava 21中包含了Alexis公认答案中的方法,该方法 可以用作:

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

编辑:@Beta版本27.16242bdd)中的ImmutableList.toImmutableList其他常用API中删除。


1
该方法标记为@Beta。因此,文档不建议您优先使用它吗?
user2602807

不过@Beta番石榴26.0。
Per Lundberg

出于角度考虑,Google在2004年至2009年之间将Gmail保持在Beta版之下,该产品在2004年发布时已经是相当稳定,成熟的产品。Google完全不愿意将其产品从Beta版提升为正式版。几乎到了喜剧的地步。
出生

67

这是collectingAndThen收集器有用的地方:

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

它将转换应用于List刚刚构建的对象。导致ImmutableList


或者,您可以直接收集到Builderbuild()在最后致电:

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

3
这仍然会创建一个中间列表,不是吗?我想避免这种情况。请问ImmutableList.Builder有什么帮助吗?
佐尔坦

4
@Zoltán您可以直接在构建器中累积值(请参见编辑),然后调用build()
Alexis C.

4
感谢您提供详细的答案。看来,这是目前正在处理:github.com/google/guava/issues/1582,这里也有一个很好的例子(很多喜欢你的建议):gist.github.com/JakeWharton/9734167
佐尔坦

4
@Zoltán啊,是的;好发现;它只是将第二种方法包装到实用程序方法中。比定义自己的Collector课程好一点:-)
Alexis C.

引用类型可以是ImmutableList<Integer>(而不是List<Integer>)。
palacsint

17

虽然不是我的问题的直接答案(它不使用收集器),但这是一种非常优雅的方法,它不使用中间收集器:

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

来源


6

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

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

toUnmodifiableSettoUnmodifiableMap可用的。

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


1
这不是完全正确,因为ImmutableCollections.List12and ImmutableCollections.ListN!= Guava's ImmutableList。从实际的角度来看,您基本上是正确的,但在您的答案中提及这一细微差别仍然有意义。
Per Lundberg

4

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

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

如果您实际上不需要List语义并且可以只使用a NavigableSet,那就更好了,因为aContiguousSet不必实际在其中存储所有元素(只需Rangeand DiscreteDomain)。

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.