【问题标题】:Difference between implicit and explicit ArrayList size declarations?隐式和显式 ArrayList 大小声明之间的区别?
【发布时间】:2016-12-16 08:36:47
【问题描述】:

以下声明有什么区别:

List list1 = new ArrayList();

List list2 = new ArrayList(10);

默认情况下是分配10个。但是有什么区别吗? 我可以通过list2.add("something")list2添加第11个元素吗?

【问题讨论】:

  • 您可以添加第 11 个元素,但必须先添加前 10 个元素。

标签: java arraylist collections


【解决方案1】:

这是source code 的第一个示例

public  ArrayList() {
    this(10);
 }

所以没有区别。由于初始容量为10,所以无论你是否通过10,它都会被初始化为容量10。

我可以通过 list2.add("something") 在 list2 中添加第 11 个元素吗?

当然,初始容量不是最终容量。因此,随着您不断添加超过 10 个,列表的大小会不断增加。

如果您想拥有一个固定大小的容器,请使用Arrays.asList(或者,对于原始数组,使用 Guava 中的asList 方法)并考虑使用java.util.Collections.unmodifiableList()

关于 Java 8 的这一变化值得一读:In Java 8, why is the default capacity of ArrayList now zero?

简而言之,提供初始容量不会真正改变任何规模。

【讨论】:

  • 请注意,您链接的源代码来自 JDK 6。较新的 JDK 的行为完全不同 - 例如,请参阅我上面的答案。
  • 那么,如果两个列表都调用 size() 会发生什么?
  • 10 个元素为空,因此大小为零。
  • @v0ld3m0rt 大小不是容量。容量为 10,大小仍然为零,因为您没有向其中添加任何内容。
  • @v0ld3m0rt as size() 只是返回了一个变量size,修改了List后才改变,就是0
【解决方案2】:

自 JDK 1.7 更新 40 以来,ArrayList 的初始化已得到优化,在此链接中有关于两种不同行为的很好的解释 java-optimization-empty-arraylist-and-Hashmap-cost-less-memory-jdk-17040-update.

所以在 Java 1.7u40 之前没有区别,但从那个版本开始有相当大的区别。

这个区别是关于性能优化的,不会改变List.add(E e)ArrayList(int initialCapacity)的合约。

【讨论】:

    【解决方案3】:

    在 JDK 1.7.0_95 中,其他答案解释得非常好,但只是为了保持相关性:

    /**
     * Constructs a new {@code ArrayList} instance with zero initial capacity.
     */
    public ArrayList() {
        array = EmptyArray.OBJECT;
    }
    
    
    /**
     * Constructs a new instance of {@code ArrayList} with the specified
     * initial capacity.
     *
     * @param capacity
     *            the initial capacity of this {@code ArrayList}.
     */
    public ArrayList(int capacity) {
        if (capacity < 0) {
            throw new IllegalArgumentException("capacity < 0: " + capacity);
        }
        array = (capacity == 0 ? EmptyArray.OBJECT : new Object[capacity]);
    }
    

    正如评论中提到的,不接受参数的构造函数初始化一个初始容量为零的 ArrayList。

    这里更有趣的是一个变量(带有注释),它自己提供了大量信息:

    /**
     * The minimum amount by which the capacity of an ArrayList will increase.
     * This tuning parameter controls a time-space tradeoff. This value (12)
     * gives empirically good results and is arguably consistent with the
     * RI's specified default initial capacity of 10: instead of 10, we start
     * with 0 (sans allocation) and jump to 12.
     */
    private static final int MIN_CAPACITY_INCREMENT = 12;
    

    【讨论】:

    • 很明显,Java 开发人员在 1.7 中已经对此行为进行了一些更改,因为在一些 javadoc 中他们引用了 10 的旧初始容量。但代码告诉我们一些不同的事情。好帖子。
    【解决方案4】:

    Java 中的ArrayLists 是可自动增长的,如果需要添加其他元素,它们会自行调整大小。构造函数中的 size 参数仅用于内部数组的初始大小,当您确切知道要使用该数组的用途时,它是一种优化。

    指定这个初始容量通常是一个过早的优化,但如果你真的需要一个 10 个元素的ArrayList,你应该明确指定它,而不是假设默认大小是 10。虽然这确实曾经是默认行为(直到 JDK 7,IIRC),你不应该依赖它 - JDK 8(检查 java-1.8.0-openjdk-1.8.0.101-1.b14.fc24.x86_64 我已经安装)默认创建 empty ArrayLists。

    【讨论】:

      【解决方案5】:

      你刚刚选择了完美的例子。两者实际上都与new ArrayList() 调用this(10) 执行相同的操作;)但在内部它会定义大小为10 的保持数组。另一侧的ArrayList#size 方法确实只返回一个变量size,它只会是添加和删​​除元素后更改。这个变量也是IOOB异常的主要原因。所以你不能这样做。

      例如,如果您检查ArrayList 的代码,您会注意到方法ArrayList#add 将调用ArrayList#rangeCheck。范围检查实际上只关心size 变量,而不是包含List 数据的数组的实际长度。

      因此,例如,您仍然无法在索引 5 处插入数据。此时数据数组的内部长度将为10,但由于您没有向List 添加任何内容,因此size 变量仍将是0,您将获得正确的@987654336 @你什么时候会尝试这样做。

      在用任意大小初始化List 后尝试调用list.size(),你会注意到返回的大小将是0

      【讨论】:

        【解决方案6】:

        您始终可以在列表中添加元素。但是,ArrayList 使用的内部数组是使用默认大小 10 或您在初始化 ArrayList 时指定的大小进行初始化的。这意味着,如果你例如添加第 11 个元素,必须增加数组大小,这是通过将数组的内容复制到一个新的更大的数组实例来完成的。这当然需要时间,具体取决于列表/数组的大小。因此,如果您已经知道您的列表将包含数千个元素,那么如果您已经使用该近似大小来初始化列表会更快。

        【讨论】:

          猜你喜欢
          • 2020-04-20
          • 1970-01-01
          • 2015-04-23
          • 1970-01-01
          • 2013-05-19
          • 1970-01-01
          • 1970-01-01
          • 1970-01-01
          • 2015-08-18
          相关资源
          最近更新 更多