【问题标题】:Backing array of an ArrayList has a different length then the ArrayList's .size()ArrayList 的后备数组的长度与 ArrayList 的 .size() 不同
【发布时间】:2018-06-15 21:53:07
【问题描述】:

在调试应用程序时,在无效索引处访问 ArrayList 时引发以下错误:

java.lang.ArrayIndexOutOfBoundsException: length=5; index=-1
    at java.util.ArrayList.get(ArrayList.java:439)

预期的索引无效 (-1),但出乎意料的是长度为 5。正在访问的 ArrayList 经验证具有 .size()3

深入ArrayList的源码,可以发现如下:

/**                                            
 * Default initial capacity.                   
 */                                            
private static final int DEFAULT_CAPACITY = 10;

/**                                                                          
 * Shared empty array instance used for default sized empty instances. We    
 * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when  
 * first element is added.                                                   
 */                                                                          
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};        

/**                                                                          
 * The array buffer into which the elements of the ArrayList are stored.     
 * The capacity of the ArrayList is the length of this array buffer. Any     
 * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA     
 * will be expanded to DEFAULT_CAPACITY when the first element is added.     
 */                                                                          
// Android-note: Also accessed from java.util.Collections                    
transient Object[] elementData; // non-private to simplify nested class access

/**
 * Constructs an empty list with an initial capacity of ten.
 */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

似乎当实例化一个ArrayList,并添加第一项时,默认情况下可以给后备数组10的长度。当上述原始错误显示长度为10 时,这已通过实验验证(一次)。

但是,在大多数运行中,错误中显示的后备数组的长度是 5,而正在访问的 ArrayList.size() 仍然是 3。这个后备数组的长度如何修改为5的长度?尤其是在源代码中,如果显示除.size() 之外的任何值,人们会认为它是10

我希望修改内部支持数组以适应其中元素数量的长度,特别是为了抛出ArrayIndexOutOfBoundsException,因为当它与@不匹配时显示的长度会很混乱ArrayList 的 987654340@。

【问题讨论】:

    标签: java android arrays arraylist


    【解决方案1】:

    首先 - 这是正常的。 ArrayList 这样做是为了更快。如果底层数组与列表大小匹配,则添加/删除操作会慢得多(每次都需要重新分配数组)。

    要回答您的直接问题,有多种方法可以实例化ArrayList - 特别是通过将现有Collection 传递给它,或提供int initialCapacity。这两者都可以为您提供比默认值更小的底层数组。

    // Source code of other `ArrayList` constructors below (trimmed for clarity to how `elementData` gets initialized)
    
    public ArrayList(int initialCapacity) {
        ...
        this.elementData = new Object[initialCapacity];
        ...
    }
    
    public ArrayList(Collection<? extends E> c) {
        ...
        elementData = c.toArray();
        ...
    }
    

    【讨论】:

    • 另一方面是支持数组永远不会自动收缩(尽管用户可以使用trimToSize() 来明确地将数组大小与元素数量相匹配)。
    【解决方案2】:

    这是完全正常的。 ArrayList 的后备数组在调整大小时会扩展到更多,而不是所需的大小。这样就不必每次添加元素时都对其进行扩展,因为扩展需要 O(n) 次副本,因此添加额外空间可以减少您必须执行的副本数。

    此外,DEFAULT_CAPACITY 实际上并没有用作初始大小。您可能已经注意到,使用的初始数组实际上是空的,大小为零,然后根据实际添加的元素数量来扩展数组。

    【讨论】:

    • 它扩展的大小是否因执行而异?我很好奇 105 在不同运行中的长度是如何观察到的。
    • 不应该这样变化。
    • @Orbit 据我所知,当数组增长时,大小应该加倍。这保证了“O(log(n))insert 操作的摊销成本。
    • 不,任何常数乘数 >1 的扩展都会给您摊销 O(1) 加法。不过,将完成 O(log n) 次扩展。
    猜你喜欢
    • 1970-01-01
    • 2016-08-28
    • 2012-04-20
    • 1970-01-01
    • 2014-06-14
    • 2019-03-24
    • 1970-01-01
    • 1970-01-01
    • 2014-11-27
    相关资源
    最近更新 更多