iis服务器助手广告广告
返回顶部
首页 > 资讯 > 后端开发 > Python >详解ArrayList的扩容机制
  • 450
分享到

详解ArrayList的扩容机制

2024-04-02 19:04:59 450人浏览 八月长安

Python 官方文档:入门教程 => 点击学习

摘要

目录一、ArrayList 了解过吗?它是啥?有啥用?二、ArrayList 如何指定底层数组大小的三、数组的大小一旦被规定就无法改变四、ArrayList 具体是怎么添加数

一、ArrayList 了解过吗?它是啥?有啥用?

众所周知,Java 集合框架拥有两大接口 CollectionMap,其中,Collection 麾下三生子 ListSetQueueArrayList 就实现了 List 接口,其实就是一个数组列表,不过作为 Java 的集合框架,它只能存储对象引用类型,也就是说当我们需要装载的数据是诸如 intfloat 等基本数据类型的时候,必须把它们转换成对应的包装类。

ArrayList 的底层实现是一个 Object 数组:

既然它是基于数组实现的,数组在内存空间中是连续分配的,那必然查询速率非常快,不过当然也肯定逃不过增删效率低的缺陷。

另外,和 ArrayList 一样同样实现了 List 接口的、我们比较常用的还有 LinkedListLinkedList 比较特殊,它不仅实现了 List 接口,还实现了 Queue 接口,所以你可以看见 LinkedList 经常被当作队列使用:


Queue<Integer> queue = new LinkedList<>();

LinkedList 人如其名,它的底层自然是基于链表的,而且还是个双向链表。链表的特性和数组正好是反的,由于没有索引,所以查询效率低,但是增删速度快。

二、ArrayList 如何指定底层数组大小的

OK,首先,既然咱真正存储数据的地方是数组,那我们初始化 ArrayList 的时候自然要给数组分配一个大小,开辟一个内存空间。我们先来看看 ArrayList 的无参构造函数:

可以看到,它为底层的 Object 数组也就是 elementData 赋值了一个默认的空数组 DEFAULTCAPACITY_EMPTY_ELEMENTDATA。也就是说,使用无参构造函数初始化 ArrayList 后,它当时的数组容量为 0 。

这给咱初始化一个容量为 0 的数组有啥用?啥也存不了啊?别急,如果使用了无参构造函数来初始化 ArrayList, 只有当我们真正对数据进行添加操作 add 时,才会给数组分配一个默认的初始容量 DEFAULT_CAPACITY = 10。看下图:

说完了无参构造,ArrayList 的有参构造函数就是中规中矩了,按照用户传入的大小开辟数组空间:

三、数组的大小一旦被规定就无法改变

 ArrayList 是怎么对底层数组进行扩容的?

ArrayList 的底层实现是 Object 数组,我们知道,数组的大小一旦被规定就无法改变。那如果我们不断的往里面添加数据的话,ArrayList 是如何进行扩容的呢?或者说 ArrayList 是如何实现存放任意数量对象的呢?

OK,扩容发生在啥时候?那肯定是我们往数组中新加入一个元素但是发现数组满了的时候。没错,我们去 add 方法中看看 ArrayList 是怎么做扩容的:

ensureExplicitCapacity 判断是否需要进行扩容,很显然,grow 方法是扩容的关键:

说实话,别的都不用看了,看上面图中的黄色框框就知道 ArrayList 是怎么扩容的了:扩容后的数组长度 = 当前数组长度 + 当前数组长度 / 2。最后使用 Arrays.copyOf 方法直接把原数组中的数组 copy 过来,需要注意的是,Arrays.copyOf 方法会创建一个新数组然后再进行拷贝。

举个例子画个图来演示一下:

四、ArrayList 具体是怎么添加数据的

OK,add 方法我们刚刚讲了一半,添加数据前会先判断一下是否需要扩容,真正的添加数据的操作在下半部分:

先讲下 add(int index, E element) 这个方法的含义,就是在指定索引 index 处插入元素 element。比如说 ArrayList.add(0, 3),意思就是在头部插入元素 3。

再来看看 add 方法的核心 System.arraycopy,这个方法有 5 个参数:

  • elementData:源数组
  • index:从源数组中的哪个位置开始复制
  • elementData:目标数组
  • index + 1:复制到目标数组中的哪个位置
  • size - index:要复制的源数组中数组元素的数量

解释一下上面代码中 arraycopy 的意思,举个例子,我们想要在 index = 5 的位置插入元素,首先,我们会复制一遍源数组 elementData(这里我们称复制的数组为新数组吧),然后把源数组中从 index = 5 的位置开始到数组末尾的元素,放到新数组的 index + 1 = 6 的位置上:

于是,这就给我们要新增的元素腾出了位置,然后在新数组 index = 5 的位置放入元素 element 就完成了添加的操作:

显然,不用多说,ArrayList 的将数据插入到指定位置的操作性能非常低下,因为要开辟新数组复制元素啊,要是涉及到扩容那就更慢了。

另外,ArrayList 还内置了一个直接在末尾添加元素的 add 方法,不用复制数组,直接 size ++ 就好,这个方法应该是我们最常使用的:

