2015-04-08 1 views
6

J'ai quelques Map objets qui sont calées par le même type K avec des valeurs différentes tapées V1...VN, qui, pour le but de cette question ne partagent pas supertype *:Comment filtrer et collecter efficacement une carte résultante avec des valeurs de carte dérivées différemment à partir de quelques cartes?

Map<K, V1> kv1 
Map<K, V2> kv2 
Map<K, V3> kv3 
... 
Map<K, VN> kvN 

Je dois créer une carte résultant en filtrant chacune de ces cartes différemment, puis en utilisant un 'mappeur de valeurs' pour mapper les valeurs V1...VN à des valeurs V communément typées (c'est-à-dire Function<? super Entry<K, VN>, ? extends V>) sur ces cartes. En tant que tel, j'ai la méthode d'assistance static suivante pour effectuer les deux premières étapes:

public static <K, VN, V> Map<K, V> filterAndMapValue(final Map<K, VN> map, 
     final Predicate<? super Entry<K, VN>> predicate, 
     final Function<? super Entry<K, VN>, ? extends V> mapper) { 
    return map.entrySet().stream().filter(predicate) 
      .collect(Collectors.toMap(Entry::getKey, mapper)); 
} 

Mes dossiers en cours d'utilisation en font de supposer que seulement après le filtrage sur chaque carte me donner des clés distinctes pour la final Map objet (il peut y avoir les mêmes clés utilisées dans chaque carte), mais dans le cas où cela ne se vérifie pas dans le futur, je sais que je peux fournir une expression supplémentaire mergeFunction à Collectors.toMap(Function, Function, BinaryOperator) pour gérer cela correctement.

Le code final se lit maintenant quelque chose comme ce qui suit:

Map<K,V> result = filterAndMapValue(kv1, predicateForKV1, mapV1toV); 
result.putAll(filterAndMapValue(kv2, predicateForKV2, mapV2toV)); 
result.putAll(filterAndMapValue(kv2, predicateForKV3, mapV3toV)); 
... 
result.putAll(filterAndMapValue(kvN, predicateForKVN, mapVNtoV)); 
// do something with result 

Question: Y at-il un moyen plus efficace de le faire? Cela ressemble à un autre cas de réduction de données (cartes filtrées) dans une collection finale (Map) qui nécessite des invocations de réduction différentes (la partie de mappage de valeur), et je ne suis pas sûr que j'approche la bonne façon.

* - Si c'est le cas, j'imagine que V1...VN peut implémenter une méthode parente, par exemple V convertToV(Object... possiblyAdditionalArgumentsToPerformTheConversion), de sorte que mon problème soit réduit à l'application de différentes formes de filtrage pour différentes cartes. S'il y a aussi une solution plus simple étant donné cette hypothèse alternative, n'hésitez pas à le mentionner aussi.

+0

Que diriez-vous de créer un objet pour contenir toutes les valeurs V1 ... VN et utiliser une seule carte? – BobTheBuilder

+0

@BobTheBuilder voulez-vous dire collecter toutes les valeurs des cartes au début? Le mappage des valeurs est fait différemment pour différents types, et à la fin, j'ai aussi besoin de connaître les mappages des touches pour les nouvelles valeurs 'V' ... J'ai également fourni une modification pour indiquer que les 'clés résultantes uniques 'est seulement applicable * après * le filtrage sur les cartes, de sorte que les cartes originales peuvent avoir les mêmes clés. Je ne serais pas en mesure de faire une «carte » dans ce sens. J'espère que ça répond à ta question. –

+0

Ce que je veux dire est de créer une classe MyValues ​​qui a des champs V1..VN.Si vous utilisez cette classe, MyValues ​​contient toutes les valeurs pour chaque clé dans un objet et il est plus facile à gérer et à maintenir – BobTheBuilder

Répondre

2

Si vous changez de méthode pour

public static <K, VN, V> Stream<Entry<K, V>> filterAndMapValue(Map<K, VN> map, 
    Predicate<? super Entry<K, VN>> predicate, 
    Function<? super Entry<K, VN>, ? extends V> mapper) { 

    return map.entrySet().stream().filter(predicate) 
       .map(e->new AbstractMap.SimpleEntry<>(e.getKey(), mapper.apply(e))); 
} 

vous pouvez effectuer l'opération comme une opération Stream simple comme:

Stream.of(filterAndMapValue(kv1, predicateForKV1, mapV1toV), 
      filterAndMapValue(kv2, predicateForKV2, mapV2toV), 
      filterAndMapValue(kv3, predicateForKV3, mapV3toV), 
      …) 
     .flatMap(Function.identity()) 
     .collect(Collectors.toMap(Entry::getKey, Entry::getValue)); 

Notez que pour un petit nombre de cartes d'entrée, vous pouvez utiliser Stream.concat mais à mesure que le nombre augmente, l'approche montrée ci-dessus est préférable.

Je ne m'attendrais pas à un gain de performance notable, mais cette approche va vérifier votre hypothèse qu'il n'y a pas de clés en double dans les entrées restantes.

+0

Merci pour la réponse! –