26

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?

Steve P.
  • 13,674
  • 6
  • 38
  • 67
Vendetta
  • 13,178
  • 19
  • 71
  • 111
  • 6
    If you want an array, use an array. The point of `ArrayList` is to enable you to use the higher-level operations such as `add`. – Tom G Jun 25 '13 at 15:30
  • 4
    I think it might be giving you an `IndexOutOfBoundsException` because `set` does the following: `Replaces the element at the specified position in this list with the specified element`. Since there is no element at index 0 to replace, an exception is thrown. Try using `add` and seeing if the size is modified past your indicated capacity. If this is not what you are looking for, I suggest the answer given by @jh314 – Alex Brooks Jun 25 '13 at 15:33
  • What would be a rationale behind such use case? I think you're trying to find out how to misuse the pattern rather than try to understand it. – Danubian Sailor Jun 25 '13 at 15:43
  • 1
    @AlexBrooks, see my answer, you're correct, but not because there's no element. – Steve P. Jun 25 '13 at 15:45
  • 1
    @SteveP. Makes perfect sense. Thanks for the clarification. – Alex Brooks Jun 25 '13 at 16:09
  • @AlexBrooks No problem. – Steve P. Jun 25 '13 at 16:28
  • 1
    This is a common design(it's the same in python too): `set` methods do *not* increase the size of a sequence/collection. If you want to increase the size then you want to `add` something. – Bakuriu Jun 25 '13 at 17:53

8 Answers8

24

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.

jh314
  • 24,533
  • 14
  • 58
  • 79
14

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.

Steve P.
  • 13,674
  • 6
  • 38
  • 67
  • 1
    While now I finally understand how it works, I still think this designer's choice is stupid and counter-intuitive. – Vendetta Jun 25 '13 at 18:21
  • 3
    @CodeNoob No problem. I agree that the term *capacity* is misleading, but I think that it actually makes sense for it to be this way. `size` keeps track of how many elements are actually in the array, so it makes sense for `size` to be 0 right after initialization, and with regards to `set/add`, as Bakuriu said: *"This is a common design (it's the same in python too): set methods do not increase the size of a sequence/collection. If you want to increase the size then you want to add something."* – Steve P. Jun 25 '13 at 18:38
  • 4
    It makes more sense if you consider that ArrayList implements the List interface. A List has no concept similar to "Initial Capacity". A list grows by adding values to it. – Buhb Jun 25 '13 at 19:25
3

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.

Martin Milan
  • 6,170
  • 2
  • 30
  • 43
2

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!

Dariusz
  • 19,750
  • 7
  • 65
  • 104
1

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]
sp00m
  • 44,266
  • 23
  • 127
  • 230
1

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.

Fabricio Araujo
  • 3,868
  • 1
  • 25
  • 40
0

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

Sami Korhonen
  • 1,246
  • 8
  • 17
0

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.

Eric Jablow
  • 7,611
  • 2
  • 20
  • 29