4

I have a particular problem and was wondering whether the Java 8 Streams API could solve it. I know that this can be done outside of using Streams API but I don't want to add all the boilerplate code associated with trying to achieve that, if it can be done using Streams. I have a map

Map<String, String> greetings = new HashMap<>();
greetings.put("abc", "Hello");
greetings.put("def", "Goodbye");
greetings.put("ghi", "Ciao");
greetings.put("xyz", "Bonsoir");

and a list of keys:

List<String> keys = Arrays.asList("def", "zxy");

and using the above with Streams API, is it possible to filter that down to:

Map<String, String> filteredGreetings = new HashMap<>();
filteredGreetings.put("def", "Goodbye");
filteredGreetings.put("xyz", "Bonsoir");

Hopefully this makes sense what I am trying to achieve.

So far I have got this to work only when specifying the exact key which to filter the map's keySet on, but then this would only return a single entry set. I am interested in a completely filtered down map and I am struggling to achieve that.

2
  • Is it just a coincidental, typo or a requirement, I get to notice the key "zxy" and the result still put("xyz", "Bonsoir")? Commented Jun 14, 2019 at 18:48
  • 1
    Yep, that’s a typo. Apologies Commented Jun 15, 2019 at 21:40

3 Answers 3

4

If the input and the expected output in the question is not a typo, you can also retain the keys of the input map as:

Map<String, String> futureGreetings = new HashMap<>(greetings);
futureGreetings.keySet().retainAll(keys);
Sign up to request clarification or add additional context in comments.

2 Comments

Of course, this doesn't use streams, but one can compare the complexity and readability for both the solutions before opting.
That's the best and most simple solution. Much better than mine ;)
3

Try this:

Map<String, String> result = keys.stream()
        .filter(greetings::containsKey)
        .collect(Collectors.toMap(Function.identity(), greetings::get));

Or the other way round:

Map<String, String> result = greetings.entrySet().stream()
        .filter(e -> keys.contains(e.getKey()))
        .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue));

For the second approach I would recommend using a Set<String> for a larger list of keys because it has a O(1) time complexity for .contains() because it does not contain any duplicates:

Set<String> keys = new HashSet<>(Arrays.asList("def", "zxy"));

4 Comments

For a list of two elements, the time complexity really doesn’t matter. In fact, the overhead of hashing will be higher than the linear lookup.
@Holger Yes that's true. Just want to add that information for general.
@SamuelPhilipp spot-on - thanks a lot! I wasn't aware of "identity" either. I need to read up a bit more on this.
@RichardC Function.identity() is a synonym for i -> i.
0

Try this

filteredGreetings= keys.stream()
                  .collect(Collectors.toMap(Function.identity(),key->greetings.get(key)));

or even for absent key you can use getOrDefault() method.

... .collect(Collectors.toMap(Function.identity(),key->greetings.getOrDefault(key,"")));

1 Comment

Because that I added second approach. Though I think he did not mean the keys were absent

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.