Java ArrayList IndexOutOfBoundsException despite g

2020-03-01 03:15发布

问题:

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?

回答1:

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.



回答2:

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.



回答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.



回答4:

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!



回答5:

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]


回答6:

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.



回答7:

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



回答8:

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.