Request for review : 7121314 : Behavior mismatch between AbstractCollection.toArray(T[] ) and its spec

Ulf Zibis Ulf.Zibis at gmx.de
Thu Mar 15 10:27:01 UTC 2012


Thanks for your comments too.

Am 14.03.2012 05:23, schrieb Sean Chou:
> Thanks for your comments, I have different opinions.
>
> About performance, I would like to say "this part of code is not in a path which causes 
> performance problem". In fact it should rarely execute, so there is no need to catch this little 
> optimization, and readability is more important.
Yes, I agree, but my 2nd motivation was to reduce the bytecode footprint a little, especially for 
such rarely executed code.

> With the if statement, it reads "put a null after the elements if there are more space", while 
> with your code, it reads "copy all the elements from r or copy all elements and 1 more from r if 
> there are more space" and we have to think "what's the next element in r ? ". In fact, we need 
> look back to find how r is defined "T[] r = a.length >= size ? a 
> : (T[])java.lang.reflect.Array.newInstance(a.getClass().getComponentType(), size);" and go through 
> the code once more to realize there is a null at that position.
Yes, a better comment would be necessary.

>
> And with JIT, will a.length and i be dereference/push/pop so many times or is it kept in cache or 
> in a register ? I believe it is better to forget the little possible enhancement here, which is 
> also encouraged by java. I'm sorry the page of bug 7153238 
> <http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7153238>  says not available to me.
Please wait 1 more day, then the content should become publicly visible. I'm waiting too, as I have 
an additional comment in queue to correct a little error.

> About the testcase, variable map and map2 are of different types, can not be reused.
Oops, yes.
But you could reuse it (like Object[] res) with:
Map<String,Object> map = new TConcurrentHashMap<>(); // better: TCHM1
...
map = new TConcurrentHashMap2<>();
I agree, a little nit, but I had the other "missing" test cases in mind, where the numbering then 
could become confusing.

> I would not like to add "// inherits from AbstractCollection.toArray()" , it is obvious and listed 
> in java doc.
In ConcurrentHashMap.values:
     Overrides: values in class AbstractMap<K,V>
In AbstractMap.values:
     This implementation returns a collection that subclasses AbstractCollection.
But there is no guarantee, that the returned collection overrides or just delegates to 
AbstractCollection.toArray().
In worst case, t.j.u.AC.ToArray doesn't test j.u. AbstractCollection.toArray() at all.

> About "// simulate concurrent decrease of map's elements", it is not enough to describe clearly 
> why it can do that. People have to refer to the bug for more knowledge. So in my opinion, it 
> changes nothing. However, I can add it if you have a strong request.
IMO would be a help for people, later reviewing the test case.

> And about the cases the testcase doesn't cover.
> " if (a == r)
>    if (a.length < i)
>    it.hasNext() ? finishToArray(r, it) : r  (neither yes nor no)
> "
> This testcase is designed to check if the returned array is the given array. In the above 2 cases, 
> there is no need to do the check.
> if (a == r), of cause it is;  if (a.length < i), of cause it is not. This 2 cases will fail due to 
> more serious bug, not 7121314 .
My complain was in assumption, that a test, named t.j.u.AC.ToArray, should test the whole complexity 
of method toArray(), not just concerning a single bug.

-Ulf


>
>
> On Wed, Mar 14, 2012 at 7:36 AM, Ulf Zibis <Ulf.Zibis at gmx.de <mailto:Ulf.Zibis at gmx.de>> wrote:
>
>     Am 13.03.2012 07:58, schrieb Sean Chou:
>
>         Hi Ulf and David,
>
>            I modified the patch and added the testcase, it's now :
>         http://cr.openjdk.java.net/~zhouyx/7121314/webrev.02/
>         <http://cr.openjdk.java.net/%7Ezhouyx/7121314/webrev.02/>
>         <http://cr.openjdk.java.net/%7Ezhouyx/7121314/webrev.02/>    .
>
>
>            Ulf's compact version is used, it looks beautiful;
>
>     Thanks!
>
>
>         however I replaced the Math.min part with if statement because if statement is more
>         intuitive and I don't think there is any performance concern. But it is not so compact now...
>
>     My performance thoughts:
>     Your version:
>        } else if (a.length < i) {   // dereferences a.length
>            ...
>        } else {
>            // push original i to stack
>            System.arraycopy(r, 0, a, 0, i);   // references array elements, uses some CPU registers
>            if (a.length > i) {   // dereferences a.length again, pop i from stack
>                a[i] = null;   // null-termination  // references array elements again
>
>     better:
>        } else if (a.length < i) {   // dereferences a.length
>            ...
>        } else {
>            if (a.length > i) {   // reuses a.length result + i from above
>                i++;   // ensure null-termination  // cheap operation
>            System.arraycopy(r, 0, a, 0, i);   // references array elements, original i must not be
>     remembered
>
>     compact:
>        } else if (a.length < i) {
>            ...
>        } else {
>            System.arraycopy(r, 0, a, 0, a.length > i ? ++i : i); // ensure null-termination
>
>     Note: I first used Math.min() as it should be intrinsified by JIT, but above seems faster again.
>
>     Comment maybe more intuitive/clear:
>      197         return it.hasNext() ? // more elements than expected
>      198                 finishToArray(r, it) : r;
>
>     Please additionally note my alternative comment at
>     bug 7153238 <http://bugs.sun.com/bugdatabase/view_bug.do?bug_id=7153238> - Use smaller more
>     optimistic array size increase for AbstractCollection.toArray()
>
>
>            Also I added the equal size case and @author to testcase.
>
>     You can reuse variable map instead map2, otherwise maybe confusing why saving map from 1st
>     testcase.
>
>     Add comments:
>      44             remove(keys[0]); // simulate concurrent decrease of map's elements
>      54             remove(keys[0]); // simulate concurrent decrease of map's elements
>      67         Object[] res = map.values().toArray(a); // inherits from AbstractCollection.toArray()
>      77         res = map2.values().toArray(a); // inherits from AbstractCollection.toArray()
>
>     Your test does not cover cases:
>        if (a == r)
>        if (a.length < i)
>        it.hasNext() ? finishToArray(r, it) : r  (neither yes nor no)
>
>
>
>            There is a little problem when I created the webrev, I don't know how to change the
>         "contributed-by" information for the testcase, so the list is still Ulf's and my emails.
>
>     Thanks listing me :-)
>
>     -Ulf
>
>
>
>
> -- 
> Best Regards,
> Sean Chou
>



More information about the core-libs-dev mailing list