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)
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 groupingBy
peut prendre une autre Collector
, vous avez nommé celui de droite, toMap
déjà, et vous avez utilisé les fonctions pour extraire les Map.Entry
valeurs 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
/ forEachOrdered
pour put
dans 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 toMap
collecteur 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 filter
suite. 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 TreeMap
insérera nouvelles clés en fonction de l'ordre descendant alors que LinkedHashMap
leur ajouter à la fin, le maintien de l'ordre d'insertion.