简体   繁体   中英

Java ArrayList<Double> IndexOutOfBoundsException Problem

I've got a Problem with ArrayList . I need it to store a result. Because I want to start with element n I tried to give the ArrayList a capacity with ensureCapacity(n+1) to use set(n,x) but I get an IndexOutOfBoundsException .

I tried to store n add(x) before the use of set and this works.

So I'd like to know why it doesn't work on my way and how to solve this because put n times a add(x) isn't a good style ;-)

When you change the capacity of an ArrayList it doesn't create any elements, it just reserves memory where there could be elements. You can check the size before and after adjusting the capacity and you will see that it does not change.

The purpose of changing the capacity is if you know in advance how many elements you will have, then you can avoid unnecessary repeated resizing as you add new elements, and you can avoid memory wastage from excess unused capacity.

If you don't like using your own loop and the list add method directly then there is another way. Create your ArrayList with the number of elements you want it directly like this:

final int MAX_ELEMENTS = 1000;
List<Integer> myList = new ArrayList<Integer>(
    Collections.<Integer>nCopies(MAX_ELEMENTS, null));

Or, if you already have a list that you want to expand the size by n elements:

myList.addAll(Collections.<Integer>nCopies(n, null));

(Note, I assumed here that the list would be holding Integer objects, but you can change this to your custom type. If you are working with raw/pre-Java 5 types then just drop the generic declarations.)

As for your actual question: capacity != contents. An ArrayList internally has both a physical array and a count of what is actually in it. Increasing the capacity, changes the internal array so it can hold that many elements, however, the count does not change. You need to add elements to increase that count.

On the other hand, if you are just trying to set specific elements and know the maximum that you want to use, why not use an array directly? If you then need to pass this array to an API that takes List s, then use Arrays.asList . The other classes could still change contents of your backing array but it would not be able to increase the size or capacity of it.

As others have answered, ensureCapacity() is just related to performance, is not frequently used by the common user.

From Bruce Eckel's Thinking in Java book:

In a private message, Joshua Bloch wrote: "... I believe that we erred by allowing implementation details (such as hash table size and load factor) into our APIs. The client should perhaps tell us the maximum expected size of a collection, and we should take it from there. Clients can easily do more harm than good by choosing values for these parameters. As an extreme example, consider Vector's capacityIncrement. No one should ever set this, and we shouldn't have provided it. If you set it to any non-zero value, the asymptotic cost of a sequence of appends goes from linear to quadratic. In other words, it destroys your performance. Over time, we're beginning to wise up about this sort of thing. If you look at IdentityHashMap, you'll see that it has no low-level tuning parameters"

You are getting this exception because ensureCapacity() only makes sure that there is enough memory allocated for adding objects to an ArrayList, I believe this is in case you want to add multiple objects at once, without having to relocate memory.

To do what you want you would have to initiate the ArrayList with null elements first...

int n = 10;  //capacity required
ArrayList foo = new ArrayList();

for( int i=0; i<=n; i++ ) {
      foo.add(null);
}

Then you have objects in the List that you can reference via index and you wont receive the exception.

ensureCapacity() has another purpose. It should be used in cases when you get to know the required size of the List after it has been constructed. If you know the size before it is constructor, just pass it as a an argument to the constructor.

In the former case use ensureCapacity() to save multiple copying of the backing array on each addition. However, using that method leaves the structure in a seemingly inconsistent state

  • the size of the backing array is increased
  • the size field on the ArrayList isn't.

This, however, is normal, since the capacity != size

Use the add(..) method, which is the only one that is increasing the size field:

ArrayList list = new ArrayList();
list.ensureCapacity(5); // this can be done with constructing new ArrayList(5)

for (int i = 0; i < list.size - 1; i ++) {
   list.add(null);
}
list.add(yourObject);

Perhaps you should rethink the choice of using List<Double> . It might be that a Map<Integer,Double> would be more appropriate if elements are to be added in an odd order.

Whether this is appropriate depends on knowledge about your usage that I don't have at the moment though.

Is the data structure eventually going to be completely filled, or is the data sparse?

what other people said about ensureCapacity() ...

you should write a class like DynamicArrayList extends ArrayList. then just overrride add(n,x) to do with for loop add(null) logic specified about.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM