Carte <K, V> Retour à la carte <V, Carte <K, V >> après la valeur groupingBy, au lieu de la carte <Obj, Liste <Entrée <K, V >>>

Greco:

Je suis aux prises avec le maintien des structures de données que je veux à travers des opérations de streaming en Java, très probablement en raison du manque de bonne compréhension et la pratique.

public class Main {
    public static void main(String[] args) {
        List<Integer> list = Arrays.asList(1, 1, 1, 2, 3, 3, 3, 3);

            //Group by
            Map <Integer, Long> countGrouped = list.stream().collect(
                    Collectors.groupingBy(
                            x -> x, Collectors.counting()));
            System.out.println("group by value, count " + countGrouped);

            //Sort desc
            Map <Integer, Long> descendingSorted = new LinkedHashMap<>();
            countGrouped.entrySet().stream()
                .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
                .forEachOrdered(x -> descendingSorted.put(x.getKey(), x.getValue()));
            System.out.println("sorted " + descendingSorted);

            //filter
            Map <Integer, Long> filtered = new LinkedHashMap<>();
            descendingSorted.entrySet().stream()
                .filter(x -> x.getValue() >= 2)
                .forEach(x -> filtered.put(x.getKey(), x.getValue()));;
            System.out.println("filtered " + filtered);

            //Split groups
            Map<Object, List<Entry<Integer, Long>>> groups = filtered.entrySet().stream()
                    .collect(Collectors.groupingBy(x -> x.getValue()));
            System.out.println("grouped " + groups);
    }
}

Résultant en

group by value, count {1=3, 2=1, 3=4}
sorted {3=4, 1=3, 2=1}
filtered {3=4, 1=3}
grouped {3=[1=3], 4=[3=4]}

ce qui est correct, mais je suis entrer dans des structures de données de plus en plus abstruses sans sens particulier, comme vous pouvez le voir, la finition dans un (wtf?) Map<Object, List<Entry<Integer, Long>>>que vous pouvez voir. Alors qu'il peut être juste Map<Int, Map<Int, Int>>.

La question spécifique est, comment puis-je transformer et les sorties de contiens structure de données résultant des opérations de flux?

J'ai vu les collectionneurs fournir à transformer les opérations toMap (...), et je suppose que c'est le chemin à parcourir, mais je ne suis pas (en raison du manque de connaissances appropriées, je pense) pour le faire fonctionner.

Dans ce cas, me semble que je grandement aidé par une explication didactique, lien vers des ressources complètes pour obtenir une meilleure compréhension des flux et la programmation fonctionnelle, ou des trucs comme ça, plus que la solution réelle au cas particulier (ce qui serait bien pour un exercice, mais vous voyez l'idée)

Holger:

Il est un peu surprenant de constater que vous avez des difficultés ici, comme vous l' avez montré la connaissance de toutes les choses nécessaires. Vous savez que groupingBypeut prendre une autre Collector, vous avez nommé celui de droite, toMapdéjà, et vous avez utilisé les fonctions pour extraire les Map.Entryvaleurs déjà.

La combinaison de ces choses, vous donne

Map<Long, Map<Integer, Long>> groups = filtered.entrySet().stream()
    .collect(Collectors.groupingBy(x -> x.getValue(),
        Collectors.toMap(x -> x.getKey(), x -> x.getValue())));
System.out.println("grouped " + groups);

Pour démontrer le fonctionnement mieux, je l'ai changé l'entrée

List<Integer> list = Arrays.asList(1, 1, 1, 2, 3, 3, 3, 3, 4, 4, 4);

qui se traduit par

grouped {3=[1=3, 4=3], 4=[3=4]}

cependant, il est inutile de répéter les comptages qui sont toujours les mêmes que l'extérieur des touches cartes. Ainsi, une solution de rechange serait

Map<Long, List<Integer>> groups = filtered.entrySet().stream()
    .collect(Collectors.groupingBy(Map.Entry::getValue,
        Collectors.mapping(Map.Entry::getKey, Collectors.toList())));
System.out.println("grouped " + groups);

qui conduit à

grouped {3=[1, 4], 4=[3]}

Notez que vous ne devez pas utiliser forEach/ forEachOrderedpour putdans une carte. Vos étapes intermédiaires devraient plutôt

//Sort desc
Map<Integer, Long> descendingSorted = countGrouped.entrySet().stream()
    .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
        (a,b) -> { throw new AssertionError(); }, LinkedHashMap::new));
System.out.println("sorted " + descendingSorted);

//filter
Map<Integer, Long> filtered = descendingSorted.entrySet().stream()
    .filter(x -> x.getValue() >= 2)
    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue,
        (a,b) -> { throw new AssertionError(); }, LinkedHashMap::new));
System.out.println("filtered " + filtered);

Le toMapcollecteur d' accepter une force de l' usine de carte nous pour fournir une fonction de fusion, mais depuis notre entrée est déjà une carte qui doit avoir des clés distinctes, je fourni une fonction jeter toujours là, comme quelque chose serait très mal, si les doublons apparaissent.

Mais notez que forçant toutes ces opérations de collecte de nouvelles cartes est inutilement compliqué et inefficace. Il y a aussi inutile de trier les données tout le premier et de réduire la quantité de données par la filtersuite. Le filtrage premier réduirait potentiellement le travail de l'étape de tri alors que le résultat de l'opération de filtrage ne doit pas dépendre de l'ordre.

Il est préférable de faire l'opération en une seule canalisation

List<Integer> list = Arrays.asList(1, 1, 1, 2, 3, 3, 3, 3, 4, 4, 4);

Map<Integer, Long> countGrouped = list.stream().collect(
    Collectors.groupingBy(x -> x, Collectors.counting()));
System.out.println("group by value, count " + countGrouped);

Map<Long, List<Integer>> groups = countGrouped.entrySet().stream()
    .filter(x -> x.getValue() >= 2)
    .sorted(Map.Entry.comparingByValue(Comparator.reverseOrder()))
    .collect(Collectors.groupingBy(Map.Entry::getValue, LinkedHashMap::new, 
        Collectors.mapping(Map.Entry::getKey, Collectors.toList())));

System.out.println("grouped " + groups);

Notez que contrairement au code précédent, maintenant la dernière opération de regroupement conservera également l'ordre, qui se traduit par

grouped {4=[3], 3=[1, 4]}

par exemple, les groupes sont classés par ordre décroissant nombre.

Étant donné que le compte est la clé de la carte résultante, on peut également utiliser une carte intrinsèquement triés comme type de résultat et omettons l'étape de tri:

Map<Long, List<Integer>> groups = countGrouped.entrySet().stream()
    .filter(x -> x.getValue() >= 2)
    .collect(Collectors.groupingBy(Map.Entry::getValue,
        () -> new TreeMap<>(Comparator.<Long>reverseOrder()),
        Collectors.mapping(Map.Entry::getKey, Collectors.toList())));

Les principaux mensonges de différence dans le comportement de la carte de résultat après l'opération de flux, par exemple si vous insérez plusieurs éléments à elle, comme TreeMapinsérera nouvelles clés en fonction de l'ordre descendant alors que LinkedHashMapleur ajouter à la fin, le maintien de l'ordre d'insertion.

Je suppose que tu aimes

Origine http://43.154.161.224:23101/article/api/json?id=204310&siteId=1
conseillé
Classement