RFR(m): 8139233 add initial compact immutable collection implementations
Stuart Marks
stuart.marks at oracle.com
Thu May 5 20:56:40 UTC 2016
On 5/5/16 4:34 AM, forax at univ-mlv.fr wrote:
> The problem is that AbstractList is a public class, removing it from the hierarchy is not a backward compatible change,
> so it's not something that can be changed as an after through.
(and AbstractSet and AbstractMap)
I disagree that removing those as superclasses would be backward incompatible,
because it's not specified anywhere that they're superclasses. The usual problem
comes from serialization, which exposes the superclass chain as well as
apparently private classes; the serialization proxy stuff should avoid these
problems.
If somebody were to write
if (Set.of(...) instanceof AbstractSet)
the behavior of their code would change, but I'm not worried about that.
>>> - 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.
It turns out that this works well for ListN, but not SetN or MapN.
307 SetN(E... input) {
308 Objects.requireNonNull(input);
309
310 size = input.length;
311 elements = (E[])new Object[(int)Math.ceil(EXPAND_FACTOR *
input.length)];
312 for (int i = 0; i < input.length; i++) {
313 E e = Objects.requireNonNull(input[i]);
314 int idx = probe(e);
315 if (idx >= 0) {
316 throw new IllegalArgumentException("duplicate element:
" + e);
317 } else {
318 elements[-(idx + 1)] = e;
319 }
320 }
321 }
The problem is that, during construction, probe() needs to see the elements
array and the actual elements as they're inserted. Hoisting the array into a
local variable and assigning elements only at the end causes probe() to NPE.
I've set aside this change for SetN and MapN.
>>> - 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.
I started down this path. It was kind-of OK for Set2 and SetN. For MapN, it
started to get hairy. Some of the Map's state needs to be passed to its entry
set, and then it needs to be passed along to the entry set iterator. I started
to hoist these into static nested classes, but the required new fields and
constructors to initialize them started to clutter things up.
We can revisit this if you really think this is an optimization, but for now
I've set this aside.
We can also revisit the restructuring of the iterator to advance the index in
the constructor and in next().
s'marks
More information about the core-libs-dev
mailing list