Raggruppa contando nell'API stream Java 8


170

Cerco di trovare un modo semplice nell'API dello stream Java 8 per fare il raggruppamento, ne esco con questo modo complesso!

List<String> list = new ArrayList<>();

list.add("Hello");
list.add("Hello");
list.add("World");

Map<String, List<String>> collect = list.stream().collect(
        Collectors.groupingBy(o -> o));
System.out.println(collect);

List<String[]> collect2 = collect
        .entrySet()
        .stream()
        .map(e -> new String[] { e.getKey(),
                String.valueOf(e.getValue().size()) })
        .collect(Collectors.toList());

collect2.forEach(o -> System.out.println(o[0] + " >> " + o[1]));

Apprezzo il tuo contributo.


1
Cosa stai cercando di realizzare qui?
Keppil,

2
È un caso molto comune, ad esempio ho riscontrato un errore in un periodo di tempo e voglio vedere alcune statistiche sul numero di occorrenze per ogni giorno in questo periodo di tempo.
Muhammad Hewedy,

Risposte:


340

Penso che stai solo cercando il sovraccarico che richiede un altro Collectorper specificare cosa fare con ciascun gruppo ... e quindi Collectors.counting()fare il conteggio:

import java.util.*;
import java.util.stream.*;

class Test {
    public static void main(String[] args) {
        List<String> list = new ArrayList<>();

        list.add("Hello");
        list.add("Hello");
        list.add("World");

        Map<String, Long> counted = list.stream()
            .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));

        System.out.println(counted);
    }
}

Risultato:

{Hello=2, World=1}

(C'è anche la possibilità di utilizzare groupingByConcurrentper una maggiore efficienza. Qualcosa da tenere a mente per il tuo codice reale, se sarebbe sicuro nel tuo contesto.)


1
Perfetto! ... da javadocand then performing a reduction operation on the values associated with a given key using the specified downstream Collector
Muhammad Hewedy il

6
L'uso di Function.identity () (con importazione statica) invece di e -> e rende un po 'più piacevole la lettura: Map <String, Long> counted = list.stream (). Collect (groupingBy (identity (), counting () ));
Kuchi,

Ciao, mi chiedevo se qualcuno potesse spiegare l'aspetto della Mappa del codice Map<String, Long> counted = list.stream() .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));, cosa sta succedendo esattamente a questo punto e qualsiasi collegamento con ulteriori spiegazioni associate all'argomento che potrebbe essere inviato
Blank

@Blank: che si sente come sarebbe meglio come una nuova domanda, con voi spiegando che parti di esso si fanno capire prima. Passare attraverso ogni suo aspetto (senza sapere quale parte non capisci) richiederebbe molto tempo - più tempo di quello che sono disposto a dare in una risposta che ha più di 5 anni a questo punto, quando la maggior parte di te potrebbe già capire.
Jon Skeet,

@JonSkeet Cool, lo inserirò in una nuova domanda, anche se ho evidenziato l'aspetto che non ho capito nella mia domanda. Detto questo, l'intero frammento di codice che ho aggiunto insieme ad esso.
Vuoto

9
List<String> list = new ArrayList<>();

list.add("Hello");
list.add("Hello");
list.add("World");

Map<String, List<String>> collect = list.stream()
                                        .collect(Collectors.groupingBy(o -> o));
collect.entrySet()
       .forEach(e -> System.out.println(e.getKey() + " - " + e.getValue().size()));

8

Ecco un esempio per l'elenco di oggetti

Map<String, Long> requirementCountMap = requirements.stream().collect(Collectors.groupingBy(Requirement::getRequirementType, Collectors.counting()));

8

Qui ci sono opzioni leggermente diverse per eseguire l'attività a portata di mano.

utilizzando toMap:

list.stream()
    .collect(Collectors.toMap(Function.identity(), e -> 1, Math::addExact));

utilizzando Map::merge:

Map<String, Integer> accumulator = new HashMap<>();
list.forEach(s -> accumulator.merge(s, 1, Math::addExact));

4

Ecco la semplice soluzione di StreamEx

StreamEx.of(list).groupingBy(Function.identity(), Collectors.countingInt());

Ridurre il codice del boilerplate: collect(Collectors.


1
Qual è il motivo per usarlo su flussi Java8?
Torsten Ojaperv,

1

Se sei aperto all'utilizzo di una libreria di terze parti, puoi utilizzare la Collectors2classe in Eclipse Collections per convertirla Listin Bagutilizzando a Stream. A Bagè una struttura di dati creata per il conteggio .

Bag<String> counted =
        list.stream().collect(Collectors2.countBy(each -> each));

Assert.assertEquals(1, counted.occurrencesOf("World"));
Assert.assertEquals(2, counted.occurrencesOf("Hello"));

System.out.println(counted.toStringOfItemToCount());

Produzione:

{World=1, Hello=2}

In questo caso particolare, si può semplicemente collectla Listdirettamente in una Bag.

Bag<String> counted = 
        list.stream().collect(Collectors2.toBag());

Puoi anche creare il Bagsenza usare a Streamadattando il Listcon i protocolli Eclipse Collections.

Bag<String> counted = Lists.adapt(list).countBy(each -> each);

o in questo caso particolare:

Bag<String> counted = Lists.adapt(list).toBag();

Puoi anche creare direttamente la borsa.

Bag<String> counted = Bags.mutable.with("Hello", "Hello", "World");

A Bag<String>è come un Map<String, Integer>in che tiene traccia internamente delle chiavi e dei loro conteggi. Ma, se chiedi Mapa una chiave che non contiene, tornerà null. Se chiedi Baga una chiave che non contiene utilizzando occurrencesOf, restituirà 0.

Nota: sono un committer per le raccolte Eclipse.

Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.