United StatesChange Country, Oracle Worldwide Web Sites Communities I am a... I want to...
Bug ID: JDK-6350752 Collections.copy() should check List capacity not size, need capacity() method
JDK-6350752 : Collections.copy() should check List capacity not size, need capacity() method

Details
Type:
Enhancement
Submit Date:
2005-11-15
Status:
Closed
Updated Date:
2010-04-02
Project Name:
JDK
Resolved Date:
2005-11-16
Component:
core-libs
OS:
windows_xp
Sub-Component:
java.util
CPU:
x86
Priority:
P4
Resolution:
Not an Issue
Affected Versions:
5.0
Fixed Versions:

Related Reports

Sub Tasks

Description
A DESCRIPTION OF THE REQUEST :
I believe java.util.List (or, perhaps java.util.Collection) should have a capacity() method. I don't care if it's publicly accessible or not, but Collections.copy() needs it to execute properly. See the justification below.


JUSTIFICATION :
Collections.copy() has this guard clause at the top:

        int srcSize = src.size();
        if (srcSize > dest.size())
            throw new IndexOutOfBoundsException("Source does not fit in dest");

This is broken. What it's attempting to discover is whether the destination List can hold all of the source List's elements. size() is not the proper method to determine this. Unfortunately, there is no capacity() method on List, so the author used size(), the next best thing.

What is needed is a capacity() method. I understand the desire to hide a List's capacity behind a layer of abstraction (though I think it might be useful in other cases), but this could be accomplished fairly well by making its accessor package-scope.

I know there are workarounds. I listed two below, but I can't help but think that the semantics of the Collections.copy() method are broken if it does not behave as I was expecting it to. Wouldn't you expect Collections.copy(destList, sourceList) to copy sourceList into destList?


EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
After running the following application, I believe list2 should have the same contents as list1

import java.util.*;
public class StackTest {
  public static void main(String[] args) {
    List<Integer> list1 = new ArrayList<Integer>();
    list1.add(2); list1.add(3); list1.add(4); list1.add(5);
    
    List<Integer> list2 = new ArrayList<Integer>(list1.size());
    Collections.copy(list2, list1);
  }
}

ACTUAL -
An error is thrown because list2.size() returns 0. As explained above, this is because Collections.copy() executes this check:

        int srcSize = src.size();
        if (srcSize > dest.size())
            throw new IndexOutOfBoundsException("Source does not fit in dest");

Instead of:

        int srcSize = src.size();
        if (srcSize > dest.capacity())
            throw new IndexOutOfBoundsException("Source does not fit in dest");

Granted, capacity() doesn't exist. But I think it should.


---------- BEGIN SOURCE ----------
import junit.*;
public class CollectionsDotCopyTest {
    public void testCollectionsDotCopy() {
        List<Integer> list1 = new ArrayList<Integer>();
        list1.add(2); list1.add(3); list1.add(4); list1.add(5);
    
        List<Integer> list2 = new ArrayList<Integer>(list1.size());
        try {
            Collections.copy(list2, list1);
        }
        catch(IndexOutOfBoundsException e) {
            fail();
        }
    }
}
---------- END SOURCE ----------

CUSTOMER SUBMITTED WORKAROUND :
        List<Integer> list2 = new ArrayList<Integer>(list1);

or

        Integer dummyInt = new Integer();
        for(int i = 0; i < list1.size(); i++) list2.insert(dummyInt);
        Collections.copy(list2, list);

(Note: I would never write the second example, but I think it's a good illustration of how strange the check in Collections.copy() is).

                                    

Comments
EVALUATION

The semantics of Collections.copy cannot be changed.
One could argue that copy would have been better designed if
- it allowed a destination smaller than the source
- the order of the parameters was reversed.

This is completely independent of whether List has a capacity() method.
Note that there are List implementations not based on an internal array,
such as LinkedList and nCopies.

A classic mistake in the early design of Java was to expose too many 
implementation details.  Adding capacity() would be repeating that mistake.

I judge this Not A Defect.  Doug Lea agrees.
                                     
2005-11-16



Hardware and Software, Engineered to Work Together