6 Ocak 2020 Pazartesi

Collectors.toMap metodu - keyMapper + valueMapper + Merge Function - Duplicate Value Varsa Kullanılır

Giriş
toMap metodunun iki parametreli hali burada
toMap metodunun üç parametreli hali burada.
toMap metodunun dört parametreli hali burada.

toMap metodu - keyMapper + valueMapper + Merge Function
keyMapper key değerini döner.
valueMapper value değerini döner.
mergeFunction aynı key değerine sahip bir nesne bulununca çağrılır

Burada en önemli şey merge metodu. Bu metod en büyük olanı seçme, birleştirme gibi şeyleri yapmak için kullanılır.

Örnek - En Büyük Olanı Seçmek
Elimizde şöyle bir  kod olsun. Aynı value değerlerini tek indirmek ve indirirken en büyük key değerine sahip olanı kullanmak isteyelim.
// Input
Map<Integer, String> map = new HashMap<>();
map.put(1, "apple");
map.put(5, "apple");
map.put(4, "orange");
map.put(3, "apple");
map.put(2, "orange");

// Output: {5=apple, 4=orange} // the key is the largest possible
Şöyle yaparız. inverse ile Value + Key şeklinde bir map elde edilir. Bu map en büyük key değerini sahip nesneleri içerir. Daha sonra inver yine tersine çevrilir ve Key + Value haline getirilir.
Map<Integer, String> deduplicateValues(Map<Integer, String> map) {
  Map<String, Integer> inverse = map.entrySet().stream().collect(toMap(
    Map.Entry::getValue,
    Map.Entry::getKey,
    Math::max) // take the highest key on duplicate values
  );

  return inverse.entrySet().stream()
    .collect(toMap(Map.Entry::getValue, Map.Entry::getKey));
}
Örnek - En Büyük Olanı Çoklu Alana Göre Seçme
Şöyle yaparız. Burada BinaryOperator.maxBy() kullanımı önemli. Eğer getID() aynıysa önce getDate() en büyük olan seçilir. getDate() aynıysa  getWeightedNumber() en büyük olan seçilir.
return new ArrayList<MyEntity>(
    Stream.concat(listOne.stream(), listTwo.stream())
          .collect(Collectors.toMap(MyEntity::getId, Function.identity(),
                   BinaryOperator.maxBy(Comparator.comparing(MyEntity::getDate)
                                          .thenComparing(MyEntity::getWeightedNumber))))
          .values());

Örnek - Birleştirme
Elimizde şöyle bir liste olsun.
List<A>{
 name = "abc"
 List<B>{1,2}

 name= "xyz"
 List<B>{3,4}

 name = "abc"
 List<B>{3,5}
}
İsmi aynı olan numaraları birleştirmek isteylim. Yani şöyle bir sonuç istiyoruz.
List<A>{
 name = "abc"
 List<B>{1,2,3,5}

 name="xyz"
 List<B>{3,4}
}
Bunun için toMap metodunun 3 parametreli keyMapper, valueMapper, mergeFunction halini kullanırız. keyMapper name değerini döner. valueMapper nesnenin kendisini döner. mergeFunction aynı name değerine sahip bir nesne bulununca çağrılır. a , b  valueMapper'in döndürdüğü nesne tipindendir. a nesnesinin numaralarına b nesnesinin numaraları eklenir. Şöyle yaparız.
Collection<A> result = list.stream()
         .collect(Collectors.toMap(a -> a.name, a -> a, 
                      (a, b) -> {a.numbers.addAll(b.numbers); return a;}))
         .values();
Örnek - Birleştirme
Elimizde şöyle iki sınıf olsun.
class Person {
    String name;
    int age;
    List<Address> address;
}

class Address {
    String street;
    String city;
    String country;
}
Şöyle yaparız.
List<Person> merged = findPerson.stream()
  .collect( 
    Collectors.collectingAndThen(
      Collectors.toMap( 
        (p) -> new AbstractMap.SimpleEntry<>( p.getName(), p.getAge() ), 
              Function.identity(), 
        (left, right) -> { 
          left.getAddress().addAll( right.getAddress() ); 
          return left; 
        }
      ),        
      ps -> new ArrayList<>( ps.values() ) //Map'i Listeye çevir
    ) 
);
Örnek - Birleştirme
Elimizde şöyle bir kod olsun
public A(A another) {
    this.id = another.id;
    this.name = another.name;
    this.list = new ArrayList<>(another.list);
}

public A merge(A another) {
    list.addAll(another.list):
    return this;
}
Çıktı olarak şunu isteyelim
[
    A(1, a_one, [B(1, b_one), B(2, b_two), B(3, b_three)]),
    A(2, a_two, [B(2, b_two), B(4, b_four), B(5, b_five)]),
    A(3, a_three, [B(4, b_four), B(5, b_five)])
]
Şöyle yaparız.
Map<Integer, A> result = listOfA.stream()
    .collect(Collectors.toMap(A::getId, A::new, A::merge));

Collection<A> result = map.values();
Örnek - Birleştirme
Şöyle yaparız.
Map<String, List<String>> map3 = Stream.of(map1, map2)
                .flatMap(map -> map.entrySet().stream())
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        e -> new ArrayList<>(e.getValue()),
                        (left, right) -> {left.addAll(right); return left;}
                ));

Hiç yorum yok:

Yorum Gönder