On 5/4/16 1:38 AM, Remi Forax wrote:
nice work !
Spoken like a true university professor: "nice work" followed by 100 lines of
criticism. :-)
first, all classes should be final otherwise, they are not truly immutable and
all arrays should be marked as @Stable.
OK, I'll make the classes final.
On @Stable, I had a chat with Paul Sandoz about this, and he suggested holding
back from adding this. Its semantics are quite particular and are subject to
change, and widespread use could easily turn into misuse.
List0, List1,
why not using Collections.emptyList(), Collections.singletonList() here ?
Serialization compatibility with future versions. I want all of these to
serialize to the same serial proxy in JDK 9, so that in JDK 9+ they can be
deserialized to whatever equivalent implementation classes exist in that version
of the JDK. For example, there might be a single field-based implementation that
covers a small range of sizes, or an implementation using value types.
ListN:
- i don't think that extending AbstractList is a good idea here,
AbstractList provide the failfast/modCount mecanism not needed here.
Moreover, i think you should have some overriden methods on par with
Arrays.asList()
(i.e. a custom isEmpty, toArray, indexOf, contains, iterator, spliterator
and forEach)
otherwise people will use Arrays.asList just because it's faster :(
The implementations all extend AbstractList/Set/Map mainly for implementation
convenience. This way it makes it easy to add and remove implementations
tailored for specific sizes, since so much code is shared with the abstract
superclasses. But they do come with some baggage, as you point out. As the set
of implementation classes settles down, it would make more sense to override
more methods, and refactor to handle sharing, at which point maybe the
dependence on the abstract superclasses can be severed.
- in the constructor, you should use a local variable here,
@SafeVarargs
ListN(E... input) {
// copy and check manually to avoid TOCTOU
@SuppressWarnings("unchecked")
E[] elements = (E[])new Object[input.length]; // implicit nullcheck
of input
for (int i = 0; i < input.length; i++) {
elements[i] = Objects.requireNonNull(input[i]);
}
this.elements = elements;
}
Nice. Will fix.
List2:
- same as for ListN, should not inherits from AbstractList.
- i wonder why iterator() is not overriden like with Set2.
Bringing up a List implementation with AbstractList requires only overriding
get() and size(), but not iterator(). On the other hand, bringing up a Set
implementation using AbstractSet requires overriding only iterator() and size().
Set2:
- again, here, i don't think that inheriting from AbstractSet is a good idea.
- in the iterator, pos (position ?) should be private and next() can be
written without the '+=',
public E next() {
if (pos == 0) {
pos = 1;
return e0;
} else if (pos == 1) {
pos = 2;
return e1;
} else {
throw new NoSuchElementException();
}
}
- the iterator should not be defined as inner class (with a reference to
Set2) but
be defined as a static class that contains a copy of e1 and e2,
this will save an indirection and avoid to keep a link on the Set if it is
transient.
Good fixes, thanks. Some of this is stuff left over from when there were
field-based implementations with larger sizes.
SetN:
- in the constructor, use a local variable like for ListN
- the @SuppressWarnings for contains is wrong, probe should take an Object,
not an E.
Ah, nice cleanup.
- the iterator should not be an inner class like Set2 and take the array as
parameter.
idx should be private. Instead of doing the loop in hasNext and next, the
loop should
be done once in the constructor and in next. So the code of hasNext will be
simple
and the JIT will be able to fold a call to hasNext() followed by a call to
next().
OK, I'll look at this.
- i don't understand how the serialization can work given that the SALT may
be different between two VMs
The salt is XORed with the element's hash to determine the position in the
collection's array. Serializing it writes all the objects in the array from left
to right, but we don't actually care what the order is. Upon deserialization,
the objects' hash values are XORed with the deserializing JVM's salt and
(probably) end up in different positions in the array of the newly deserialized
collection. But again we don't care where they end up.
Same goes for MapN's keys and values.
MapN:
- see SetN :)
:-)
SerialProxy:
- I believe the class SerialProxy should be public, no ?
No, the proxy object can and usually should be private. What's missing is a doc
comment specifying the serial format. This should have an "@serial include" tag,
which will cause the doc comment to end up in the serialized-form.html file.
This works even if the proxy class itself is private.
This work is tracked by JDK-8133977.
- The constructor should take a Object... to simplify all calls in the
different writeReplace.
Good idea.
- fields flags and array should be private
OK.
Thanks for all the comments! I'll work on an updated webrev and post it soon.
s'marks