The primary design goal of the List.of
, Set.of
, Map.of
, and Map.ofEntries
static factory methods is to enable programmers to create these collections by listing the elements explicitly in the source code. Naturally there is a bias toward a small number of elements or entries, because they're more common, but the relevant characteristic here is that the elements are listed out in the source code.
What should the behavior be if duplicate elements are provided to Set.of
or duplicate keys provided to Map.of
or Map.ofEntries
? Assuming that the elements are listed explicitly in the source code, this is likely a programming error. Alternatives such as first-wins or last-wins seem likely to cover up errors silently, so we decided that making duplicates be an error was the best course of action. If the elements are explicitly listed, it would be nice if this were a compile-time error. However, the detection of duplicates isn't detected until runtime,* so throwing an exception at that time is the best we could do.
* In the future, if all the arguments are constant expressions or are constant-foldable, the Set or Map creation could also be evaluated at compile time and also constant-folded. This might enable duplicates to be detected at compile time.
What about the use case where you have a collection of elements and you want to deduplicate them? That's a different use case, and it's not well handled by Set.of
and Map.ofEntries
. You have to create an intermediate array first, which is quite cumbersome:
Set<String> set = Set.of(list.toArray());
This doesn't compile, because list.toArray()
returns an Object[]
. This will produce a Set<Object>
which can't be assigned to Set<String>
. You want toArray
to give you a String[]
instead:
Set<String> set = Set.of(list.toArray(new String[0]));
This typechecks, but it still throws an exception for duplicates! Another alternative was suggested:
Set<String> set = new HashSet<>(list);
This works, but you get back a HashSet
, which is mutable, and which takes up a lot more space than the set returned from Set.of
. You could deduplicate the elements through a HashSet
, get an array from that, and then pass it to Set.of
. That would work, but bleah.
Fortunately, this is fixed in Java 10. You can now write:
Set<String> set = Set.copyOf(list);
This creates an unmodifiable set from the elements of the source collection, and duplicates do not throw an exception. Instead, an arbitrary one of the duplicates is used. There are similar methods List.copyOf
and Map.copyOf
. As a bonus, these methods skip creating a copy if the source collection is already an unmodifiable collection of the right type.