ArrayList 扩容机制

这篇具有很好参考价值的文章主要介绍了ArrayList 扩容机制。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

ArrayList 基本介绍

ArrayList实现了List接口。它可以存储包括null的任何类型的对象,允许重复元素。ArrayList在内部使用一个数组来存储元素,当元素数量超过数组容量时,ArrayList会自动重新分配更大的内部数组,并且将现有元素复制到新数组中。ArrayList基本等同于Vector,但是ArrayList是线程不安全的(执行效率高),在多线程情况下不建议使用ArrayList

ArrayList 源码阅读及操作机制

首先ArrayList中用来存储元素的数组是 Object 类型的数组 elementData,ArrayList的容量就是这个数组的大小。

transient Object[] elementData;

通过 debug 下面这段代码来观察ArrayList的扩容机制。

public class TestArrayList() {
  public static void main(String[] args) {
    ArrayList list = new ArrayList();
    // ArrayList list = new ArrayList(4);
        for (int i = 1; i <= 10; i++) {
            list.add(i);
        }
        for (int i = 11; i <= 15; i++) {
            list.add(i);
        }
  }
}

构造方法

当使用无参构造器创建ArrayList对象时,会创建一个默认容量为10的空数组。

public ArrayList() {
  this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 用于默认大小的空实例的共享空数组。将其与 EMPTY_ELEMENTDATA 区分开,
// 以便在添加第一个元素时知道需要扩容多少。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

使用指定大小的构造器创建ArrayList对象时,则初始 elementData 容量为指定大小。

public ArrayList(int initialCapacity) {
  if (initialCapacity > 0) {
    this.elementData = new Object[initialCapacity];
  } else if (initialCapacity == 0) {
    this.elementData = EMPTY_ELEMENTDATA;
  } else {
    throw new IllegalArgumentException("Illegal Capacity: "+
                                       initialCapacity);
  }
}

add(E e)

add(E e)方法将指定元素添加到列表末尾,该方法先调用ensureCapacityInternal()方法确保容量至少是所需的最小容量(即当前大小加一),然后再赋值。

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

由于ArrayList允许的元素类型是 Object,所以添加基本类型的数据时,会先将其转换为对应的包装类型。

ensureCapacityInternal(int minCapacity)

private void ensureCapacityInternal(int minCapacity) {
  ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}

calculateCapacity()

该方法计算需要的容量,如果elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA(使用无参构造器时即符合该条件),则返回DEFAULT_CAPACITY(10),否则返回 minCapacity(size + 1)。

private static int calculateCapacity(Object[] elementData, int minCapacity) {
  if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
    return Math.max(DEFAULT_CAPACITY, minCapacity);
  }
  return minCapacity;
}
private static final int DEFAULT_CAPACITY = 10;

ensureExplicitCapacity(int minCapacity)

modCount 记录列表被结构性修改的次数(结构性修改是指添加或删除一个或多个元素,或显式调整后备数组的大小,仅仅设置元素的值不是结构性修改)。如果需要的容量大于 elementData 的长度,则调用grow()方法进行扩容。

private void ensureExplicitCapacity(int minCapacity) {
  modCount++;

  // overflow-conscious code
  if (minCapacity - elementData.length > 0)
    grow(minCapacity);
}

grow()

该方法增加容量以确保列表至少能够容纳最小容量参数minCapacity指定的元素数量。计算得到新容量应为旧容量的 1.5 倍。如果计算得到的新容量小于需要的最小容量,则新容量应为需要的最小容量(使用无参构造器第一次添加元素时即为这种情况,第一次扩容为 10)。如果请求的数组大小超过了虚拟机的限制,可能会导致 OutOfMemoryError。最后通过数组复制copyOf.copyOf()来进行真正的扩容。

private void grow(int minCapacity) {
  // overflow-conscious code
  int oldCapacity = elementData.length;
  int newCapacity = oldCapacity + (oldCapacity >> 1);
  if (newCapacity - minCapacity < 0)
    newCapacity = minCapacity;
  if (newCapacity - MAX_ARRAY_SIZE > 0)
    newCapacity = hugeCapacity(minCapacity);
  // minCapacity is usually close to size, so this is a win:
  elementData = copyOf.copyOf(elementData, newCapacity);
}

总结

当使用无参构造器创建ArrayList对象时,elementData 初始容量为 0,第一次添加元素时 elementData 扩容为 10,以后再需要扩容,按 1.5 倍来扩容。若使用有参构造器创建ArrayList对象,elementData 初始容量为指定大小,如果需要扩容,则扩容为 1.5 倍。

值得注意的是,由于每次调整容量都需要将所有元素复制到新数组中,所以在元素数量较多时,频繁地调整容量可能会导致性能下降。为了避免频繁地调整容量,可以使用ArrayList的指定大小的构造方法或在添加大量元素之前使用ensureCapacity()方法预先指定较大的容量,以减少容量调整的次数。

