CuriousMind
CuriousMind

Reputation: 15808

Java ArrayList IndexOutOfBoundsException despite giving an initial capacity

When I do

ArrayList<Integer> arr = new ArrayList<Integer>(10);
arr.set(0, 1);

Java gives me

Exception in thread "main" java.lang.IndexOutOfBoundsException: Index: 0, Size: 0
    at java.util.ArrayList.rangeCheck(Unknown Source)
    at java.util.ArrayList.set(Unknown Source)
    at HelloWorld.main(HelloWorld.java:13)

Is there an easy way I can pre-reserve the size of ArrayList and then use the indices immediately, just like arrays?

Upvotes: 27

Views: 7757

Answers (8)

Steve P.
Steve P.

Reputation: 14699

Here's the source from ArrayList:

The constructor:

public ArrayList(int initialCapacity) 
{
     super();

     if (initialCapacity < 0)
          throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
     this.elementData = new Object[initialCapacity];
}

You called set(int, E):

public E set(int index, E element) 
{
     rangeCheck(index);  
     E oldValue = elementData(index);
     elementData[index] = element;
     return oldValue;
}

Set calls rangeCheck(int):

private void rangeCheck(int index) 
{
    if (index >= size) {
         throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }
}

It may be subtle, but when you called the constructor, despite initializing an Object[], you did not initialize size. Hence, from rangeCheck, you get the IndexOutOfBoundsException, since size is 0. Instead of using set(int, E), you can use add(E e) (adds e of type E to the end of the list, in your case: add(1)) and this won't occur. Or, if it suits you, you could initialize all elements to 0 as suggested in another answer.

Upvotes: 15

Dariusz
Dariusz

Reputation: 22251

Programming aside, what you are trying to do here is illogical.

Imagine an empty egg carton with space for ten eggs. That is more or less what you have created. Then you tell a super-precise-and-annoying-which-does-exactly-what-you-tell-him robot to replace the 0th egg with another egg. The robot reports an error. Why? He can't replace the 0th egg, because there is no egg there! There is a space reserved for 10 eggs, but there are really no eggs inside!

Upvotes: 2

Eric Jablow
Eric Jablow

Reputation: 7899

Capacity is used to prepare ArrayLists for expansion. Take the loop

List<Integer> list = new ArrayList<>();
for(final int i = 0; i < 1024; ++i) {
    list.add(i);
}

list starts off with a capacity of 10. Therefore it holds a new Integer[10] inside. As the loop adds to the list, the integers are added to that array. When the array is filled and another number is added, a new array is allocated twice the size of the old one, and the old values are copied to the new ones. Adding an item is O(1) at best, and O(N) at worst. But adding N items will take about 2*1024 individual assignments: amortized linear time.

Capacity isn't size. If you haven't added to the array list yet, the size will be zero, and attempting to write into the 3rd element will fail.

Upvotes: 0

Fabricio Araujo
Fabricio Araujo

Reputation: 3820

This is not an Java-specific answer but an data structure answer.

You are confusing the Capacity concept with the Count (or Size) one.

Capacity is when you tell the list to reserve/preallocate a number of slots in advance (in this ArrayList case, you are saying to it create an array of 10 positions) in its' internal storage. When this happens, the list still does not have any items.

Size (or Count) is the quantity of items the list really have. In your code, you really doesn't added any item - so the IndexOutOfBoundException is deserved.

Upvotes: 1

Sami Korhonen
Sami Korhonen

Reputation: 1303

While you can't do what you want with arraylist, there is another option: Arrays.asList()

Upvotes: 0

sp00m
sp00m

Reputation: 48817

You could use arr.add(1), which will add 1 in the first empty cell, i.e. the 0-indexed one.

Or you could create your own list:

public static class PresetArrayList<E> extends ArrayList<E> {

    private static final long serialVersionUID = 1L;

    public PresetArrayList(int initialCapacity) {
        super(initialCapacity);
        addAll(Collections.nCopies(initialCapacity, (E) null));
    }

}

Then:

List<Integer> list = new PresetArrayList<Integer>(5);
list.set(3, 1);
System.out.println(list);

Prints:

[null, null, null, 1, null]

Upvotes: 1

Martin Milan
Martin Milan

Reputation: 6390

I believe the issue here is that although you have suggested the allocated space of entries in the Array, you have not actually created entries.

What does arr.size() return?

I think you need to use the add(T) method instead.

Upvotes: 3

jh314
jh314

Reputation: 27792

How about this:

ArrayList<Integer> arr = new ArrayList<Integer>(Collections.nCopies(10, 0));

This will initialize arr with 10 zero's. Then you can feel free to use the indexes immediately.

Upvotes: 25

Related Questions