五、ArrayList 又是如何删除数据的呢

Ctrl + F 找到 remove 方法,就这?和添加一个道理,也是复制数组

举个例子,假设我们要删除数组的 index = 5 的元素,首先,我们会复制一遍源数组,然后把源数组中从 index + 1 = 6 的位置开始到数组末尾的元素,放到新数组的 index = 5 的位置上:

也就是说 index = 5 的元素直接被覆盖掉了,给了你被删除的感觉。同样的,它的效率自然也是十分低下的

六、ArrayList 是线程安全的吗?不安全的表现

ArrayListLinkedList 都不是线程安全的,我们以在末尾添加元素的 add 方法为例,来看看 ArrayList 线程不安全的表现是啥:

黄色框里的并不是一个原子操作,它由两步操作构成:


elementData[size] = e;
size = size + 1;

在单线程执行这两条代码时,那当然没有任何问题,但是当多线程环境下执行时,可能就会发生一个线程添加的值覆盖另一个线程添加的值。举个例子:

  • 假设 size = 0,我们要往这个数组的末尾添加元素
  • 线程 A 开始添加一个元素,值为 A。此时它执行第一条操作,将 A 放在了数组 elementData 下标为 0 的位置上
  • 接着线程 B 刚好也要开始添加一个值为 B 的元素,且走到了第一步操作。此时线程 B 获取到的 size 值依然为 0,于是它将 B 也放在了 elementData 下标为 0 的位置上
  • 线程 A 开始增加 size 的值,size = 1
  • 线程 B 开始增加 size 的值,size = 2

这样,线程 A、B 都执行完毕后,理想的情况应该是 size = 2,elementData[0] = A,elementData[1] = B。而实际情况变成了 size = 2,elementData[0] = B(线程 B 覆盖了线程 A 的操作),下标 1 的位置上什么都没有。并且后续除非我们使用 set 方法修改下标为 1 的值,否则这个位置上将一直为 null,因为在末尾添加元素时将会从 size = 2 的位置上开始。

上段代码验证下:

结果和我们分析的一样:

ArrayList 的线程安全版本是 Vector,它的实现很简单,就是把所有的方法统统加上 synchronized

既然它需要额外的开销来维持同步,所以理论上来说它要比 ArrayList 要慢。

七、为什么线程不安全还要用它呢

因为在大多数场景中,查询的情况居多,不会涉及太频繁的增删。那如果真的涉及频繁的增删,可以使用LinkedList,底层链表实现,为增删而生。而如果你非得保证线程安全那就使用 Vector。当然实际开发中使用最多的还是 ArrayList,虽然线程不安全、增删效率低,但是查询效率高啊。

以上就是详解ArrayList的扩容机制的详细内容,更多关于ArrayList 扩容机制的资料请关注编程网其它相关文章!

--结束END--

本文标题: 详解ArrayList的扩容机制

本文链接: https://www.lsjlt.com/news/128708.html(转载时请注明来源链接)

有问题或投稿请发送至: 邮箱/279061341@qq.com    QQ/279061341

本篇文章演示代码以及资料文档资料下载

下载Word文档到电脑,方便收藏和打印~