另外,当从ArrayList中删除元素时,并不会立即缩小内部数组的容量。如果希望减少内存占用,可以使用trimToSize()方法来调整ArrayList的容量,使其与元素数量匹配。这样可以释放未使用的内存空间。文章来源地址https://www.toymoban.com/news/detail-480785.html

到了这里,关于ArrayList 扩容机制的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 学习一下Java的ArrayList和contains函数和扩容机制

    在Leetcode上做题写了两种暴力解法,但是执行效率上不太一样。 时间上差很远,内存虽然差不多但是前者击败30%,后者击败94%。这两种解法区别是用一条 ArrayList 还是两条来存数据,所以contains虽然执行次数一样但是检测的长度上不一样,而且 ArrayList 的扩容次数也不一样,所

    2024年02月08日
    浏览(53)
  • ArrayList是如何动态扩容的

    ArrayList是Java中常用的动态数组实现,在底层是使用数组来存储元素。当需要扩展ArrayList的长度时,它会执行以下步骤: 初始化数组:在创建ArrayList对象时,会初始化一个初始长度的数组来存储元素。默认情况下,初始长度为10。 添加元素时的扩容:当往ArrayList中添加元素时

    2024年02月15日
    浏览(37)
  • Java中LinkList的基本介绍和细节讨论。双向链表的代码和LinkList的源码。LinkList和ArrayList的比较与选择。

    LinkedList 是 Java 中的一个双向链表实现的类,它实现了 List 接口,同时也实现了 Deque 接口,因此可以用作列表、队列或双端队列。下面是关于 LinkedList 的基本介绍和细节讨论: 基本介绍: LinkedList 是一个双向链表实现,每个节点包含了当前元素的值、指向前一个节点的引用和

    2024年02月11日
    浏览(36)
  • 【数据结构与算法】1、学习动态数组数据结构(基本模拟实现 Java 的 ArrayList 实现增删改查)

    🍃 数据结构是 计算机 存储 、 组织 数据的方式 🎉 线性 结构 线性表(数组、链表、栈、队列、哈希表) 🎉 树形 结构 二叉树 AVL 树 红黑树 B 树 堆 Trie 哈夫曼树 并查集 🎉 图形 结构 邻接矩阵 邻接表 🎁 线性表是具有 n 个 相同类型元素 的有限 序列 (n = 0) a1 是首节点

    2024年02月10日
    浏览(77)
  • ArrayList快速失败机制

    ArrayList实现了一种称为 快速失败 (fail-fast)的机制,该机制在并发修改时会抛出 ConcurrentModificationException 异常。 这种机制的实现原理是:ArrayList在遍历时会 记录列表的修改总数 (通过modCount字段),如果在遍历过程中列表结构发生变化,那么modCount的值会增大。 每次遍历前,迭代器都会

    2024年02月04日
    浏览(30)
  • vector扩容机制

    在学习了vector的时候,总说linux下是以二倍扩容的,VS是以1.5倍扩容的。 但是想一想为什么扩容是这样的呢,为什么不能是3倍或者其他倍数呢?  所以带着这些疑问,接着往下看。 首先,我们要知道vector的扩容机制:当向vector插入元素的时候,即当_finish == _end_of_storage,可能

    2024年01月15日
    浏览(49)
  • c++ vector的扩容机制

    1、当向vector push_back一个元素时,如果此时元素个数超过了vector的容量,会触发扩容 2、扩容的过程是:开辟新空间-拷贝旧空间的元素-释放旧空间 3、扩容过程中开辟新空间的大小影响着往vector插入元素的效率: 如果新空间大小为旧空间大小+1,也就是边插入边扩容,这样每

    2024年02月10日
    浏览(38)
  • redis源码之:扩容后的dictScan遍历顺序与JDK的concurrentHashMap 扩容机制

    进入正题前,先来复习下关于2次幂的mod运算 设n为2次幂,数a mod n 等价于 a n-1 从二进制来看,相当于余数为a省去n最高位左侧的所有位(含最高位),保留n右侧所有低位即为余数 如:a = 7(0000_0111),n=4(0000_0100),通过7 (4-1) 即0000_0111 0000_0011 结果为3(0000_0011) dictScan方法中,对游标

    2024年02月11日
    浏览(31)
  • 深入源码解析ArrayList:探秘Java动态数组的机制与性能

    1.1 介绍ArrayList的基本概念和作用 在Java中,ArrayList是一个实现了List接口的动态数组。它可以根据需要自动增加大小,因此可以存储任意数量的元素。 基本概念: ArrayList是Java中常用的集合类之一,它可以存储对象,并且可以根据索引访问和操作这些对象。 ArrayList是基于数组

    2024年02月04日
    浏览(41)
  • C++中STL的vector扩容机制

    前阵子面试的时候,被问到往vector中插入一个数据可能会发生什么? 我答:可能会 扩容 ; 为啥vector支持变长? 我答:它实在堆上动态申请内存,因此有自己的一套扩容机制,可以操作内存大小; 它有size()和capacity()记录当前的有效元素个数和容量, 还有配套的resize()管理实际存放

    2024年02月20日
    浏览(31)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包