2015-03-12 6 views
56

Ich möchte folgendes tun:Wie kann ich einen Java 8-Stream in eine Guava ImmutableCollection sammeln?

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

aber in einer Weise, dass die resultierende Liste ImmutableList eine Implementierung von Guava ist ist.

Ich weiß, ich

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

tun könnte, aber ich würde direkt sammeln möchten. Ich habe versucht,

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

aber es hat eine Ausnahme:

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

Antwort

50

Hier wird der collectingAndThen Sammler nützlich ist:

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

Sie wendet die Umwandlung auf die gerade erstellte List an; was zu einem ImmutableList führt.


Oder Sie könnten direkt sammeln in den Builder und rufen build() am Ende:

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

Wenn diese Option für Sie ein bisschen-ausführliche und Sie wollen, es zu benutzen in vielen Orten, Sie

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

und dann: können eigene Sammler erstellen

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

Nur für den Fall, dass der Link in den Kommentaren verschwindet; Mein zweiter Ansatz könnte in einer statischen Hilfsmethode definiert werden, die einfach Collector.of verwendet. Es ist einfacher als das Erstellen einer eigenen Collector Klasse.

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

und die Nutzung:

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

schafft Dies noch eine Zwischen Liste, nicht wahr? Ich möchte das vermeiden. Könnte der 'ImmutableList.Builder' hilfreich sein? –

+3

@ Zoltán Sie könnten die Werte im Builder direkt akkumulieren (siehe Bearbeiten) und dann 'build()' aufrufen. –

+2

Vielen Dank für diese detaillierte Antwort. Es scheint, dass dies derzeit angesprochen wird: https://github.com/google/guava/issues/1582, hier gibt es auch ein schönes Beispiel (ähnlich wie du es vorgeschlagen hast): https://gist.github.com/ JakeWharton/9734167 –

13

Während keine direkte Antwort auf meine Frage (es verwendet Sammler nicht), ist dies eine ziemlich elegante Methode, die nicht Zwischen Sammlungen nicht verwendet:

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

Source.

3

FYI, ist es eine vernünftige Möglichkeit, dies ohne Java 8 in Guava zu tun:

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

Wenn Sie nicht wirklich brauchen, um die List Semantik und können nur NavigableSet verwenden, das ist noch besser, da ein ContiguousSet doesn muss nicht wirklich alle Elemente darin speichern (nur die Range und DiscreteDomain).

51

Die toImmutableList() Methode in der akzeptierte Antwort von Alexis ist jetzt in Guava 21 enthalten und kann eingesetzt werden als:

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

Ich mag diesen Ansatz) – ycomp

+0

... außer dass ich Guava 22 oder 21 nicht verwenden kann, weil ich bei 19 stecke seit ich eine andere Bibliothek in meinem habe Poms mit einer älteren Version von Guave ... weiß jemand, wie ich es aufspüren kann? – ycomp

Verwandte Themen