Je sais comment "transformer" un simple Java à List
partir de Y
-> Z
, c'est-à-dire:
List<String> x;
List<Integer> y = x.stream()
.map(s -> Integer.parseInt(s))
.collect(Collectors.toList());
Maintenant, je voudrais faire la même chose avec une carte, c'est-à-dire:
INPUT:
{
"key1" -> "41", // "41" and "42"
"key2" -> "42 // are Strings
}
OUTPUT:
{
"key1" -> 41, // 41 and 42
"key2" -> 42 // are Integers
}
La solution ne doit pas se limiter à String
-> Integer
. Tout comme dans l' List
exemple ci-dessus, je voudrais appeler n'importe quelle méthode (ou constructeur).
java
mapreduce
java-8
java-stream
collectors
Benjamin M
la source
la source
e -> e.getKey()
parMap.Entry::getKey
. Mais c'est une question de goût / style de programmation.Voici quelques variantes de la réponse de Sotirios Delimanolis , qui était plutôt bonne au départ (+1). Considérer ce qui suit:
Quelques points ici. Le premier est l'utilisation de caractères génériques dans les génériques; cela rend la fonction un peu plus flexible. Un caractère générique serait nécessaire si, par exemple, vous vouliez que la carte de sortie ait une clé qui est une superclasse de la clé de la carte d'entrée:
(Il existe également un exemple pour les valeurs de la carte, mais il est vraiment artificiel, et j'avoue que le caractère générique borné pour Y n'aide que dans les cas limites.)
Un deuxième point est qu'au lieu d'exécuter le flux sur la carte d'entrée
entrySet
, je l'ai exécuté surkeySet
. Cela rend le code un peu plus propre, je pense, au prix d'avoir à extraire des valeurs de la carte plutôt que de l'entrée de la carte. Par ailleurs, j'ai d'abord eukey -> key
comme premier argumenttoMap()
et cela a échoué avec une erreur d'inférence de type pour une raison quelconque. Le changer en(X key) -> key
fonction, comme l'a faitFunction.identity()
.Une autre variante encore est la suivante:
Cela utilise à la
Map.forEach()
place des flux. C'est encore plus simple, je pense, car cela évite les collecteurs, qui sont un peu maladroits à utiliser avec les cartes. La raison en est queMap.forEach()
donne la clé et la valeur en tant que paramètres séparés, alors que le flux n'a qu'une seule valeur - et vous devez choisir d'utiliser la clé ou l'entrée de carte comme valeur. Du côté négatif, cela n'a pas la richesse et la bonté des autres approches. :-)la source
Function.identity()
peut sembler cool, mais comme la première solution nécessite une recherche de carte / hachage pour chaque entrée, contrairement à toutes les autres solutions, je ne le recommanderais pas.Une solution générique comme ça
Exemple
la source
La fonction de la goyave
Maps.transformValues
est ce que vous recherchez, et elle fonctionne bien avec les expressions lambda:la source
java.util.Function
, vous avez deux options. 1. Évitez le problème en utilisant un lambda pour permettre à l'inférence de type Java de le comprendre. 2. Utilisez une référence de méthode comme javaFunction :: apply pour produire un nouveau lambda que l'inférence de type peut comprendre.Doit-elle absolument être 100% fonctionnelle et fluide? Sinon, que diriez-vous de cela, qui est à peu près aussi court que possible:
( si vous pouvez vivre avec la honte et la culpabilité de combiner des flux avec des effets secondaires )
la source
Ma bibliothèque StreamEx qui améliore l'API de flux standard fournit une
EntryStream
classe qui convient mieux à la transformation de cartes:la source
Une alternative qui existe toujours à des fins d'apprentissage est de construire votre collecteur personnalisé via Collector.of () bien que le collecteur JDK toMap () ici soit succinct (+1 ici ).
la source
map2.entrySet().forEach(entry -> { if (map1.containsKey(entry.getKey())) { map1.get(entry.getKey()).merge(entry.getValue()); } else { map1.put(entry.getKey(),entry.getValue()); } }); return map1
ou des valeurs seront perdues lors de la réduction.Si cela ne vous dérange pas d'utiliser des bibliothèques tierces, ma bibliothèque cyclops- react a des extensions pour tous les types de collections JDK , y compris Map . Nous pouvons simplement transformer la carte directement en utilisant l'opérateur 'map' (par défaut, map agit sur les valeurs de la carte).
bimap peut être utilisé pour transformer les clés et les valeurs en même temps
la source
La solution déclarative et plus simple serait:
yourMutableMap.replaceAll ((key, val) -> return_value_of_bi_your_function); Nb. sachez que vous modifiez l'état de votre carte. Donc, ce n'est peut-être pas ce que vous voulez.
Bravo à: http://www.deadcoderising.com/2017-02-14-java-8-declarative-ways-of-modifying-a-map-using-compute-merge-and-replace/
la source