10

I have the following code:

private static <T> Map<String, ?> getDifference(final T a, final T b, final Map<String, Function<T, Object>> fields) { return fields.entrySet().stream() .map(e -> { final String name = e.getKey(); final Function<T, Object> getter = e.getValue(); final Object pairKey = getter.apply(a); final Object pairValue = getter.apply(b); if (Objects.equals(pairKey, pairValue)) { return null; } else { return Pair.of(name, pairValue); } }) .filter(Objects::nonNull) .collect(Collectors.toMap(Pair::getKey, Pair::getValue)); } 

Now, pairValue can be null. In order to avoid the NPE as described here, while "collect"-ing, I wish to ensure that I send only those values that are non-null. If null, I want to send "".

So, I tried replacing the last line with this:

.collect(Collectors.toMap(Pair::getKey,Optional.ofNullable(Pair::getValue).orElse("")); 

And other modifications thereof:

.collect(Collectors.toMap(pair -> pair.getKey(), Optional.ofNullable(pair -> pair.getValue()).orElse("")); 

Does not compile. I'm not sure what is needed here. Any help?

10
  • 1
    Can you elaborate on what doesn't work? Does the computer catch fire? Commented Feb 15, 2018 at 4:04
  • Java 8 NullPointerException in Collectors.toMap stackoverflow.com/questions/24630963/… Is this the same problem? Commented Feb 15, 2018 at 4:05
  • It doesn't compile, edited the question. Commented Feb 15, 2018 at 4:05
  • 1
    Unfortunately there's not much that I can do at the moment. I voted to reopen the question and flagged it for moderator intervention, so we'll see what happens. Commented Feb 15, 2018 at 4:10
  • 1
    @user2116243 It seems it was removed! Commented Feb 15, 2018 at 4:16

2 Answers 2

10

You can just collect into a HashMap which allows null values without the need for an Optional:

private static <T> Map<String, Object> getDifference( final T a, final T b, final Map<String, Function<T, Object>> fields) { return fields.entrySet().stream() .map(e -> { final Function<T, Object> getter = e.getValue(); final Object value = getter.apply(b); return Objects.equals(getter.apply(a),value)? null: Pair.of(e.getKey(), value); }) .filter(Objects::nonNull) .collect(HashMap::new, (m,p) -> m.put(p.getKey(),p.getValue()), Map::putAll); } 

By the way, it is discouraged to use wildcards in return types, they can make the caller’s life unnecessarily hard for no benefit.

For comparison, here the same operation without Stream:

private static <T> Map<String, Object> getDifference( final T a, final T b, final Map<String, Function<T, Object>> fields) { HashMap<String, Object> result = new HashMap<>(); fields.forEach((key, getter) -> { final Object value = getter.apply(b); if(!Objects.equals(getter.apply(a), value)) result.put(key, value); }); return result; } 

Of course, this would also work with optional:

private static <T> Map<String, Optional<Object>> getDifference( final T a, final T b, final Map<String, Function<T, Object>> fields) { HashMap<String, Optional<Object>> result = new HashMap<>(); fields.forEach((key, getter) -> { final Object value = getter.apply(b); if(!Objects.equals(getter.apply(a), value)) result.put(key, Optional.ofNullable(value)); }); return result; } 

But if all you want to do, is replace null with an empty string, you don’t need an Optional:

private static <T> Map<String, Object> getDifference( final T a, final T b, final Map<String, Function<T, Object>> fields) { HashMap<String, Object> result = new HashMap<>(); fields.forEach((key,getter) -> { final Object value = getter.apply(b); if(!Objects.equals(getter.apply(a), value)) result.put(key, value==null? "": value); }); return result; } 

and well, this substitution would also work out-of-the-box with your original code, if you just do it in the map function instead of the collector:

private static <T> Map<String, ?> getDifference(final T a, final T b, final Map<String, Function<T, Object>> fields) { return fields.entrySet().stream() .map(e -> { final String name = e.getKey(); final Function<T, Object> getter = e.getValue(); final Object pairKey = getter.apply(a); final Object pairValue = getter.apply(b); if (Objects.equals(pairKey, pairValue)) { return null; } else { return Pair.of(name, pairValue==null? "": pairValue); } }) .filter(Objects::nonNull) .collect(Collectors.toMap(Pair::getKey, Pair::getValue)); } 

or

private static <T> Map<String, Object> getDifference( final T a, final T b, final Map<String, Function<T, Object>> fields) { return fields.entrySet().stream() .map(e -> { final Function<T, Object> getter = e.getValue(); final Object pairValue = getter.apply(b); return Objects.equals(getter.apply(a), pairValue)? null: Pair.of(e.getKey(), pairValue==null? "": pairValue); }) .filter(Objects::nonNull) .collect(Collectors.toMap(Pair::getKey, Pair::getValue)); } 
Sign up to request clarification or add additional context in comments.

Comments

9

You have incorrect syntax. toMap()'s second parameter must be lambda, so

.collect(Collectors.toMap( pair -> pair.getKey(), pair -> Optional.ofNullable(pair.getValue()).orElse("") )); 

OR

you can modify map() section as follows

return Pair.of(name, Optional.ofNullable(pairValue).orElse("")); 

and use your original collect()

Comments

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.