Grupowanie kolekcji w streamach

range-java

Streamy to zdecydowanie jedno z moich ulubionych rozwiązań w Javie. Parafrazując wieszcza – streamy się docenia, gdy się je straci. Gdy zacząłem programować w Apexie, od samego początku odczułem brak tego co strumienie oferują.

Jedną z często używanych przeze ze mnie funkcji jest grupowanie, czyli metoda groupingBy Collectors w pakiecie java.util.stream. Służy ona do tworzenia map z list czy zbiorów. Wracając do wspomnianego Apexa, chcąc z listy Tasków stworzyć mapę, z kluczem jako Kontakt przypisany do Taska musielibyśmy wykonać mniej więcej takie operacje:

Map<Contact, List<Task>> tasksByContact = new Map<Contact, List<Task>>();     
for(Task iTask : tasks) {
    if(tasksByContact.get(iTask.getContact()) == null) {
        tasksByContact.put(iTask.getContact(), new List<Task>{iTask});
    } else {
        tasksByContact.get(iTask.getContact()).add(iTask);
    }
}

Przyznacie, że trochę dużo tu się dzieje? Na szczęście w Javie mamy możliwość wykonać wszystkie te operacje w streamie. I tak uprościmy sobie kod do takiej postaci:

Map<User, List<Task>> tasksByContact = tasks.stream()
    .collect(Collectors.groupingBy(Task::getContact));

Czyż nie prościej? Przy grupowanie można również używać mappingu i np. zamiast listy tasków uzyskać zbiór taskId:

Map<Contact, Set<Long>> taskIdsByContact = tasks.stream()
    .collect(Collectors.groupingBy(Task::getContact, mapping(Task::getId, toSet()));

Czy innych funkcji jak suma, czy wartość maksymalna:

Map<Contact, Long> tasksNumberByContact = tasks.stream()
    .collect(Collectors.groupingBy(Task::getContact, Collectors.counting()));
Map<Contact, Integer> tasksPlannedTimeByUser = tasks.stream()
    .collect(Collectors.groupingBy(Task::getContact, summingInt(Task::getPlannedTime)));

Ale to jeszcze nie wszystko. Możecie też oczywiście użyć grupowania dwa razy i stworzyć mapę z wartościami w formie kolejnej mapy:

Map<Contact, Map<Company, Long>> tasksNumberByCompanyByContact = tasks.stream()
    .collect(
        Collectors.groupingBy(Task::getContact, 
        Collectors.groupingBy(Task::getCompany, Collectors.counting())
    );

I to oczywiście tylko część możliwości, które daje nam Collectors w kwestii grupowania.

Grupowanie kolekcji w streamach

Dodaj komentarz

Twój adres email nie zostanie opublikowany. Pola, których wypełnienie jest wymagane, są oznaczone symbolem *

Przewiń do góry