I have a few Map
objects that are keyed by the same type K
with differently typed values V1...VN
, which for the purpose of this question do not share a 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 a 'value mapper' to map the V1...VN
values to a commonly-typed V
new values (i.e. a Function<? super Entry<K, VN>, ? extends V>
) on these maps. As such, I have the following static
helper method to perform 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 make it safe to assume that only after the filtering on each map will give me distinct keys for the final Map
object (there can be the same keys used in each map), but in the event that does not hold true in the future, I know I can provide a supplementary mergeFunction
expression to Collectors.toMap(Function, Function, BinaryOperator)
to handle this properly.
The final code now reads something like 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 of doing this? This sounds like yet another case of reducing stuff (filtered maps) into a final collection (Map
) which requires different reduction invocations (the value mapping part), and I am not sure if I am approaching this the correct way or not.
* - If they do, then I guess V1...VN
can implement a parent method, say V convertToV(Object... possiblyAdditionalArgumentsToPerformTheConversion)
, so that my problem is reduced to just applying different forms of filtering for different maps. If there is also a simpler solution given this alternate assumption, feel free to mention it too.
If I understand your filtering criteria correctly, you want to check if the filtered Stream you produced from the value List has any elements, and if so, pass the corresponding Map entry to the output Map . Map<String, List<BoMLine>> filtered = materials. entrySet() . stream() .
All you need is a mapping function to convert one object to another, and the map() function will do the transformation for you. It is also an intermediate stream operation which means you can call other Stream methods like a filter or collect on this to create a chain of transformation.
Since our filter condition requires an int variable we first need to convert Stream of String to Stream of Integer. That's why we called the map() function first. Once we have the Stream of Integer, we can apply maths to find out even numbers. We passed that condition to the filter method.
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 like:
Stream.of(filterAndMapValue(kv1, predicateForKV1, mapV1toV),
filterAndMapValue(kv2, predicateForKV2, mapV2toV),
filterAndMapValue(kv3, predicateForKV3, mapV3toV),
…)
.flatMap(Function.identity())
.collect(Collectors.toMap(Entry::getKey, Entry::getValue));
Note that for a small number of input maps, you may use Stream.concat
but as the number grows, the approach shown above is preferable.
I wouldn’t expect a noticeable performance gain, but this approach will verify your assumption that there are no duplicate keys in the remaining entries.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With