2

I want to filter some values in the Map via Stream. Let's see the simple example, where I want to extract entries with keys, for example, higher than 2.

Here is the code I use:

Map<Integer, String> map = new HashMap<>(); 
map.put(1, "one"); 
map.put(2, "two"); 
map.put(3, "three"); 
map.put(4, "four"); 

Map<Integer, String> map2 = map.entrySet().stream()
    .filter(e -> e.getKey() > 2)
    .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));

System.out.println(map2.toString());

The result correct:

{3=three, 4=four}

When I decide to put the String value as null, that's legal, this is thrown:

Exception in thread "main" java.lang.NullPointerException

Here is the continuation of the code:

map.put(5, null);
map.put(6, "six");

Map<Integer, String> map3 = map.entrySet().stream()
    .filter(e -> e.getKey() > 2)
    .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));

System.out.println(map3.toString());

I would expect the result:

{3=three, 4=four, 5=null, 6=six}

Well, it works when I change the condition in the filter's Predicate to e -> e.getKey() < 2 since the null value is unaffected. How to handle this with Stream? The null value may occur anywhere intentionally. I don't want to use the for-loop. Shouldn't Stream architecture be more "null-safe"?

The question How should we manage jdk8 stream for null values deals with the different problem. I don't want to use .filter(Objects::nonNull) because I need to keep the null values.


Please don't mark it as duplicate with famous What is a NullPointerException and how do I fix it. I know this well, I ask the solution using Stream, that is not as low-level as for-loop is. This behaviour quite limits me.

Stefan Zobel
  • 3,182
  • 7
  • 28
  • 38
Nikolas Charalambidis
  • 40,893
  • 16
  • 117
  • 183
  • Tired of Null Pointer Exceptions? Consider Using Java SE 8's Optional! – Yahya Jun 23 '17 at 17:21
  • Would you give me an example applied to my sample problem? I am not familiar with the `Optional`. – Nikolas Charalambidis Jun 23 '17 at 17:24
  • 1
    Look [here](https://stackoverflow.com/questions/24630963/java-8-nullpointerexception-in-collectors-tomap) and [here](https://stackoverflow.com/questions/43606886/java-stream-throws-npe-when-tomap-stores-a-null-value) – fps Jun 23 '17 at 18:18
  • I'm sorry, it doesn't answer my question. – Nikolas Charalambidis Jun 23 '17 at 18:21
  • 2
    @NikolasCharalambidis with current `toMap` implementation you can't put null values. You have to create own `collector` - this is described in one of answers in linked question – ByeBye Jun 23 '17 at 18:23
  • 3
    @NikolasCharalambidis How is that the linked questions (along with their answers) don't answer your question? – fps Jun 23 '17 at 18:25
  • 2
    Unless you are not telling us something, then it is a duplicate. the exact answer is this: `Map map3 = map.entrySet().stream().filter(e -> e.getKey() > 2).collect(HashMap::new, (map, entry) -> map.put(entry.getKey(), entry.getValue()), HashMap::putAll);` Would this satisfy your needs? – Eugene Jun 24 '17 at 09:26
  • @Eugene, yea, this is what I need. Why don't you post it as an answer? – Nikolas Charalambidis Jun 24 '17 at 11:47
  • 1
    because that *is* the answer in one of the linked questions... But it should not matter - it's good that you find your solution – Eugene Jun 24 '17 at 11:48
  • 1
    I have used the similar in the wrong way, my bad... Also I am happy you give me lot of time to edit my question (since I am not online 24/7). You can close it if you wish a lot. – Nikolas Charalambidis Jun 24 '17 at 11:50
  • 1
    actually you can close it too... or even delete it. I would delete it to be honest :) – Eugene Jun 24 '17 at 12:25

1 Answers1

2

Optional is a container object which is used to contain not-null objects. Optional object is used to represent null with absent value.

You can do something like this:

Map<Integer, Optional<String>> map = new HashMap<>(); 

//Optional.ofNullable - allows passed parameter to be null.
map.put(1, Optional.ofNullable("one")); 
map.put(2, Optional.ofNullable("two")); 
map.put(3, Optional.ofNullable(null)); 
map.put(4, Optional.ofNullable("four")); 

Map<Integer, Optional<String>> map2 = map.entrySet().stream()
        .filter(e -> e.getKey() > 2)
        .collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));

System.out.println(map2.toString());

Test

{3=Optional.empty, 4=Optional[four]}

For more utility methods to facilitate code to handle values as available or not available instead of checking null values, I refer you to the documentation.

Yahya
  • 13,349
  • 6
  • 30
  • 42
  • Yes, it may solve my problem. However, my goal is to get the new `Map` back with the new values including `null`. It means I have to check the value with `.isPresent()`? – Nikolas Charalambidis Jun 23 '17 at 17:42
  • @NikolasCharalambidist The idea of `Optional` is to achieve what you want. You need to replace the second type argument `String` with `Optional` then work with it. – Yahya Jun 23 '17 at 18:18
  • 2
    Yahya, I initially downvoted your answer, but I immediately reverted it, without reading your previous comment. I downvoted because you were not answering what the OP was asking, but then I thought that I was being too harsh, because even though OP was not asking about `Optional`, you were providing a workaround. However, a better way to solve this problem is to use a custom collector. Anyways, this is a duplicate question, check the answers of the linked original questions to find out. And sorry for being too quick and harsh with my downvote. – fps Jun 23 '17 at 18:22