简体   繁体   English

在 Java 8 中,为什么 ArrayList 的默认容量现在为零?

[英]In Java 8, why is the default capacity of ArrayList now zero?

As I recall, before Java 8, the default capacity of ArrayList was 10.我记得,在 Java 8 之前, ArrayList的默认容量是 10。

Surprisingly, the comment on the default (void) constructor still says: Constructs an empty list with an initial capacity of ten.令人惊讶的是,对默认(void)构造函数的注释仍然说: Constructs an empty list with an initial capacity of ten.

From ArrayList.java :ArrayList.java

/**
 * 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 = {};

...

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

Technically, it's 10 , not zero, if you admit for a lazy initialisation of the backing array.从技术上讲,它是10 ,而不是零,如果您承认支持数组的延迟初始化。 See:看:

public boolean add(E e) {
    ensureCapacityInternal(size + 1);
    elementData[size++] = e;
    return true;
}

private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }

    ensureExplicitCapacity(minCapacity);
}

where在哪里

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

What you're referring to is just the zero-sized initial array object that is shared among all initially empty ArrayList objects.您所指的只是在所有最初为空的ArrayList对象之间共享的零大小的初始数组对象。 Ie the capacity of 10 is guaranteed lazily , an optimisation that is present also in Java 7.10的容量是惰性保证的,Java 7 中也存在这种优化。

Admittedly, the constructor contract is not entirely accurate.诚然,建造商合同并不完全准确。 Perhaps this is the source of confusion here.也许这就是这里混乱的根源。

Background背景

Here's an E-Mail by Mike Duigou这是 Mike Duigou 的电子邮件

I have posted an updated version of the empty ArrayList and HashMap patch.我已经发布了空 ArrayList 和 HashMap 补丁的更新版本。

http://cr.openjdk.java.net/~mduigou/JDK-7143928/1/webrev/ http://cr.openjdk.java.net/~mduigou/JDK-7143928/1/webrev/

This revised implementation introduces no new fields to either class.这个修改后的实现没有为任何一个类引入新的字段 For ArrayList the lazy allocation of the backing array occurs only if the list is created at default size.对于 ArrayList,仅当以默认大小创建列表时,才会发生后备数组的延迟分配。 According to our performance analysis team, approximately 85% of ArrayList instances are created at default size so this optimization will be valid for an overwhelming majority of cases.根据我们的性能分析团队,大约 85% 的 ArrayList 实例是在默认大小下创建的,因此这种优化对于绝大多数情况都是有效的。

For HashMap, creative use is made of the threshold field to track the requested initial size until the bucket array is needed.对于 HashMap,创造性地使用阈值字段来跟踪请求的初始大小,直到需要存储桶数组。 On the read side the empty map case is tested with isEmpty().在读取端,空地图案例使用 isEmpty() 进行测试。 On the write size a comparison of (table == EMPTY_TABLE) is used to detect the need to inflate the bucket array.在写入大小上,使用 (table == EMPTY_TABLE) 的比较来检测是否需要扩充存储桶数组。 In readObject there's a little more work to try to choose an efficient initial capacity.在 readObject 中有更多的工作来尝试选择有效的初始容量。

From: http://mail.openjdk.java.net/pipermail/core-libs-dev/2013-April/015585.html来自: http : //mail.openjdk.java.net/pipermail/core-libs-dev/2013-April/015585.html

In java 8 default capacity of ArrayList is 0 until we add at least one object into the ArrayList object (You can call it lazy initialization).在 java 8 中,ArrayList 的默认容量为 0,直到我们向 ArrayList 对象中添加至少一个对象(您可以称之为延迟初始化)。

Now question is why this change has been done in JAVA 8?现在的问题是为什么在 JAVA 8 中进行了这种更改?

Answer is to save memory consumption.答案是节省内存消耗。 Millions of array list objects are created in real time java applications.数以百万计的数组列表对象是在实时 Java 应用程序中创建的。 Default size of 10 objects means that we allocate 10 pointers (40 or 80 bytes) for underlying array at creation and fill them in with nulls. 10 个对象的默认大小意味着我们在创建时为底层数组分配 10 个指针(40 或 80 字节)并用空值填充它们。 An empty array (filled with nulls) occupy lot of memory .空数组(填充空值)占用大量内存。

Lazy initialization postpones this memory consumption till moment you will actually use the array list.延迟初始化将这种内存消耗推迟到您实际使用数组列表的那一刻。

Please see below code for help.请参阅下面的代码以获取帮助。

ArrayList al = new ArrayList();          //Size:  0, Capacity:  0
ArrayList al = new ArrayList(5);         //Size:  0, Capacity:  5
ArrayList al = new ArrayList(new ArrayList(5)); //Size:  0, Capacity:  0
al.add( "shailesh" );                    //Size:  1, Capacity: 10

public static void main( String[] args )
        throws Exception
    {
        ArrayList al = new ArrayList();
        getCapacity( al );
        al.add( "shailesh" );
        getCapacity( al );
    }

    static void getCapacity( ArrayList<?> l )
        throws Exception
    {
        Field dataField = ArrayList.class.getDeclaredField( "elementData" );
        dataField.setAccessible( true );
        System.out.format( "Size: %2d, Capacity: %2d%n", l.size(), ( (Object[]) dataField.get( l ) ).length );
}

Response: - 
Size:  0, Capacity:  0
Size:  1, Capacity: 10

Article Default capacity of ArrayList in Java 8 explains it in details.文章Java 8 中 ArrayList 的默认容量详细解释了它。

If the very first operation that is done with an ArrayList is to pass addAll a collection which has more than ten elements, then any effort put into creating an initial ten-element array to hold the ArrayList's contents would be thrown out the window.如果对 ArrayList 完成的第一个操作是向addAll传递一个包含超过十个元素的集合,那么在创建初始十元素数组以保存 ArrayList 的内容方面所做的任何努力都将被抛弃。 Whenever something is added to an ArrayList it's necessary to test whether the size of the resulting list will exceed the size of the backing store;每当向 ArrayList 添加某些内容时,有必要测试结果列表的大小是否会超过后备存储的大小; allowing the initial backing store to have size zero rather than ten will cause this test to fail one extra time in the lifetime of a list whose first operation is an "add" which would require creating the initial ten-item array, but that cost is less than the cost of creating a ten-item array that never ends up getting used.允许初始后备存储的大小为零而不是十将导致此测试在列表的生命周期中失败一次,该列表的第一个操作是“添加”,这将需要创建初始十项数组,但该成本是低于创建一个永远不会被使用的十项数组的成本。

That having been said, it might have been possible to improve performance further in some contexts if there were a overload of "addAll" which specified how many items (if any) would likely be added to the list after the present one, and which could use that to influence its allocation behavior.话虽如此,在某些情况下,如果“addAll”过载,它可能会在某些情况下进一步提高性能,它指定在当前列表之后可能会添加多少项(如果有),并且可以使用它来影响其分配行为。 In some cases code which adds the last few items to a list will have a pretty good idea that the list is never going to need any space beyond that.在某些情况下,将最后几项添加到列表中的代码将有一个很好的想法,即列表永远不需要超出此范围的任何空间。 There are many situations where a list will get populated once and never modified after that.在很多情况下,列表会被填充一次,之后再也不会被修改。 If at the point code knows that the ultimate size of a list will be 170 elements, it has 150 elements and a backing store of size 160, growing the backing store to size 320 will be unhelpful and leaving it at size 320 or trimming it to 170 will be less efficient than simply having the next allocation grow it to 170.如果此时代码知道列表的最终大小将是 170 个元素,它有 150 个元素和大小为 160 的后备存储,则将后备存储增加到 320 大小将无济于事,将其保留为 320 大小或将其修剪为170 比简单地让下一个分配将其增加到 170 效率更低。

The question is 'why?'.问题是“为什么?”。

Memory profiling inspections (for example ( https://www.yourkit.com/docs/java/help/inspections_mem.jsp#sparse_arrays ) shows that empty (filled with nulls) arrays occupy tons of memory .内存分析检查(例如( https://www.yourkit.com/docs/java/help/inspections_mem.jsp#sparse_arrays )显示空(填充为空值)数组占用大量内存。

Default size of 10 objects means that we allocate 10 pointers (40 or 80 bytes) for underlying array at creation and fill them in with nulls. 10 个对象的默认大小意味着我们在创建时为底层数组分配 10 个指针(40 或 80 字节)并用空值填充它们。 Real java applications create millions of array lists.真正的 Java 应用程序创建了数百万个数组列表。

The introduced modification removes^W postpone this memory consumption till moment you will actually use the array list.引入的修改删除 ^W 将此内存消耗推迟到您实际使用数组列表的那一刻。

After above question I gone through ArrayList Document of Java 8. I found the default size is still 10 only.在上述问题之后,我浏览了 Java 8 的 ArrayList 文档。我发现默认大小仍然只有 10。

请看下面

ArrayList default size in JAVA 8 is stil 10. The only change made in JAVA 8 is that if a coder adds elements less than 10 then the remaining arraylist blank places are not specified to null. JAVA 8 中ArrayList 默认大小为stil 10。JAVA 8 中唯一的变化是,如果编码器添加小于10 的元素,则剩余的arraylist 空白位置不会指定为空。 Saying so because I have myself gone through this situation and eclipse made me look into this change of JAVA 8.之所以这么说,是因为我自己也经历过这种情况,而 eclipse 让我研究了 JAVA 8 的这种变化。

You can justify this change by looking at below screenshot.您可以通过查看下面的屏幕截图来证明此更改的合理性。 In it you can see that ArrayList size is specified as 10 in Object[10] but the number of elements displayed are only 7. Rest null value elements are not displayed here.其中可以看到Object[10]中ArrayList的大小被指定为10,但是显示的元素数量只有7个,其余空值元素没有显示在这里。 In JAVA 7 below screenshot is same with just a single change which is that the null value elements are also displayed for which the coder needs to write code for handling null values if he is iterating complete array list while in JAVA 8 this burden is removed from the head of coder/developer.在下面的 JAVA 7 屏幕截图中,只有一个更改,即空值元素也显示出来,如果他正在迭代完整的数组列表,编码器需要编写代码来处理空值,而在 JAVA 8 中,这个负担从编码员/开发人员的负责人。

Screen shot link.屏幕截图链接。

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

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