14 Ağustos 2018 Salı

Stream collect metodu

collect metodu - Collector
İmzası şöyle.
<R, A> R collect(Collector<? super T, A, R> collector);
Örnek
Hazır collector kullanmak istersek şöyle yaparız.
yourList.stream().collect(Collectors.joining(","))
Örnek
Elimizde bir Collector olsun.
class PackCollector<T> implements Collector<T, List<List<T>>, List<List<T>>> {

  @Override
  public Supplier<List<List<T>>> supplier() {
    return () -> {
      ...
    };
  }

  @Override
  public BiConsumer<List<List<T>>, T> accumulator() {
    return (list, s) -> {
      ...
    };
  }

  @Override
  public BinaryOperator<List<List<T>>> combiner() {
    return (left, right) -> {
      ...
    };
  }

  @Override
  public Set<Characteristics> characteristics() {
    return EnumSet.of(Characteristics.IDENTITY_FINISH);
  }

  @Override
  public Function<List<List<T>>, List<List<T>>> finisher() {
    return Function.identity();
  }

}
Şöyle yaparız.
List<List<String>> result = items.stream().parallel().collect(new PackCollector<>());
System.out.println(result);
collect metodu - Supplier + Accumulator + Combiner
İmzası şöyle.
<R> R collect(Supplier<R> supplier,
              BiConsumer<R, ? super T> accumulator,
              BiConsumer<R, R> combiner);
Accumulator
Açıklaması şöyle.
accumulator - an associative, non-interfering, stateless function that must fold an element into a result container.
Combiner Ne İşe Yarar
Açıklaması şöyle.
combiner - an associative, non-interfering, stateless function that accepts two partial result containers and merges them, which must be compatible with the accumulator function. The combiner function must fold the elements from the second result container into the first result container.
Combiner yerine kendimiz yazmak istersek, lambda kullanabiliriz ya da sınıfımızın bir metodunu çağırabiliriz. Bu metod paralelleştirme varsa işe yarar. Açıklaması şöyle. Yani iki thread'in çıktısını birleştirmek için kullanılır.
The first argument is a supplier that supplies an empty array list for adding collected stuff into. The second argument is a biconsumer that consumes each element of the array. The third argument is there only to provide parallelism support. This enables it to collect the elements into multiple array lists at the same time, and it asks you for a way to connect all these array lists together at the end.
Combiner'ın devreye girdiği ve girmediğini görmek için şöyle yaparız.
String resultParallel = list.parallelStream().collect(
            StringBuilder::new,
            (builder, elem) -> builder.append(" ").append(elem),
            (left, right) -> left.append(" ").append(right)).toString();

String result = list.stream().collect(
            StringBuilder::new,
            (builder, elem) -> builder.append(" ").append(elem),
            (left, right) -> left.append(" ").append(right)).toString();


System.out.println("ResultParallel: ->" + resultParallel + "<-"); // -> 1  2  3  4<-
System.out.println("Result: ->" + result + "<-"); // -> 1 2 3 4<-
Combiner ve Accumulator aynı şekilde yani aynı sonucu dönmeli. 
Açıklaması şöyle.
What this is saying is that it should not matter whether the elements are collected by "accumulating" or "combining" or some combination of the two. But in your code, the accumulator and the combiner concatenate using a different separator. They are not "compatible" in the sense required by the javadoc.

Örnek
Şöyle yaparız.
ArrayList<Integer> collected = Stream.of(1,2,3)
    .collect(
        ArrayList::new, 
        ArrayList::add, 
        ArrayList::addAll);
System.out.println(collected);
Örnek
Şöyle yaparız.
Path path = ...;
List<String> lines = Files.lines(path)
  .collect(
    ArrayList::new, 
    new BiConsumer<ArrayList<String>, String>() {
      @Override
      public void accept(ArrayList<String> lines, String line) {
      ...                  
      }
    }, 
    ArrayList::addAll
);
Örnek
 Şöyle bir collector sınıfımız olsun.
// Averager.java from the official Java tutorial
public class Averager
{
  private int total = 0;
  private int count = 0;


  public void addcount(int i) { total += i; count++;}
  public void combine(Averager other) {
    total += other.total;
    count += other.count;
  }
}
Kendi metodlarımızı çağırmak istersek şöyle yaparız. Bir Averager nesnesi yaratılır. Her eleman için addCount metodu çağrılır. addAccount bir accumulator metodudur. Stream elemanını bir result container nesnesine ekler. combine bir combiner metodudur. İki tane result container nesnesini birleştirir.
List<Integer> list = new LinkedList<Integer>();
...
Averager averageCollect = list.stream()
      .collect(Averager::new, Averager::addcount, Averager::combine);
Eğer lambda kullanmak istersek şöyle yaparız.
Averager averageCollect = list.stream()
  .collect(Averager::new, (a,b) -> a.addcount(b), (a,b) -> a.combine(b));

Hiç yorum yok:

Yorum Gönder