Hi Peter,
Great comments and questions. Responses below....
On 5/4/16 12:20 AM, Peter Levart wrote:
- What's the point of 11 overloaded List.of() methods for 0 ... 10-arity if you
then invoke the var-args ListN(E...input) constructor that does the array
cloning with 8 of them? The same for Set.of()... I suggest that you don't clone
the array in ListN(E... input) constructor but only in List.of(E ... input)
method.
Yes, it is silly to have fixed-arg overloads to provide a fast path, and then to
turn around and use them in a varargs context that creates an array and then
copies them, after which the constructor clones the array. The main point,
though, is that the API allows for a fast path. Even if the current
implementation doesn't take advantage of it, it can be updated later without
changing the API.
There's tension here between avoiding copying and central enforcement of all the
invariants. I'll probably need to do something like adding non-cloning
constructors and possibly fixed-arg constructor overloads to avoid varargs array
creation. This is a bit more than I want to do at the moment, but I agree it's
important.
I've filed JDK-8156071 to track this.
- {Set0, Set1, Set2}.contains(null) return false, but SetN.contains(null) throws
NPE. All List{0,1,2,N}.contains(null) return false. What should be the correct
behavior? ConcurrentHashMap.keySet().contains(null) throws NPE for example.
Good catch. I did some investigation, and the results are interesting. All of
the List implementations allow nulls. But the all the Queue and Deque
implementations all disallow nulls, and they all return false from contains(null):
- ArrayBlockingQueue
- ArrayDeque
- ConcurrentLinkedDeque
- ConcurrentLinkedQueue
- LinkedBlockingDeque
- LinkedBlockingQueue
- LinkedTransferQueue
- PriorityBlockingQueue
- PriorityQueue
The following Set implementations disallow nulls, and they throw NPE from
contains(null):
- ConcurrentHashMap$KeySetView
- ConcurrentSkipListSet
The following Map implementations disallow null keys, and they throw NPE from
containsKey(null) and containsValue(null):
- ConcurrentHashMap
- ConcurrentSkipListMap
- Hashtable
Things seem pretty consistent across the null-unfriendly collections: on Deques
and Queues, contains(null) is allowed, but on Sets and Maps, contains(null)
throws NPE. (I have no idea why things are this way.)
Although there are no existing Lists that disallow nulls, based on the Queue and
Deque behavior, the current behavior of contains(null) on the new List
implementations seems reasonable.
I will update/override the various contains() methods on the new Set and Map
implementations to adjust them to throw NPE.
This area could use more tests, too. I've filed JDK-8156074 to track this.
- SetN.probe(E pe) and MapN.probe(K pk) invoke ee.equals(pe) and ek.equals(pk)
respectively. Although it should be logically equivalent, it has been a practice
in collections to invoke the .equals() method on the passed-in argument rather
than on individual elements of the collection.
Huh, I didn't know that. Good catch. Will fix.
- Should unchecked exceptions from constructors be wrapped with
InvalidObjectException in SerialProxy.readResolve() or does the Serialization
infrastructure already perform the equivalent?
Another good question! The serialization mechanism does deal with any exception
that's thrown here, since readResolve is invoked reflectively. But basically it
just rethrows runtime exceptions. Callers are presumably prepared to catch
ObjectStreamExceptions, so wrapping exceptions thrown from the constructors in a
suitable exception instance (probably InvalidObjectException) seems like a
sensible thing to do.
Thanks,
s'marks