-2

Today I was looking in cloning an ArrayList when I ran in a weird problem. I read this answer and tried that. However, I received an ArrayOutOfBounds exception. So I looked a bit more into it, and appearently the ArrayList<>(int size) is not working?
Is this a known problem?

Test class:

import java.util.ArrayList;
import java.util.List;

import org.junit.Test;

public class ArrayListTest {
    @Test
    public void test() {
        List<String> lista = new ArrayList<>();
        lista.add("a");
        List<String> listb = new ArrayList<>(lista.size());
        System.out.println("Size of lista: " + lista.size());
        System.out.println("Size of listb: " + listb.size());
    }
}

Troubled class:

import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

import org.junit.Test;

public class ArrayListTest2 {
    @Test
    public void test() {
        List<String> lista = new ArrayList<>();
        lista.add("a");
        List<String> listb = new ArrayList<>(lista.size());
        Collections.copy(listb, lista);
        System.out.println("Lista: " + lista);
        System.out.println("Listb: " + listb);
    }
}

Which results in:

java.lang.IndexOutOfBoundsException: Source does not fit in dest
at java.util.Collections.copy(Collections.java:556)
at ArrayListTest2.test(ArrayListTest2.java:13)

Any ideas?

Community
  • 1
  • 1
SLG
  • 75
  • 1
  • 1
  • 10
  • 5
    You know, if a so widely used method for 10 years was so broken that it simply doesn't work, don't you think people would have found it by now? What I mean by that is that you're not looking for a bug in Java. You should first be looking at a bug in _your_ code. Read the documentation again. Especially the constructor `new ArrayList(size)`. See that it allocates memory, it does not insert element. The size is still 0 after that. – Tunaki Mar 25 '16 at 15:54
  • Okay, I understand that. But why is the copy not working? And of course I'm not assuming I found a bug in Java. But I was just curious if I did indeed make a wrong assumption, or that something else was going on. – SLG Mar 25 '16 at 15:58
  • 1
    Again, did you read the documentation of [`copy`](https://docs.oracle.com/javase/8/docs/api/java/util/Collections.html#copy-java.util.List-java.util.List-)? There's a part that says *The destination list must be at least as long as the source list* – Tunaki Mar 25 '16 at 16:00
  • I think downvotes are unwarranted. OP was probably misled by poor documentation. See [my answer](http://stackoverflow.com/a/36224959/1441122). – Stuart Marks Mar 25 '16 at 17:41
  • @StuartMarks, still, he could have simply read the JDK source, that would have made it obvious. And it's not a complex method, so understanding what it does isn't really a tall task either. – the8472 Mar 25 '16 at 17:45
  • 3
    @the8472 Tracking down the JDK source is IMHO asking too much of SO questioners. The fact is that we've received bug reports on `Collections.copy()` from people who were confused about exactly the same thing. That's why we fixed the documentation. – Stuart Marks Mar 25 '16 at 17:49
  • @StuartMarks, tracking down? It opens in my IDE automatically. Improved documentation is good of course, but I find just looking at the source often helps when ambiguities arise. – the8472 Mar 25 '16 at 18:46
  • 1
    Not only is it unreasonable to expect people to read the source of Java SE classes, doing so is the exact opposite of object-oriented development. One should be going by a class's contract, not its implementation. – VGR Mar 25 '16 at 19:13
  • @VGR, I'm not saying you should read the code *first*. To me it's just the next logical step when I'm uncertain about the documentation. And I'm not sure what the particular programming style has to do with this. Other styles have a spec-implementation divide too and there looking at the implementation is not any more or less enlightening. You shouldn't *rely* on specifics on the implementation, reading it will often be *informative* anyway. Sometimes Specese can be obtuse. Really, I don't see how it's "unreasonable". – the8472 Mar 25 '16 at 20:04
  • Just FYI SLG, to copy lista you can just do `List listb = new ArrayList<>(lista);` – MikeFHay May 15 '16 at 12:06

3 Answers3

4

This is really a problem with the documentation of Collections.copy(). Its JDK 8 documentation says:

The destination list must be at least as long as the source list. If it is longer, the remaining elements in the destination list are unaffected.

This uses the terms "long" and "longer" which seem to refer to the length of a list. However, if you look at the List specification, there is no definition of length -- there are however definitions for size and, in ArrayList, for capacity. It's unclear whether Collections.copy() is defined in terms of the size or capacity of the lists.

In fact, Collections.copy() should be defined in terms the size of the lists. The JDK 9 specification of Collections.copy() has been modified to read:

The destination list's size must be greater than or equal to the source list's size. If it is greater, the remaining elements in the destination list are unaffected.

The arg to the ArrayList constructor sets its initial capacity, not its size. The size is the number of elements that are currently present in the list. Collections.copy() requires the destination list's size to be greater or euqal to the source's size. That's why you get IndexOutOfBoundsException when you try to copy a list of size 1 into a list of size 0.

Stuart Marks
  • 127,867
  • 37
  • 205
  • 259
0

If the target List is too small.I will throw IndexOutOfBoundsException.

import java.util.*;

public class CollectionsDemo {
   public static void main(String args[]) {
      // create two lists    
      List<String> srclst = new ArrayList<String>(5);
      List<String> destlst = new ArrayList<String>(10);

      // populate two lists
      srclst.add("a");
      srclst.add("b");
      srclst.add("d");

      destlst.add("e");
      destlst.add("f");
      destlst.add("g");


      // copy into dest list
      Collections.copy(destlst, srclst);            

      System.out.println("Value of source list: "+srclst);
      System.out.println("Value of destination list: "+destlst);
   }    
TanLingxiao
  • 402
  • 5
  • 7
0

I think there is a problem with your understanding of the initialCapacity. Like when you initialize an array of primitive that gets filled with 'null' values, nothing as such happens during the initialization of an ArrayList. By setting an initialCapacity of n, all you are doing is letting the compiler know that it should allocate enough memory for n objects. Please note, that even though memory has been allocated, the size of the list is still 0 since there are no elements inside it.

About the copy(). You are trying to copy lista into a list with an initial capacity of lista.size() = 0. Doesn't something seem amiss there?

This confusion may be warranted to the fact that the documentation simply doesn't mention the word size, yet that is the key factor here.

Debosmit Ray
  • 5,228
  • 2
  • 27
  • 43