RError.com

RError.com Logo RError.com Logo

RError.com Navigation

  • 主页

Mobile menu

Close
  • 主页
  • 系统&网络
    • 热门问题
    • 最新问题
    • 标签
  • Ubuntu
    • 热门问题
    • 最新问题
    • 标签
  • 帮助
主页 / 问题 / 1060744
Accepted
Miron
Miron
Asked:2020-12-19 20:10:43 +0000 UTC2020-12-19 20:10:43 +0000 UTC 2020-12-19 20:10:43 +0000 UTC

向 ArrayList 添加元素的机制是什么?

  • 772

这个过程在内部是如何安排的ArrayList?我想知道实施细节。它是如何作用于价值观的?特别是grow().

java
  • 1 1 个回答
  • 10 Views

1 个回答

  • Voted
  1. Best Answer
    Miron
    2020-12-19T20:10:43Z2020-12-19T20:10:43Z

    首先,让我们找出可以增加 ArrayList 的方法。

    1) 使用方法add(T element)
    2) 使用方法add(int index, T element)
    3) 使用方法addAll(Collection<? extends T> c)
    4) 使用方法addAll(int index, Collection<? extends T> c)

    让我们分别分析每种方法:

    1)这是方法源代码add(T element):

    public boolean add(E e) {
        modCount++;
        add(e, elementData, size);
        return true;
    }
    

    它增加一个变量modCount(结构转换的数量)。以下是 javadoc 1.4 的摘录:

    该字段由迭代器使用,实现了迭代器和 listIterator 方法返回的迭代器的实现。如果此字段的值意外更改,迭代器(或列表迭代器)将抛出 ConcurrentModificationException 以响应以下操作:remove、previous、set 或 add。这提供了容错行为,而不是在迭代期间面对并发修改时的不确定行为。

    然后调用方法add(E e, Object[], int s)。然后它返回 true,因为它应该返回这个值,因为它在Collection.add(E e).

    我们来分析一下这个方法add(E e, Object[] elementData, int s)。这是源代码:

    private void add(E e, Object[] elementData, int s) {
        if (s == elementData.length)
            elementData = grow();
        elementData[s] = e;
        size = s + 1;
    }
    

    在一个变量中e- 我们创建的元素。在变量elementData中 - 所有元素的数组(可能未完全填充),以及s- 实际位于元素数组中的元素数量。如果数组没有完全填满,我们向数组中添加一个元素,并增加包含实际定位元素数量的变量。否则,除此之外,我们首先使用grow().

    我们来分析一下这个方法grow()。这是源代码:

    private Object[] grow() {
        return grow(size + 1);
    }
    

    这意味着它只调用了另一个grow方法,但是使用参数int minCapacity,我们将包含实际定位元素数量的变量的值加1。这是它的源代码:

    private Object[] grow(int minCapacity) {
        int oldCapacity = elementData.length;
        if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            int newCapacity = ArraysSupport.newLength(oldCapacity,
                    minCapacity - oldCapacity, /* minimum growth */
                    oldCapacity >> 1           /* preferred growth */);
            return elementData = Arrays.copyOf(elementData, newCapacity);
        } else {
            return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
        }
    }
    

    如果数组不为空,那么我们将原始数组的长度增加等于旧长度的一半(向下舍入),如果结果长度不大于最大数组长度(Integer.MAX_VALUE - 8),否则等于最大数组长度,如果oldLength + minGrowth小于最大数组长度,否则等于Integer.MAX_VALUE。
    否则,我们重新初始化数组,将引用传递给长度为 10 或minCapacity如果minCapacity> 10的对象数组。
    如果有人对代码感兴趣ArraysSupport.newLength,这里是源代码(我刚刚从 java 13 找到它们):

    public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
        // assert oldLength >= 0
        // assert minGrowth > 0
    
        int newLength = Math.max(minGrowth, prefGrowth) + oldLength;
        if (newLength - MAX_ARRAY_LENGTH <= 0) {
            return newLength;
        }
        return hugeLength(oldLength, minGrowth);
    }
    
    private static int hugeLength(int oldLength, int minGrowth) {
        int minLength = oldLength + minGrowth;
        if (minLength < 0) { // overflow
            throw new OutOfMemoryError("Required array length too large");
        }
        if (minLength <= MAX_ARRAY_LENGTH) {
            return MAX_ARRAY_LENGTH;
        }
        return Integer.MAX_VALUE;
    }
    

    方法分析add(T element)结束。

    2)这是方法源代码add(int index, T element):

    public void add(int index, E element) {
        rangeCheckForAdd(index);
        modCount++;
        final int s;
        Object[] elementData;
        if ((s = size) == (elementData = this.elementData).length)
            elementData = grow();
        System.arraycopy(elementData, index,
                         elementData, index + 1,
                         s - index);
        elementData[index] = element;
        size = s + 1;
    }
    

    rangeCheckForAdd(int index)index如果它不在真实元素中,则抛出错误。modCount 传统上会增加。然后我们在没有空间容纳新元素的情况下使用方法增加数组的长度grow(),并将所有元素移到需要插入元素的索引的右侧,将要插入的对象插入到产生“空白”。内部长度变量传统上是递增的。

    3)这是方法源代码addAll(Collection<? extends T> c):

    public boolean addAll(Collection<? extends E> c) {
        Object[] a = c.toArray();
        modCount++;
        int numNew = a.length;
        if (numNew == 0)
            return false;
        Object[] elementData;
        final int s;
        if (numNew > (elementData = this.elementData).length - (s = size))
            elementData = grow(s + numNew);
        System.arraycopy(a, 0, elementData, s, numNew);
        size = s + numNew;
        return true;
    }
    

    modCount传统上增加。我们从集合中获取一个数组,以便我们可以使用标准方法插入一个数组。如果要插入的集合为空,则返回false。如果插入的集合不适合数组,我们增加它。然后我们将“集合”添加到数组的最后。内部长度变量传统上是递增的。我们报告工作的顺利完成。

    4)这是方法源代码addAll(int index, Collection<? extends T> c):

    public boolean addAll(int index, Collection<? extends E> c) {
        rangeCheckForAdd(index);
    
        Object[] a = c.toArray();
        modCount++;
        int numNew = a.length;
        if (numNew == 0)
            return false;
        Object[] elementData;
        final int s;
        if (numNew > (elementData = this.elementData).length - (s = size))
            elementData = grow(s + numNew);
    
        int numMoved = s - index;
        if (numMoved > 0)
            System.arraycopy(elementData, index,
                             elementData, index + numNew,
                             numMoved);
        System.arraycopy(a, 0, elementData, index, numNew);
        size = s + numNew;
        return true;
    }
    

    rangeCheckForAdd(int index)执行与插入单个元素时相同的功能。我们所做的一切都与 中相同addAll(Collection<? extends T> c),直到创建一个变量numMoved,它存储包含后的真实数组元素的数量index(请记住,迭代从 0 开始!)。如果它们确实存在,那么我们将它们移到右侧,为插入的集合腾出空间。然后将集合插入到以 开头的范围内index。内部长度变量传统上是递增的。我们报告工作的顺利完成。

    感谢您的关注。如果您发现错误或不准确之处 - 请告诉我,我愿意接受任何批评。

    • 10

相关问题

Sidebar

Stats

  • 问题 10021
  • Answers 30001
  • 最佳答案 8000
  • 用户 6900
  • 常问
  • 回答
  • Marko Smith

    根据浏览器窗口的大小调整背景图案的大小

    • 2 个回答
  • Marko Smith

    理解for循环的执行逻辑

    • 1 个回答
  • Marko Smith

    复制动态数组时出错(C++)

    • 1 个回答
  • Marko Smith

    Or and If,elif,else 构造[重复]

    • 1 个回答
  • Marko Smith

    如何构建支持 x64 的 APK

    • 1 个回答
  • Marko Smith

    如何使按钮的输入宽度?

    • 2 个回答
  • Marko Smith

    如何显示对象变量的名称?

    • 3 个回答
  • Marko Smith

    如何循环一个函数?

    • 1 个回答
  • Marko Smith

    LOWORD 宏有什么作用?

    • 2 个回答
  • Marko Smith

    从字符串的开头删除直到并包括一个字符

    • 2 个回答
  • Martin Hope
    Alexandr_TT 2020年新年大赛! 2020-12-20 18:20:21 +0000 UTC
  • Martin Hope
    Alexandr_TT 圣诞树动画 2020-12-23 00:38:08 +0000 UTC
  • Martin Hope
    Air 究竟是什么标识了网站访问者? 2020-11-03 15:49:20 +0000 UTC
  • Martin Hope
    Qwertiy 号码显示 9223372036854775807 2020-07-11 18:16:49 +0000 UTC
  • Martin Hope
    user216109 如何为黑客设下陷阱,或充分击退攻击? 2020-05-10 02:22:52 +0000 UTC
  • Martin Hope
    Qwertiy 并变成3个无穷大 2020-11-06 07:15:57 +0000 UTC
  • Martin Hope
    koks_rs 什么是样板代码? 2020-10-27 15:43:19 +0000 UTC
  • Martin Hope
    Sirop4ik 向 git 提交发布的正确方法是什么? 2020-10-05 00:02:00 +0000 UTC
  • Martin Hope
    faoxis 为什么在这么多示例中函数都称为 foo? 2020-08-15 04:42:49 +0000 UTC
  • Martin Hope
    Pavel Mayorov 如何从事件或回调函数中返回值?或者至少等他们完成。 2020-08-11 16:49:28 +0000 UTC

热门标签

javascript python java php c# c++ html android jquery mysql

Explore

  • 主页
  • 问题
    • 热门问题
    • 最新问题
  • 标签
  • 帮助

Footer

RError.com

关于我们

  • 关于我们
  • 联系我们

Legal Stuff

  • Privacy Policy

帮助

© 2023 RError.com All Rights Reserve   沪ICP备12040472号-5