How to effectively filter and collect the resulting map with different values ​​obtained from several maps?

I have several Map objects that are connected by the same type K with differently typed values V1...VN , which for this question do not share the supertype *:

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

I need to create a resulting map of type Map<K, V> , by filtering each of these maps differently, and then use the “mapper map” to map the values ​​of V1...VN to typical typical values ​​of V (for example, i.e. a Function<? super Entry<K, VN>, ? extends V> ) on these mappings. So I have the following static helper method to complete the first two steps:

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

My current use cases allow me to assume with confidence that only after filtering on each map will give me different keys for the final Map object (there may be the same keys used on each map), but if it does not work in the future, I know that I can provide an additional mergeFunction expression for Collectors.toMap(Function, Function, BinaryOperator) to handle this correctly.

The latest code now reads the following:

 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: Is there a more efficient way to do this? This sounds like another case of material reduction (filtered maps) to the final collection ( Map ), which requires different reductions (part of the display of values), and I'm not sure if I'm approaching this in the right way or not.

* - If yes, then I believe that V1...VN can implement the parent method, say V convertToV(Object... possiblyAdditionalArgumentsToPerformTheConversion) , so my problem boils down to simply applying different forms of filtering for different cards. If there is a simpler solution, given this alternative suggestion, feel free to mention it as well.

+6
source share
1 answer

If you change your method to

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

you can perform the operation as a single Stream operation, for example:

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

Please note that for a small number of input cards you can use Stream.concat , but as the number grows, the preferred approach is preferred.

I would not expect a noticeable performance gain, but this approach will confirm your assumption that the remaining entries do not have duplicate keys.

+2
source

Source: https://habr.com/ru/post/984832/


All Articles