45

Is there a difference between Map.of() and Collections.emptyMap(), between List.of() and Collections.emptyList() and between Set.of() and Collections.emptySet()?

Daniel
  • 93
  • 1
  • 1
  • 5
msayag
  • 8,407
  • 4
  • 30
  • 29
  • 1
    What kind of difference are you looking for? The method names differ, but the resulting data structures behave extremely similar. – C-Otto Sep 25 '17 at 12:15
  • 2
    @C-Otto If you claim that the methods are just 'extremely similar' (and not identical), you must obviously think that there is a difference. – jarnbjo Sep 25 '17 at 12:29
  • @jarnbjo there are differences - see my answer – xenteros Sep 25 '17 at 12:31
  • @xenteros The question is valid enough and there are relevant differences in the returned objects, not just the specific class (as you point out in your answer), but also how the classes are implemented and behave. I just don't see the point in C-Otto's comment asking for 'what kind of difference the OP is looking for'. Yes, there are differences between the returned objects and a list of these differences might be an answer, although I am not 100% sure myself if the differences are just implementation specific or perhaps even bugs in the current state of OpenJDK 9. – jarnbjo Sep 25 '17 at 12:40
  • 3
    See also: https://stackoverflow.com/questions/40888754/usage-of-java-9-collection-factories/40943796#40943796 – Stuart Marks Sep 25 '17 at 16:07

1 Answers1

44

Yes, there are even behavioral and not just technical differences between the collections returned by the emptyXyz factory methods in the Collections class and the new of factory methods introduced in the interfaces (Map, List, Set) with JDK 9, if these are invoked with no arguments.

The relevant difference is that the collections returned by the new of factory methods disallow null elements, keys and values (as pointed out in the API documentation in the List, Set and Map interfaces). This might sound irrelvant for empty collections, but even if it is not quite clearly documented, even the accessor methods in the new collection implementations check for null values.

Some examples of the differences:

Collections.emptyList().contains(null) will return false, while List.of().contains(null) will throw a NullPointerException.

Collections.emptyMap().getOrDefault(null, V) will return V, while Map.of().getOrDefault(null, V) will throw a NullPointerException.

As currently implemented in Oracle's JDK 9, at least the following methods on the collections returned by the new factory methods will throw NullPointerExceptions, but behave 'sanely' (as in how the collection classes were originally designed and specified to support null keys and values) using the old factory methods in the Collections class:

  • List.of().contains(null);
  • Set.of().contains(null);
  • Map.of().containsKey(null);
  • Map.of().containsValue(null);
  • Map.of().getOrDefault(null, <any>);
Marc Bannout
  • 388
  • 4
  • 15
jarnbjo
  • 33,923
  • 7
  • 70
  • 94
  • +1 This is good. Maybe, when you draw attention to "as pointed out in the API documentation", specify the section in all the collection types: *"Immutable Static Factory Methods"* where the properties of these instances are explained. – slim Sep 25 '17 at 13:29
  • Also "sanely" is debatable -- NPE is arguably more sane, but not backward-compatible. – slim Sep 25 '17 at 13:29
  • 8
    @slim there is no issue with backward compatibility here. The `X.of()` methods are new, and not intended as direct replacements of the `Collections.emptyX()` methods. Existing code will continue to work as in older Java versions. – Jesper Sep 25 '17 at 13:36
  • 3
    @Jesper it's worth pointing out that you can't go into your old code and globally replace `Collections.emptySet()` with `Set.of()` without expecting a change in behaviour. Especially as several people (including me) wrongly gave an answer suggesting you could do just that. – slim Sep 25 '17 at 13:39
  • 1
    @Jesper My intuitive understanding of the new `of` factory methods would be that they are not introducing new behaviour or restrictions to the collections. One may dispute if it was correct for the original collections API to support null values and keys, but since it was done that way, I find it both unintuitive and surprising that new factory methods create collections, which do not support null. – jarnbjo Sep 25 '17 at 13:40
  • 5
    That behavior is not "new". Map and Set are interfaces. Interfaces permit different implementations. Some implementations support null values, others do not. E.g. ConcurrentSkipListMap would also throw NPE on `containsKey(null)`. The interface documentation says as much. – the8472 Sep 25 '17 at 14:45
  • 1
    @the8472 well yes; but even the jdk devs find collections that support nulls shortcomings of the early implementations. – Eugene Sep 25 '17 at 20:00