下载Word文档
猜你喜欢
  • 详解ArrayList的扩容机制
    目录一、ArrayList 了解过吗?它是啥?有啥用?二、ArrayList 如何指定底层数组大小的三、数组的大小一旦被规定就无法改变四、ArrayList 具体是怎么添加数...
    99+
    2022-11-12
  • ArrayList扩容机制(原理)
    ArrayList扩容的本质就是计算出新的扩容数组的size后实例化,并将原有数组内容复制到新数组中去。(不是原数组,而是新数组然后给予数组对象地址)。 默认情况下,新的容量会是原容量的1.5倍。 新容量=旧容量右移一位(相当于除于2)在加...
    99+
    2023-09-03
    java 开发语言
  • add方法理解ArrayList的扩容机制
    目录ArrayList的构造方法(前置知识)ArrayList的add方法(理解扩容机制)add 添加元素得到最小扩容量判断是否需要扩容扩容方法ArrayList的构造方法(前置知识...
    99+
    2023-03-07
    add方法ArrayList扩容 add ArrayList
  • 关于ArrayList的动态扩容机制解读
    目录1. 前言2. ArrayList 的动态扩容机制2.1. ArrayList 的主要属性2.2. ArrayList 的构造器2.3. ArrayList 的动态扩容3. 小结...
    99+
    2022-11-13
    ArrayList的扩容机制 动态扩容机制 ArrayList动态扩容机制
  • Java基础之ArrayList的扩容机制
    我们知道Java中的ArrayList对象底层是基于数组实现的,而数组是有长度限制的,那基于数组实现的ArrayList是否有长度限制呢?我们通过ArrayList的构造方法来剖析 ...
    99+
    2022-11-12
  • 怎么用add方法理解ArrayList的扩容机制
    这篇文章主要介绍“怎么用add方法理解ArrayList的扩容机制”,在日常操作中,相信很多人在怎么用add方法理解ArrayList的扩容机制问题上存在疑惑,小编查阅了各式资料,整理出简单好用的操作方法,希望对大家解答”怎么用add方法理...
    99+
    2023-07-05
  • java arraylist扩容机制原理是什么
    Java中的ArrayList是基于数组实现的动态数组,其扩容机制的原理如下:1. 初始容量:当创建一个ArrayList对象时,会...
    99+
    2023-10-19
    java arraylist
  • Java ArrayList扩容机制原理是什么
    本文小编为大家详细介绍“Java ArrayList扩容机制原理是什么”,内容详细,步骤清晰,细节处理妥当,希望这篇“Java ArrayList扩容机制原理是什么”文章能帮助大家解决疑惑,下面跟着小编的思路慢慢深入,一...
    99+
    2023-07-05
  • java中ArrayList集合的扩容机制是什么
    这篇文章主要讲解了“java中ArrayList集合的扩容机制是什么”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“java中ArrayList集合的扩容机制是什么”吧!1、扩容要看添加方法,...
    99+
    2023-06-20
  • go的切片扩容机制详解
    切片的扩容策略?如何扩容? 扩容策略:如果切片的容量小于 1024 个元素,于是扩容的时候就翻倍增加容量。总容量从原来的1个翻倍到现在的2个。 一旦元素个数超过 1024 个元素,那...
    99+
    2023-05-14
    go 切片扩容
  • JDK8中的HashMap初始化和扩容机制详解
    一、HashMap初始化方法 HashMap() 不带参数,默认初始化大小为16,加载因子为0.75; HashMap(int initialCapacity) 指定初始化大小; H...
    99+
    2022-11-12
  • ArrayList和LinkedList的区别、扩容机制以及底层的实现方式
    目录ArrayList和LinkedList区别、扩容机制及底层实现ArrayListLinkedListVestorArrayList的扩容机制LinkedList的扩容机制Arr...
    99+
    2023-05-13
    ArrayList和LinkedList区别 扩容机制 底层实现
  • Java中的ArrayList容量及扩容方式
    目录查看JDK1.8ArrayList的源代码1、默认初始容量为102、最大容量为Integer.MAX_VALUE-83、扩容方式:JavaArrayList()扩容原理先看下Ar...
    99+
    2022-11-12
  • JDK1.8中ArrayList是如何扩容的
    ArrayList简介: ArrayList实现了List接口它是一个可调整大小的数组可以用来存放各种形式的数据。并提供了包括CRUD在内的多种方法可以对数据进行操作但是它不是线程...
    99+
    2022-11-12
  • JDK1.8中ArrayList是怎么扩容的
    本篇内容主要讲解“JDK1.8中ArrayList是怎么扩容的”,感兴趣的朋友不妨来看看。本文介绍的方法操作简单快捷,实用性强。下面就让小编来带大家学习“JDK1.8中ArrayList是怎么扩容的”吧!ArrayList简介:ArrayL...
    99+
    2023-06-25
  • HashMap的扩容机制
    目录 一、HashMap的底层 二、HashMap的扩容机制原理 1、JDK1.7版本扩容 2、JDK1.8版本扩容 三、HashMap底层JDK1.7到JDK1.8的变化 一、HashMap的底层 底层:采用数组+链表(JDK1.7)...
    99+
    2023-09-04
    java 面试 数据结构 链表 容器
  • ArrayList中自动扩充机制的示例分析
    这篇文章主要为大家展示了“ArrayList中自动扩充机制的示例分析”,内容简而易懂,条理清晰,希望能够帮助大家解决疑惑,下面让小编带领大家一起研究并学习一下“ArrayList中自动扩充机制的示例分析”这篇文章吧。ArrayList li...
    99+
    2023-05-30
    java arraylist
  • 怎么理解List的扩容机制
    这篇文章主要讲解了“怎么理解List的扩容机制”,文中的讲解内容简单清晰,易于学习与理解,下面请大家跟着小编的思路慢慢深入,一起来研究和学习“怎么理解List的扩容机制”吧!一:背景 讲故事在前一篇大内存排查中,我们看到了Dictionar...
    99+
    2023-06-01
  • hashmap的扩容机制怎么理解
    今天小编给大家分享一下hashmap的扩容机制怎么理解的相关知识点,内容详细,逻辑清晰,相信大部分人都还太了解这方面的知识,所以分享这篇文章给大家参考一下,希望大家阅读完这篇文章后有所收获,下面我们一起来了解一下吧。hashmap的扩容机制...
    99+
    2023-07-05
  • ArrayList和LinkedList的区别、扩容机制及底层的实现方式是什么
    这篇文章主要介绍“ArrayList和LinkedList的区别、扩容机制及底层的实现方式是什么”的相关知识,小编通过实际案例向大家展示操作过程,操作方法简单快捷,实用性强,希望这篇“ArrayList和LinkedList的区别、扩容机制...
    99+
    2023-07-05
软考高级职称资格查询
编程网,编程工程师的家园,是目前国内优秀的开源技术社区之一,形成了由开源软件库、代码分享、资讯、协作翻译、讨论区和博客等几大频道内容,为IT开发者提供了一个发现、使用、并交流开源技术的平台。
  • 官方手机版

  • 微信公众号

  • 商务合作