-
Bug
-
Resolution: Not an Issue
-
P4
-
None
-
5.0
-
x86
-
windows_xp
FULL PRODUCT VERSION :
java version "1.5.0_04"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_04-b05)
Java HotSpot(TM) Client VM (build 1.5.0_04-b05, mixed mode, sharing)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]
EXTRA RELEVANT SYSTEM CONFIGURATION :
Not applicable. This isn't an OS issue, it's a Java issue.
A DESCRIPTION OF THE PROBLEM :
Note: This might technically be a Request for Enhancement because I'm requesting an additional method on java.util.List. I consider it a bug because the code in Collections.copy() is wrong, however there is no "right" way to write it without the additional method.
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?
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run this application:
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);
}
}
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
list2 contains the same elements as 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.
ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "main" java.lang.IndexOutOfBoundsException: Source does not fit in dest
at java.util.Collections.copy(Unknown Source)
at foo.bar.baz.CollectionsDotCopy.main(CollectionsDotCopy.java:8)
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
I'm just writing this off the top of my head so if it doesn't compile, sorry.
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).
java version "1.5.0_04"
Java(TM) 2 Runtime Environment, Standard Edition (build 1.5.0_04-b05)
Java HotSpot(TM) Client VM (build 1.5.0_04-b05, mixed mode, sharing)
ADDITIONAL OS VERSION INFORMATION :
Microsoft Windows XP [Version 5.1.2600]
EXTRA RELEVANT SYSTEM CONFIGURATION :
Not applicable. This isn't an OS issue, it's a Java issue.
A DESCRIPTION OF THE PROBLEM :
Note: This might technically be a Request for Enhancement because I'm requesting an additional method on java.util.List. I consider it a bug because the code in Collections.copy() is wrong, however there is no "right" way to write it without the additional method.
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?
STEPS TO FOLLOW TO REPRODUCE THE PROBLEM :
Run this application:
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);
}
}
EXPECTED VERSUS ACTUAL BEHAVIOR :
EXPECTED -
list2 contains the same elements as 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.
ERROR MESSAGES/STACK TRACES THAT OCCUR :
Exception in thread "main" java.lang.IndexOutOfBoundsException: Source does not fit in dest
at java.util.Collections.copy(Unknown Source)
at foo.bar.baz.CollectionsDotCopy.main(CollectionsDotCopy.java:8)
REPRODUCIBILITY :
This bug can be reproduced always.
---------- BEGIN SOURCE ----------
I'm just writing this off the top of my head so if it doesn't compile, sorry.
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).