Regrouper en comptant dans l'API de flux Java 8


170

J'essaie de trouver un moyen simple dans l'API de flux Java 8 pour faire le regroupement, je viens avec cette méthode complexe!

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

J'apprécie votre contribution.


1
Qu'essayez-vous d'accomplir ici?
Keppil

2
C'est un cas très courant, par exemple, une erreur se produit dans une période donnée et je veux voir des statistiques sur le nombre d'occurrences par jour au cours de cette période.
Muhammad Hewedy

Réponses:


341

Je pense que vous cherchez juste la surcharge qui en prend une autre Collectorpour spécifier quoi faire avec chaque groupe ... et ensuite Collectors.counting()faire le comptage:

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

Résultat:

{Hello=2, World=1}

(Il y a aussi la possibilité d'utiliser groupingByConcurrentpour plus d'efficacité. Quelque chose à garder à l'esprit pour votre vrai code, si ce serait sûr dans votre contexte.)


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

6
L'utilisation de Function.identity () (avec import statique) au lieu de e -> e rend la lecture un peu plus agréable: Map <String, Long> counted = list.stream (). Collect (groupingBy (identity (), counting () ));
Kuchi

Salut, je me demandais si quelqu'un pourrait expliquer l'aspect Carte du code Map<String, Long> counted = list.stream() .collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));, ce qui se passe exactement à ce stade et tout lien avec des explications supplémentaires associées au sujet qui pourrait être envoyé
Vierge le

@Blank qui donne l'impression que ce serait mieux une nouvelle question, vous expliquant quelles sont les parties de celui - ci vous ne comprennent d' abord. Passer en revue tous les aspects de celui-ci (ne pas savoir quelle partie vous ne comprenez pas) prendrait beaucoup de temps - plus de temps que je ne suis prêt à en mettre dans une réponse qui a plus de 5 ans à ce stade, alors que la plupart peut déjà comprendre.
Jon Skeet le

@JonSkeet Cool, je vais le mettre dans une nouvelle question, même si j'ai souligné l'aspect que je n'ai pas compris dans ma question. Cela étant, l'extrait de code entier que j'ai ajouté avec lui.
Blank le

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

Voici un exemple de liste d'objets

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

8

Voici des options légèrement différentes pour accomplir la tâche à accomplir.

utilisant toMap:

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

utilisant Map::merge:

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

4

Voici la solution simple de StreamEx

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

Réduisez le code passe-partout: collect(Collectors.


1
Quelle est la raison de l'utiliser sur les flux Java8?
Torsten Ojaperv

1

Si vous êtes prêt à utiliser une bibliothèque tierce, vous pouvez utiliser la Collectors2classe dans Eclipse Collections pour convertir le Listen un Bagfichier Stream. A Bagest une structure de données conçue pour le comptage .

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

Production:

{World=1, Hello=2}

Dans ce cas particulier, vous pouvez simplement collectle Listdirectement dans un fichier Bag.

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

Vous pouvez également créer le Bagsans utiliser de Streamen adaptant le Listavec les protocoles des collections Eclipse.

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

ou dans ce cas particulier:

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

Vous pouvez également créer le sac directement.

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

A Bag<String>est comme a Map<String, Integer>en ce sens qu'il garde en interne la trace des clés et de leur décompte. Mais, si vous demandez une Mapclé qu'il ne contient pas, il reviendra null. Si vous demandez à un Bagune clé qu'il ne contient pas occurrencesOf, il renverra 0.

Remarque: je suis un committer pour les collections Eclipse.

En utilisant notre site, vous reconnaissez avoir lu et compris notre politique liée aux cookies et notre politique de confidentialité.
Licensed under cc by-sa 3.0 with attribution required.