MySQL Java JavaScript PHP Python HTML-CSS C-sharp

Grupare

Pentru a grupa datele în funcție de un anumit criteriu, trebuie să folosim împreună metoda collect() a obiectului Stream și metoda Collectors.groupingBy(). Să presupunem că avem următoarea clasă:

class Phone {
   private String name;
   private String company;
   private int price;

   public Phone(String name, String comp, int price) {
       this.name = name;
       this.company = comp;
       this.price = price;
   }

   public String getName() { return name; }
   public int getPrice() { return price; }
   public String getCompany() { return company; }
}

Să presupunem că avem un set de obiecte Phone pe care dorim să le grupăm după companie:

import java.util.List;
import java.util.Map;
import java.util.stream.Stream;
import java.util.stream.Collectors;

public class Program {
   public static void main(String[] args) {
       Stream<Phone> phoneStream = Stream.of(
           new Phone("iPhone X", "Apple", 600),
           new Phone("Pixel 2", "Google", 500),
           new Phone("iPhone 8", "Apple", 450),
           new Phone("Galaxy S9", "Samsung", 440),
           new Phone("Galaxy S8", "Samsung", 340)
       );

       Map<String, List<Phone>> phonesByCompany = phoneStream.collect(
               Collectors.groupingBy(Phone::getCompany));

       for (Map.Entry<String, List<Phone>> item : phonesByCompany.entrySet()) {
           System.out.println(item.getKey());
           for (Phone phone : item.getValue()) {
               System.out.println(phone.getName());
           }
           System.out.println();
       }
   }
}

Output în consolă:

Google
Pixel 2

Apple
iPhone X
iPhone 8

Samsung
Galaxy S9
Galaxy S8

Așadar, pentru a crea grupuri, în metoda phoneStream.collect() transmitem apelul funcției Collectors.groupingBy(), care grupează obiectele după companie folosind expresia Phone::getCompany. Rezultatul este un obiect Map în care cheile sunt denumirile companiilor, iar valorile sunt liste de telefoane asociate fiecărei companii.

Metoda Collectors.partitioningBy

Metoda Collectors.partitioningBy() funcționează similar, dar împarte elementele în grupuri în funcție de o condiție. De exemplu:

Map<Boolean, List<Phone>> phonesByCompany = phoneStream.collect(
               Collectors.partitioningBy(p -> p.getCompany() == "Apple"));

for (Map.Entry<Boolean, List<Phone>> item : phonesByCompany.entrySet()) {
   System.out.println(item.getKey());
   for (Phone phone : item.getValue()) {
       System.out.println(phone.getName());
   }
   System.out.println();
}

În acest caz, condiția p -> p.getCompany() == "Apple" verifică dacă telefonul aparține companiei Apple. Dacă aparține, telefonul este plasat într-un grup, altfel este plasat în altul.

Metoda Collectors.counting

Metoda Collectors.counting este folosită împreună cu Collectors.groupingBy() pentru a calcula numărul de elemente din fiecare grup:

Map<String, Long> phonesByCompany = phoneStream.collect(
       Collectors.groupingBy(Phone::getCompany, Collectors.counting()));

for (Map.Entry<String, Long> item : phonesByCompany.entrySet()) {
   System.out.println(item.getKey() + " - " + item.getValue());
}

Output în consolă:

Google - 1
Apple - 2
Samsung - 2

Metoda Collectors.summing

Metoda Collectors.summing este utilizată pentru a calcula suma elementelor. În funcție de tipul de date, are următoarele forme: summingInt(), summingLong(), summingDouble(). Aplicăm această metodă pentru a calcula prețul total al smartphone-urilor pe companii:

Map<String, Integer> phonesByCompany = phoneStream.collect(
       Collectors.groupingBy(Phone::getCompany, Collectors.summingInt(Phone::getPrice)));

for (Map.Entry<String, Integer> item : phonesByCompany.entrySet()) {
   System.out.println(item.getKey() + " - " + item.getValue());
}

Output în consolă:

Google - 500
Apple - 1050
Samsung - 780

Prin expresia Collectors.summingInt(Phone::getPrice) indicăm că pentru fiecare companie va fi calculată suma totală a prețurilor tuturor smartphone-urilor sale. Și deoarece rezultatul calculat este o sumă pentru valori de tip int, tipul colecției returnate va fi Map<String, Integer>.

Metodele maxBy și minBy

Metodele maxBy și minBy sunt folosite pentru a calcula valoarea maximă sau minimă din fiecare grup. Aceste metode acceptă ca parametru o funcție comparator. De exemplu, găsim telefonul cu prețul minim pentru fiecare companie:

import java.util.Comparator;
import java.util.Optional;
import java.util.Map;

Map<String, Optional<Phone>> phonesByCompany = phoneStream.collect(
       Collectors.groupingBy(Phone::getCompany,
               Collectors.minBy(Comparator.comparing(Phone::getPrice))));

for (Map.Entry<String, Optional<Phone>> item : phonesByCompany.entrySet()) {
   System.out.println(item.getKey() + " - " + item.getValue().get().getName());
}

Output în consolă:

Google - Pixel 2
Apple - iPhone 8
Samsung - Galaxy S8

Ca valoare returnată a operațiunii de grupare este utilizat un obiect de tip Map<String, Optional<Phone>>. Deoarece grupăm după companii, cheia va fi un șir de caractere, iar valoarea va fi un obiect Optional<Phone>.

Metoda summarizing

Metodele summarizingInt(), summarizingLong(), summarizingDouble() permit colectarea datelor într-un set de valori de tipul corespunzător:

import java.util.IntSummaryStatistics;
import java.util.Map;

Map<String, IntSummaryStatistics> priceSummary = phoneStream.collect(
   Collectors.groupingBy(Phone::getCompany,
       Collectors.summarizingInt(Phone::getPrice)));

for (Map.Entry<String, IntSummaryStatistics> item : priceSummary.entrySet()) {
   System.out.println(item.getKey() + " - " + item.getValue().getAverage());
}

Metoda Collectors.summarizingInt(Phone::getPrice()) creează un set de prețuri pentru fiecare companie. Rezultatul este încorporat într-un obiect IntSummaryStatistics, care are diverse metode pentru a efectua operații atomice asupra setului de date:

  • getAverage(): returnează valoarea medie
  • getCount(): returnează numărul de elemente din set
  • getMax(): returnează valoarea maximă
  • getMin(): returnează valoarea minimă
  • getSum(): returnează suma elementelor

Output în consolă:

Google - 500.0
Apple - 525.0
Samsung - 390.0

Metoda mapping

Metoda mapping permite procesarea suplimentară a datelor și transformarea obiectelor din flux într-un alt tip de date. De exemplu:

Map<String, List<String>> phonesByCompany = phoneStream.collect(
   Collectors.groupingBy(Phone::getCompany,
       Collectors.mapping(Phone::getName, Collectors.toList())));

for (Map.Entry<String, List<String>> item : phonesByCompany.entrySet()) {
   System.out.println(item.getKey());
   for (String name : item.getValue()) {
       System.out.println(name);
   }
}

Expresia Collectors.mapping(Phone::getName, Collectors.toList()) specifică faptul că în grup vor fi extrase doar numele smartphone-urilor, iar fiecare grup va fi reprezentat ca un obiect List.

← Lecția anterioară Lecția următoare →