1

Given a Set of objects that I want to use as keys, how can I easily get a Map instance leaving the values as null?

The purpose is to pre-populate the map with keys before determining the values to be stored.

Of course, I could create an empty map, then loop the set of would-be key objects, while doing a put on each, along with null as the value.

Set< Month > months = EnumSet.of( Month.MARCH , Month.MAY , Month.JUNE ) ; 
Map< Month , String > map = new EnumMap<>( Month.class ) ;
for( Month month : months ) 
{
    map.put( month , null ) ;
}

I just wonder if there is a nifty trick to do this with less code. Something like the opposite of Map#keySet.

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
  • What kind of `Map`? You could write your own `Map` implementation that accepts a `Set` for initialization and/or is backed by a `Set`. Else it's not possible. Either use a for-loop or `Stream` (which is essentially the same as a for-loop). `...` **EDIT:** What's the reason for pre-populating the `Map` with keys? – Benjamin M Mar 06 '21 at 21:32
  • @BenjaminM Any `Map` implementation would do. I could always pass that map to the constructor of another `Map` I want. – Basil Bourque Mar 06 '21 at 21:51

2 Answers2

1
set.stream().collect(Collectors.toMap(k -> k, k -> null));
Louis Wasserman
  • 191,574
  • 25
  • 345
  • 413
  • I cannot seem to get that code to work. I’m not sure about the runtime error. I suspect that null values are not tolerated. See code [run live at IdeOne.com](https://ideone.com/xvWmgB). – Basil Bourque Mar 06 '21 at 21:45
  • That code does work if we use empty string as the value rather than null. See [this code on IdeOne.com](https://ideone.com/8cotE3). `Map< Month , String > map = Set.of( Month.MARCH , Month.MAY , Month.JUNE ).stream().collect( Collectors.toMap( k -> k , k -> "" ) );` – Basil Bourque Mar 07 '21 at 04:19
1

Collectors.toMap() and the static factory methods like Map.of() use internally Map.merge which will throw NPE if key or value is null.

See this post: java-8-nullpointerexception-in-collectors-tomap. And see this issue page on the OpenJDK project: JDK-8148463 Collectors.toMap fails on null values

You could work around by using Stream.collect with the three args signature as a reduction.

collect(Supplier supplier, BiConsumer accumulator, BiConsumer combiner)

Something like:

Set< Month > months = EnumSet.of( Month.MARCH , Month.MAY , Month.JUNE ) ;         
Map< Month , String > myMap = 
        months.stream()
              .collect(
                  HashMap::new , 
                  ( map , val )-> map.put( val , null ) , 
                  HashMap::putAll
              );
              
System.out.println(myMap);

See that code run live at IdeOne.com.

But I'm not sure if this is either more readable or more elegant than your approach with the classic for-loop

Basil Bourque
  • 303,325
  • 100
  • 852
  • 1,154
Eritrean
  • 15,851
  • 3
  • 22
  • 28