4

The release of Java 10 brings new static factory methods, specifically:

Seeing as these methods allow us to copy Collections into different Collection implementations, how do they compare and contrast to existing methods?

Jacob G.
  • 28,856
  • 5
  • 62
  • 116
  • 2
    I don't know why they don't just do what scala did and create an `immutable` package where these implementations will live. I would feel much better knowing that the list some method just returned was a `java.util.immutable.List`, rather than a `java.util.List`... *BUT it may or may not be immutable, you just have to trace the code to find out*. Ugh! – smac89 Mar 26 '18 at 01:23
  • I think this question already has an answer (though this is not an exact duplicate): https://stackoverflow.com/questions/39385658/list-of-or-collections-unmodifiablelist – ZhekaKozlov Mar 30 '18 at 05:51
  • @ZhekaKozlov These are for the new `copyOf` methods added in Java 10, not the `of` methods added in 9, but I see your point. – Jacob G. Mar 30 '18 at 10:34

1 Answers1

8

Just as List#of, Set#of, and Map#ofEntries allow us to create unmodifiable implementations in Java 9, the copyOf methods provide a convenient way to create unmodifiable implementations from existing Collections and Maps (depending on the method, as Map#copyOf accepts a Map) in Java 10.

This allows us to easily create an unmodifiable Set<E> from a List<E> and vice versa.

Though, these methods bring a few caveats (quoting the documentation of java.util.List):

  • They are unmodifiable. Elements cannot be added, removed, or replaced. Calling any mutator method on the List will always cause UnsupportedOperationException to be thrown. However, if the contained elements are themselves mutable, this may cause the List's contents to appear to change.
  • They disallow null elements. Attempts to create them with null elements result in NullPointerException.
  • They are serializable if all elements are serializable.
  • The order of elements in the list is the same as the order of the provided arguments, or of the elements in the provided array.
  • They are value-based. Callers should make no assumptions about the identity of the returned instances. Factories are free to create new instances or reuse existing ones. Therefore, identity-sensitive operations on these instances (reference equality (==), identity hash code, and synchronization) are unreliable and should be avoided.
  • They are serialized as specified on the Serialized Form page.

For the caveats of Set#copyOf and Map#copyOf, refer to their documentation.

Jacob G.
  • 28,856
  • 5
  • 62
  • 116
  • 9
    An important aspect of these methods is that they are idempotent. Copying a previously copied collection does not make another copy. – Brian Goetz Mar 04 '18 at 17:43
  • 1
    @BrianGoetz does that also imply not making a copy, if the input is already an immutable collection like, e.g. created via `List.of(…)`? – Holger Mar 05 '18 at 07:56
  • 2
    @Holger yes, the implementation contains: `if (coll instanceof ImmutableCollections.AbstractImmutableList) { return (List)coll;` – Eugene Mar 05 '18 at 08:09
  • 2
    @Eugene I supposed that, but would like to know, whether it’s part of the contract… – Holger Mar 05 '18 at 08:18
  • @Holger exactly my thought btw. guava `copyOf` states that it only copies when needed, so I was sort of expecting this one here too. – Eugene Mar 05 '18 at 08:19
  • 1
    @Holger Formally, the idempotent copy isn't part of the contract; that clause is in an "implementation note." However, for practical purposes we want people to be able to call `copyOf` without worrying about whether it makes redundant copies. The reason it's not part of the contract is that the unmodifiable collections are "value-based classes" (see link from the "Unmodifiable List" section) which discourage identity-based operations. Today, however, the only way assert "doesn't make a copy" is with an identity comparison, which we're trying to avoid. – Stuart Marks Mar 05 '18 at 17:06
  • 1
    @Eugene See above. – Stuart Marks Mar 05 '18 at 17:07
  • @StuartMarks the identity based comparison tells you whether it returns the same instance, which doesn’t need to be guaranteed. “Doesn’t make a copy” should be read as “doesn’t bear an `O(n)` (or worse) memory overhead”, rather than “will return the same instance” (which it practically does today). So in a future version, it could return a different already existing instance, which would still be within “does not make a new copy” and “value based/has an unspecified identity”… – Holger Mar 05 '18 at 17:15
  • 1
    @Holger Sure, one could write a spec that attempts to allow returning an *existing* copy but disallowing returning a *new* copy, but then that'd require defining "existing" and "new"... without resorting to any identity semantics. I suppose it could have been done, but it didn't seem worth it; a simple implementation note seemed sufficient. – Stuart Marks Mar 05 '18 at 17:59
  • 1
    @StuartMarks this was not meant as a suggestion for a specification but an example of what an implementation could do within the spec when the spec keeps the already existing *value based* statement and only adds a “no copy” guaranty whereas “copy” is defined as in my previous comment, not in terms of object identity but memory complexity. – Holger Mar 05 '18 at 19:28