Advanced Java Services | Collector |
Collector ist ein Interface mit zwei statischen Factorymethoden mit denen man Instanzen vom Typ Collector erstellen kann. Eine Instanzen dieses Typs übergibt man dann der Methode <R,A> R collect(Collector<? super T,A,R> collector) aus Stream<T>.
Die Bedeutung der generischen Typen kann man am besten über die Signatur der zwei statischen Factorymethoden erläutern.
Die Methode hat die folgende Form
static <T,A,R> Collector<T,A,R> of(Supplier<A> supplier, BiConsumer<A,T> accumulator, BinaryOperator<A> combiner, Function<A,R> finisher, Collector.Characteristics... characteristics)
Die Bedeutung der Parameter
Supplier<A> liefert einen Behälter vom Typ A, der Elemente vom Typ T aufnehmen kann BiConsumer<A,T> bringt Elemente vom Typ T in den Behälter vom Typ A (daher der Name accumulator) BinaryOperator<A> bringt alle Elemente aus einem temporären Behälter (2-ter Parameter) vom Typ A in den vom Supplier bereitgestellten Behälter vom Typ A (1-ter Parameter) (daher der Name combiner) Der combiner wird nur im parallelen Fall verwendet, was man durch eine Konsolmeldung zeigen kann. Function<A,R> erhält den vom Supplier bereitgestellten Behälter und bringt die Elemente in einen Behälter vom Typ R und gibt diesen Behälter zurück.
//Supplier<A> supplier A = List<String> Supplier<List<String>> supplier = ArrayList::new; // BiConsumer<A,T> accumulator T = String BiConsumer<List<String>,String> accumulator = List::add ; // (list, s) -> list.add(s); //BinaryOperator<A> combiner BinaryOperator<List<String>> combiner = (left, right) -> { left.addAll(right); return left; }; // Im nichtparallelen Fall kann der combiner einfach null zurückgeben, er darf aber selbst nicht null sein. // Function<A,R> finisher R = List<String> Function<List<String>,List<String>> finisher = Collections::unmodifiableList; // Collector<T,A,R> Collector<String,List<String>,List<String>> myCollector = Collector.of(supplier, accumulator, combiner, finisher); Stream<String> stringStream = Stream.of("Doris", "Diana", "Berta", "Bruno", "Bernd", "Beate", "Anna", "Anke"); List<String> immutableList = stringStream.collect(myCollector); System.out.println(immutableList); //immutableList.add("ccc"); // java.lang.UnsupportedOperationException
Ausgabe
[Doris, Diana, Berta, Bruno, Bernd, Beate, Anna, Anke]
Die Methode hat die folgende Form
static <T,R,R> Collector<T,R,R> of(Supplier<R> supplier, BiConsumer<R,T> accumulator, BinaryOperator<R> combiner, Collector.Characteristics... characteristics)
Die Bedeutung der Parameter
Supplier<R> liefert einen Behälter vom Typ R, der Elemente vom Typ T aufnehmen kann BiConsumer<R,T> bringt Elemente vom Typ T in den Behälter vom Typ R (daher der Name accumulator) BinaryOperator<R> bringt alle Elemente aus einem temporären Behälter (2-ter Parameter) vom Typ R in den vom Supplier bereitgestellten Behälter vom Typ R (1-ter Parameter) (daher der Name combiner) Der combiner wird nur im parallelen Fall verwendet, was man durch eine Konsolmeldung zeigen kann.
Diese Methode entspricht im Wesentlichen der zweiten collect()-Methode aus Stream<T>, allerdings mit einem wichtigen Unterschied: Der dritte Parameter ist hier ein BiConsumer<R,R> und kein BinaryOperator<R>
<R> R collect(Supplier<R> supplier, BiConsumer<R,? super T> accumulator, BiConsumer<R,R> combiner)
Stream<String> stringStream2 = Stream.of("Doris", "Diana", "Berta", "Bruno", "Bernd", "Beate", "Anna", "Anke"); BiConsumer<Set<String>,String> setAccumulator = (set, st) -> { if(st.startsWith("B")) set.add(st); } ; BinaryOperator<Set<String>> setCombiner = (left, right) -> { System.out.println("comb*");left.addAll(right); return left; }; //Function<Set<String>,Set<String>> setFinisher = Collections::unmodifiableSet; Collector<String,Set<String>,Set<String>> setCollector = Collector.of( TreeSet::new, setAccumulator, setCombiner); //setFinisher); Set<String> treeSet = stringStream2.collect(setCollector); System.out.println(treeSet);
Ausgabe
[Beate, Bernd, Berta, Bruno]
Wenn wir die zweite collect()-Methode aus Stream<T> verwenden wollen müssen wir aus dem BinaryOperator<R> einen BiConsumer<R,R> machen. Hierzu wird einfach das return-Statement weggelassen.
// wie vorher BiConsumer<Set<String>,String> setAccumulator = (set, st) -> { if(st.startsWith("B")) set.add(st); } ; // hier jetzt BiConsumer<Set<String>,Set<String>> und kein BinaryOperator<Set<String>> BiConsumer<Set<String>,Set<String>> consCombiner = (left, right) -> { System.out.println("comb+");left.addAll(right); }; Stream<String> stringStream = Stream.of("Doris", "Diana", "Berta", "Bruno", "Bernd", "Beate", "Anna", "Anke"); Set<String> treeSet3 = stringStream.parallel().collect(TreeSet::new, setAccumulator, consCombiner); System.out.println(treeSet3);