3

I have a Map<String, Integer>, which has some keys and values. I want to associate all keys with the values as the key's length. I have been able to solve this in pure java and java-8, but somehow I don't think that appending a terminal operation at the end like .collect(Collectors.toList()); which is not required for me in my code.

My code: ( Java ) works fine

 Map<String, Integer> nameLength = new HashMap<>();
   nameLength.put("John", null);
    nameLength.put("Antony", 6);
    nameLength.put("Yassir", 6);
    nameLength.put("Karein", 6);
    nameLength.put("Smith", null);
    nameLength.put("JackeyLent",null);
    for(Entry<String, Integer> length: nameLength.entrySet()){
      if(length.getValue() == null){
        nameLength.put(length.getKey(),length.getKey().length());
      }
    }

Java-8 also works fine but the terminal operation is useless, how I avoid it without using .foreach().

nameLength.entrySet().stream().map(s->{
  if(s.getValue() == null){
    nameLength.put(s.getKey(),s.getKey().length());
  }
  return nameLength;
}).collect(Collectors.toList());
System.out.println(nameLength);

Any other way in which I can do the above logic in Java-8 and above??

5
  • Why are you putting these artificial restrictions: no for statements, no forEach? Commented Apr 23, 2020 at 13:31
  • 1
    This sounds like the XY problem - meta.stackexchange.com/questions/66377/what-is-the-xy-problem . What are you trying to achieve? Commented Apr 23, 2020 at 13:31
  • 1
    Also please edit your title to reflect your actual issue. Refactoring in Java-8 doesn't tell us anything. Commented Apr 23, 2020 at 13:34
  • Whatever this is doing, it is not refactoring. You gain nothing if you randomly put lambdas in your code, this particular case there's no reason to replace a perfectly fine and working foreach loop with arcane constructs of streams and lambdas that barely do what original code did. Commented Apr 23, 2020 at 13:52
  • that's terrible use for map.. mutable operation, that doesn't return anything interesting Commented Apr 23, 2020 at 13:59

2 Answers 2

4

If you're going to use streams then you should avoid side effects. Functional programming is all about pure operations where the output depends only on the input and functions have no side effects. In other words, create a new map instead of modifying the existing one.

If you do that you might as well just throw away the partially-filled-out map and recompute everything from scratch. Calling String.length() is cheap and it's not really worth the effort to figure out which values are null and which aren't. Recompute all the lengths.

Map<String, Integer> newMap = nameLength.keySet().stream()
    .collect(Collectors.toMap(
        name -> name,
        name -> name.length()
    ));

On the other hand if you just want to patch up your current map streams don't really buy you anything. I'd just modify it in place without involving streams.

for (Map.Entry<String, Integer> entry: nameLength.entrySet()) {
  if (entry.getValue() == null) {
    entry.setValue(entry.getKey().length());
  }
}

Or, as discussed above, you could simplify matters by replacing all of the lengths:

nameLength.replaceAll((name, __) -> name.length());

(__ signifies a variable that isn't used and so doesn't get a meaningful name.)

Sign up to request clarification or add additional context in comments.

1 Comment

@M.Prokhorov My answer addresses that. It's easier to recompute everything than to do it piecemeal, so that's what the first and last snippets do. The middle one only replaces null values and has the ugliest code as a result.
3

You almost there, just use the filter to identify the entries with null values and then use Collectors.toMap to collect them into Map with key length as value

Map<String, Integer> nameLengths = nameLength.entrySet()
                 .stream()
                 .filter(entry->entry.getValue()==null)
                 .collect(Collectors.toMap(Map.Entry::getKey, entry->entry.getKey().length()));

Or more simpler way you have that check in Collectors.toMap

  Map<String, Integer> nameLengths = nameLength.entrySet()
                 .stream()
                 .collect(Collectors.toMap(Map.Entry::getKey, entry->entry.getValue() == null ? entry.getKey().length() : entry.getValue()));

2 Comments

Of all the answers I feel like you're the closest: nameLength.entrySet().stream().collect(Collectors.toMap(Map.Entry::getKey, entry -> entry.getValue() != null ? entry.getValue() : entry.getKey().length()));.
OMG, thank god for downvotes, any chance of explaining why ?

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.