简体   繁体   English

Java ArrayList <Double> IndexOutOfBoundsException问题

[英]Java ArrayList<Double> IndexOutOfBoundsException Problem

I've got a Problem with ArrayList . 我有一个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 . 因为我想从元素n开始,我试图给ArrayList一个带有ensureCapacity(n+1)的容量来使用set(n,x)但是我得到一个IndexOutOfBoundsException

I tried to store n add(x) before the use of set and this works. 我尝试在使用set之前存储n add(x) ,这是有效的。

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 ;-) 所以我想知道为什么它在我的路上不起作用以及如何解决这个因为把n次add(x)不是一个好的风格;-)

When you change the capacity of an ArrayList it doesn't create any elements, it just reserves memory where there could be elements. 当您更改ArrayList的容量时,它不会创建任何元素,它只会保留可能存在元素的内存。 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. 如果您不喜欢直接使用自己的循环和列表add方法,那么还有另一种方法。 Create your ArrayList with the number of elements you want it directly like this: 使用您想要的元素数创建您的ArrayList ,如下所示:

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: 或者,如果您已经有一个列表要扩展n元素的大小:

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.) (注意,我在这里假设列表将包含Integer对象,但您可以将其更改为自定义类型。如果您使用的是raw / pre-Java 5类型,则只需删除泛型声明。)

As for your actual question: capacity != contents. 至于你的实际问题:容量!=内容。 An ArrayList internally has both a physical array and a count of what is actually in it. ArrayList内部同时具有物理数组和实际数据。 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 . 如果您需要将此数组传递给采用List的API,则使用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. 正如其他人已经回答的那样, ensureCapacity()只与性能有关,并不是普通用户经常使用的。

From Bruce Eckel's Thinking in Java book: 来自Bruce Eckel的Thinking in Java书籍:

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" 在私人消息中,Joshua Bloch写道:“...我认为我们错误地将实现细节(例如散列表大小和加载因子)放入我们的API中。客户端应该告诉我们集合的最大预期大小,我们应该从那里开始。通过选择这些参数的值,客户很容易弊大于利。作为一个极端的例子,考虑Vector的capacityIncrement。没有人应该设置这个,我们不应该提供它。如果你将它设置为任何非零值,一系列附加的渐近成本从线性到二次。换句话说,它会破坏你的表现。随着时间的推移,我们开始明智地对待这类事情。如果你看看IdentityHashMap,你会发现它没有低级调整参数“

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. 您正在获得此异常,因为ensureCapacity()仅确保分配足够的内存以将对象添加到ArrayList,我相信这是为了您想要一次添加多个对象,而无需重新定位内存。

To do what you want you would have to initiate the ArrayList with null elements first... 要做你想做的事,你必须先用null元素启动ArrayList ...

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. 然后,List中有对象可以通过索引引用,并且不会收到异常。

ensureCapacity() has another purpose. ensureCapacity()有另一个目的。 It should be used in cases when you get to know the required size of the List after it has been constructed. 它应该用于在构建List之后了解List所需大小的情况。 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. 在前一种情况下,使用ensureCapacity()可以在每次添加时保存后备阵列的多次复制。 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. ArrayList上的size字段不是。

This, however, is normal, since the capacity != size 然而,这是正常的,因为容量!=大小

Use the add(..) method, which is the only one that is increasing the size field: 使用add(..)方法,这是增加size字段的唯一方法:

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> . 也许您应该重新考虑使用List<Double>的选择。 It might be that a Map<Integer,Double> would be more appropriate if elements are to be added in an odd order. 如果要以奇数顺序添加元素,那么Map<Integer,Double>可能更合适。

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() ... 其他人对EnsureCapacity()的评价......

you should write a class like DynamicArrayList extends ArrayList. 你应该写一个像DynamicArrayList extends ArrayList这样的类。 then just overrride add(n,x) to do with for loop add(null) logic specified about. 然后只需覆盖add(n,x)来处理指定的for循环add(null)逻辑。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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