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ą guawy ImmutableList
.
Wiem, że mógłbym to zrobić
List<Integer> list = IntStream.range(0, 7).collect(Collectors.toList());
List<Integer> immutableList = ImmutableList.copyOf(list);
ale chciałbym odebrać do niego bezpośrednio. próbowałem
List<Integer> list = IntStream.range(0, 7)
.collect(Collectors.toCollection(ImmutableList::of));
ale rzucił wyjątek:
java.lang.UnsupportedOperationException pod adresem com.google.common.collect.ImmutableCollection.add (ImmutableCollection.java:96)
java-8
guava
java-stream
Zoltán
źródło
źródło
@Beta
od Guava 26.0.Tutaj
collectingAndThen
przydaje się kolektor:List<Integer> list = IntStream.range(0, 7).boxed() .collect(collectingAndThen(toList(), ImmutableList::copyOf));
Stosuje transformację do
List
właśnie zbudowanego; w wyniku czegoImmutableList
.Lub możesz bezpośrednio odebrać
Builder
i zadzwonićbuild()
na koniec: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 nieco rozwlekła i chcesz jej używać w wielu miejscach, możesz stworzyć 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(); } }
i wtedy:
List<Integer> list = IntStream.range(0, 7) .boxed() .collect(new ImmutableListCollector<>());
Na wypadek, gdyby link zniknął z komentarzy; moje drugie podejście można zdefiniować w statycznej metodzie narzędziowej, która po prostu używa
Collector.of
. To prostsze niż tworzenie własnejCollector
klasy.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 zastosowanie:
List<Integer> list = IntStream.range(0, 7) .boxed() .collect(toImmutableList());
źródło
ImmutableList.Builder
być pomocne?build()
.Collector
klasy :-)ImmutableList<Integer>
(zamiastList<Integer>
).Chociaż nie jest to bezpośrednia odpowiedź na moje pytanie (nie korzysta z kolekcjonerów), jest to dość eleganckie podejście, które nie wykorzystuje kolekcji pośrednich:
Stream<Integer> stream = IntStream.range(0, 7).boxed(); List<Integer> list = ImmutableList.copyOf(stream.iterator());
Źródło .
źródło
BTW: od JDK 10 można to zrobić w czystej Javie:
List<Integer> list = IntStream.range(0, 7) .collect(Collectors.toUnmodifiableList());
Również
toUnmodifiableSet
itoUnmodifiableMap
dostępne.Wewnątrz kolektora dokonano tego za pośrednictwem
List.of(list.toArray())
źródło
ImmutableCollections.List12
iImmutableCollections.ListN
! = GuawaImmutableList
. Z praktycznego punktu widzenia masz w większości rację, ale mimo to wspomnienie o tym niuansie w Twojej odpowiedzi miałoby sens.FYI, istnieje rozsądny sposób na zrobienie tego w Guava bez Java 8:
ImmutableSortedSet<Integer> set = ContiguousSet.create( Range.closedOpen(0, 7), DiscreteDomain.integers()); ImmutableList<Integer> list = set.asList();
Jeśli w rzeczywistości nie potrzebujesz
List
semantyki i możesz po prostu użyć aNavigableSet
, jest to jeszcze lepsze, ponieważ aContiguousSet
nie musi faktycznie przechowywać wszystkich elementów w nim (tylkoRange
iDiscreteDomain
).źródło