Gruppieren Sie durch Zählen in der Java 8-Stream-API

170

Ich versuche, in der Java 8-Stream-API einen einfachen Weg zu finden, um die Gruppierung durchzuführen. Ich komme mit diesem komplexen Weg heraus!

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]));

Ich freue mich über Ihren Beitrag.

Muhammad Hewedy
quelle
1
Was versuchst du hier zu erreichen?
Keppil
2
Es ist ein sehr häufiger Fall, zum Beispiel, dass in einem bestimmten Zeitraum ein Fehler aufgetreten ist, und ich möchte einige Statistiken über die Anzahl der Vorkommen pro Tag in diesem Zeitraum anzeigen.
Muhammad Hewedy

Antworten:

339

Ich denke, Sie suchen nur nach der Überlastung, die eine andere benötigt, Collectorum anzugeben, was mit jeder Gruppe Collectors.counting()zu tun ist ... und dann um zu zählen:

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);
    }
}

Ergebnis:

{Hello=2, World=1}

(Es besteht auch die Möglichkeit, die groupingByConcurrentEffizienz zu steigern. Beachten Sie dies für Ihren echten Code, wenn er in Ihrem Kontext sicher wäre.)

Jon Skeet
quelle
1
Perfekt! ... aus Javadocand then performing a reduction operation on the values associated with a given key using the specified downstream Collector
Muhammad Hewedy
6
Die Verwendung von Function.identity () (mit statischem Import) anstelle von e -> e macht das Lesen etwas angenehmer: Map <String, Long> gezählt = list.stream (). Collect (groupingBy (identity (), count () ));
Kuchi
Hallo, ich habe mich gefragt, ob jemand den Map-Aspekt des Codes erklären könnte Map<String, Long> counted = list.stream() .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));, was genau an diesem Punkt passiert und welche Links mit weiteren Erklärungen zum Thema gesendet werden könnten
Blank
@Blank: Das fühlt sich an wie es am besten als eine neue Frage wäre, mit Ihnen zu erklären , welche Teile davon Sie haben zuerst zu verstehen. Es würde sehr lange dauern, jeden Aspekt davon durchzugehen (ohne zu wissen, welches Bit Sie nicht verstehen) - mehr Zeit, als ich bereit bin, eine Antwort zu geben, die zu diesem Zeitpunkt über 5 Jahre alt ist, wenn Sie das meiste davon sind kann schon verstehen.
Jon Skeet
@ JonSkeet Cool, ich werde es in eine neue Frage einfügen, obwohl ich den Aspekt hervorgehoben habe, den ich in meiner Frage nicht verstanden habe. Das heißt, das gesamte Code-Snippet, das ich zusammen mit ihm hinzugefügt habe.
Leer
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()));
Sivakumar
quelle
8

Hier ist ein Beispiel für eine Liste von Objekten

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

Hier sind etwas andere Optionen, um die vorliegende Aufgabe zu erfüllen.

mit toMap:

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

mit Map::merge:

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

Hier ist die einfache Lösung von StreamEx

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

Reduzieren Sie den Boilerplate-Code: collect(Collectors.

user_3380739
quelle
1
Was ist der Grund, es über Java8-Streams zu verwenden?
Torsten Ojaperv
1

Wenn Sie eine Drittanbieter - Bibliothek mit Open sind, können Sie die Verwendung der Collectors2Klasse in Eclipse - Kollektionen auf die konvertieren Listzu einem Bageiner Verwendung Stream. A Bagist eine Datenstruktur, die zum Zählen erstellt wurde .

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());

Ausgabe:

{World=1, Hello=2}

In diesem speziellen Fall können Sie einfach collectdie Listdirekt in eine Bag.

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

Sie können das auch Bagohne Verwendung eines erstellen, Streamindem Sie das Listmit den Eclipse Collections-Protokollen anpassen .

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

oder in diesem speziellen Fall:

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

Sie können die Tasche auch direkt erstellen.

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

A Bag<String>ist insofern wie a Map<String, Integer>, als es intern die Schlüssel und ihre Anzahl verfolgt. Wenn Sie jedoch Mapnach einem Schlüssel fragen , der nicht enthalten ist, wird er zurückgegeben null. Wenn Sie a Bagnach einem Schlüssel fragen , der nicht using enthält occurrencesOf, wird 0 zurückgegeben.

Hinweis: Ich bin ein Committer für Eclipse-Sammlungen.

Donald Raab
quelle