Làm cách nào để thu thập một luồng Java 8 vào một Guava ImmutableCollection?


82

Tôi muốn làm như sau:

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

nhưng theo cách mà danh sách kết quả là sự triển khai của Guava ImmutableList.

Tôi biết tôi có thể làm

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

nhưng tôi muốn thu thập trực tiếp. Tôi đã thử

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

nhưng nó đã ném ra một ngoại lệ:

java.lang.UnsupportedOperationException tại com.google.common.collect.ImmutableCollection.add (ImmutableCollection.java:96)

Câu trả lời:


89

Các toImmutableList()phương pháp trong các câu trả lời được chấp nhận của Alexis hiện được đưa vào Ổi 21 và có thể được sử dụng như:

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

Chỉnh sửa: Đã bị xóa @Betakhỏi ImmutableList.toImmutableListcùng với các API thường dùng khác trong Bản phát hành 27.1 ( 6242bdd ).


1
Phương thức được đánh dấu là @Beta. Vì vậy, nó không được giới thiệu bởi tài liệu?
user2602807

Vẫn @Betanhư Guava 26.0.
Per Lundberg

Vì lợi ích của quan điểm, Google đã giữ Gmail dưới dạng thẻ beta từ năm 2004 đến năm 2009, vốn đã là một sản phẩm khá ổn định và trưởng thành khi ra mắt vào năm 2004. Nhìn chung, Google khá miễn cưỡng trong việc quảng bá các sản phẩm từ trạng thái Beta. Gần đến mức hài hước.
anataliocs

67

Đây là nơi mà collectingAndThenbộ sưu tập hữu ích:

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

Nó áp dụng chuyển đổi cho Listbạn vừa xây dựng; dẫn đến một ImmutableList.


Hoặc bạn có thể thu thập trực tiếp vào Buildervà gọi build()ở cuối:

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

Nếu tùy chọn này hơi dài dòng đối với bạn và bạn muốn sử dụng nó ở nhiều nơi, bạn có thể tạo bộ sưu tập của riêng mình:

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

và sau đó:

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

Chỉ trong trường hợp liên kết biến mất trong các nhận xét; cách tiếp cận thứ hai của tôi có thể được xác định trong một phương thức tiện ích tĩnh sử dụng đơn giản Collector.of. Nó đơn giản hơn việc tạo Collectorlớp học của riêng bạn .

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

và cách sử dụng:

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

3
Điều này vẫn tạo ra một danh sách trung gian, phải không? Tôi muốn tránh điều đó. Có thể ImmutableList.Builderđược bất kỳ sự giúp đỡ?
Zoltán

4
@ Zoltán Bạn có thể tích lũy các giá trị trong trình tạo trực tiếp (xem chỉnh sửa) và sau đó gọi build().
Alexis C.

4
Cảm ơn bạn vì câu trả lời chi tiết này. Có vẻ như điều này hiện đang được giải quyết: github.com/google/guava/issues/1582 , cũng có một ví dụ hay ở đây (rất giống những gì bạn đã đề xuất): gist.github.com/JakeWharton/9734167
Zoltán

4
@ Zoltán À vâng; phát hiện tốt; nó chỉ đơn giản là kết thúc phương thức thay thế thứ hai thành các phương thức tiện ích. Tốt hơn một chút so với việc xác định Collectorlớp học của riêng bạn :-)
Alexis C.

Các loại tham chiếu có thể là ImmutableList<Integer>(thay vì List<Integer>).
palacsint

17

Mặc dù không phải là câu trả lời trực tiếp cho câu hỏi của tôi (nó không sử dụng bộ sưu tập), đây là một cách tiếp cận khá thanh lịch không sử dụng bộ sưu tập trung gian:

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

Nguồn .


6

BTW: kể từ JDK 10, nó có thể được thực hiện bằng Java thuần túy:

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

Ngoài ra toUnmodifiableSettoUnmodifiableMapcó sẵn.

Bên trong bộ sưu tập nó đã được thực hiện thông qua List.of(list.toArray())


1
Điều này không hoàn toàn đúng, vì ImmutableCollections.List12ImmutableCollections.ListN! = Guava'sImmutableList . Từ góc độ thực tế, bạn hầu như đúng, nhưng sẽ vẫn hợp lý nếu đề cập đến sắc thái này trong câu trả lời của bạn.
Per Lundberg

4

FYI, có một cách hợp lý để thực hiện việc này trong Guava mà không có Java 8:

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

Nếu bạn không thực sự cần List ngữ nghĩa và chỉ có thể sử dụng a NavigableSet, thì điều đó thậm chí còn tốt hơn vì a ContiguousSetkhông phải thực sự lưu trữ tất cả các phần tử trong đó (chỉ RangeDiscreteDomain).

Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.