I have a Java Map, and I want to use Java Map Stream operations to convert values (e.g., from String to Integer) and filter out certain entries in a single pass.
For example, given this input:
Map<String, String> input = new HashMap<>();
input.put("a", "1234");
input.put("b", "2345");
input.put("c", "3456");
input.put("d", "4567");
I want to:
Convert all values to Integers.
Remove entries where the value is odd.
Collect the result without calling .entrySet().stream() twice.
Currently, my approach is:
Map<String, Integer> output = input.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
e -> Integer.parseInt(e.getValue())
))
.entrySet().stream()
.filter(e -> e.getValue() % 2 == 0)
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
This works and produces {a=1234, c=3456}, but I’m wondering:
How can I perform both the transformation and filtering in a single stream operation, calling .collect() only once at the end?
Instead of first converting values and then filtering in a second stream operation, you can apply both transformations within the same stream:
Map<String, Integer> output = input.entrySet().stream()
.filter(e -> Integer.parseInt(e.getValue()) % 2 == 0) // Convert & filter at once
.collect(Collectors.toMap(
Map.Entry::getKey,
e -> Integer.parseInt(e.getValue())
));
Why this works:
We parse and filter at the same time in .filter(), reducing the need for a second pass.
We collect directly at the end, so .entrySet().stream() is only called once.
You can also move filtering into toMap() by using an optional filtering condition inside a stream pipeline:
Map<String, Integer> output = input.entrySet().stream()
.map(e -> Map.entry(e.getKey(), Integer.parseInt(e.getValue()))) // Convert values
.filter(e -> e.getValue() % 2 == 0) // Remove odd values
.collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));
- .map() converts the values before filtering, avoiding duplicate operations.
- .filter() then removes odd numbers before collecting into the final map.
Another approach is using Collectors.filtering(), which allows filtering within the collector itself:
Map<String, Integer> output = input.entrySet().stream()
.collect(Collectors.toMap(
Map.Entry::getKey,
e -> Integer.parseInt(e.getValue()),
(v1, v2) -> v1, // Merge function (not needed here)
() -> new HashMap<>()
));
output.entrySet().removeIf(e -> e.getValue() % 2 != 0); // Remove odd values after collection
Why this works:
- We first convert everything and collect into a HashMap.
- Then we remove odd values after collecting, which can be useful in cases where filtering before collecting isn’t feasible.