IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> Java知识库 -> Java常用集合类:ArrayList -> 正文阅读

[Java知识库]Java常用集合类:ArrayList

1、知识点

1、ArrayList底层数组默认初始化大小为10,但是使用ArrayList的无参构造函数的时候,并没有马上进行扩容,我们查看源码就会发现,无参构造函数只是将ArrayList中的DEFAULTCAPACITY_EMPTY_ELEMENTDATA(一个空数组)赋值给了底层数组。
在这里插入图片描述
2、ArrayList是非线程安全的,是因为ArrayList底层数组,及维护数组的属性等信息,在修改的时候并没有进行同步控制(加锁),所以多线程环境下对这些数据进行修改的时候是互相不可见的,他们修改的值也存在互相覆盖的问题。

3、如果在增强for循环或者迭代器中使用ArrayList时,数组被改变会马上抛出异常。这里说的for循环只有增强的for循环,不包括普通for循环,因为增强for循环底层使用的是迭代器(iterator)ArrayList在实现Iterable接口时,加入了版本号进行控制(ArrayList每次对数组进行修改的操作都会同时增加版本号),迭代过程中会对版本号进行校验,如果与预期版本号不符就会抛出异常。

4、源码中的变量size并不是指的是ArrayList中数组的大小,而是数组中元素的个数,这个看源码的注释就能明白。这个也很好理解,当你初始化一个容量为100的数组的时候(new ArrayList(100);),当没有添加任何元素时调用size方法,其返回值是0;这个问题其实是要说你要明白两个概念:大小和容量。

5、当采用添加初始化数据的方式初始化ArrayList时(ArrayList(Collection<? extends E> c)),如果添加的集合中元素不是Object类型(如:数组类型),当调用toArray方法将这个集合转换为Object[]后再对该数组进行修改时,会报数组存储异常(ArrayStoreExecption)。

6、ArrayList在按照对象删除元素(remove(Object obj))的时候,其实也是通过循环并对比的方式来完成的,这里要注意的是这里用于比较的方法是equals方法,那么当这个类是我们自定义的类时就需要特别注意了,如果有这种使用场景那就需要重写equals方法了,比如你自定义的类Student,User。。。

7、说一下扩容:
无参构造函数,会创建一个空数组,当第一次add元素时会初始化为大小为10(默认初始化大小)的数组。

扩容时机:就是当ArrayList中数组满的时候,再加元素时会进行扩容。就是比如数组长度是10,加第11个元素的时候;或者长度是10,现在有9个元素,再一次性加2个元素的时候就会扩容。

扩容大小一般是当前数组大小的1.5倍;扩容的临界值是int的最大值减去8,也就是说ArrayList中允许的数组最大长度为int的最大值减去8。这里可以查看一下jdk1.8源码(网上有些教程中说是int最大值,略微有点不准确):

    /**
     * The maximum size of array to allocate.
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

在这里插入图片描述上面这个图其实就是扩容的源码,我们看到其实ArrayList在扩容的时候其实就是看最小容量(minCapacity这个值,也是你向数组中加入元素后元素的个数),有没有大于当前数组大小。如果大于,那就扩容到旧数组的1.5倍,如果旧数组1.5倍还是不够那就扩容到最小容量(比如初始化一个大小为10的数组,结果一次性加入18个元素),如果大于了ArrayList允许的最大长度,那大小就不再增长了。

ArrayList提供了两个空数组,一个是DEFAULTCAPACITY_EMPTY_ELEMENTDATA,一个是EMPTY_ELEMENTDATA,空参构造函数将DEFAULTCAPACITY_EMPTY_ELEMENTDATA赋值给底层数组elementData,而new ArrayList(0)会将EMPTY_ELEMENTDATA赋值给elementData(看下面的图片),这里只是做个区分,好在扩容的时候为空参构造函数扩容为默认长度10的数组(这个可以看上面图片第一个方法)。
在这里插入图片描述

扩容其实就是创建一个新的大一些的数组,然后把旧数组中的数据拷贝到新的数组中去,用新的数组代替旧的数组。调用的是Arrays.copyOf()方法,这个方法又是调用了native方法:arraycopy,其实删除ArrayList中删除元素也是通过该方法进行删除操作的。

为什么不是每次扩容都是直接扩容到正好的大小呢?其实我们看到扩容其实就是不断的创建更大的数组来代替原来的数组,并且将旧值复制到新的数组,这样的操作其实是很耗资源的,为了节省频繁复制的开销就需要多预留一定的空间;那么当我们在开发时如果能预先知道我们要创建的数组有多大,就直接在初始化的时候指定数组大小,这样就可以达到节约系统资源的效果。还有就是如果能采用addAll()方法就不要使用循环add()的方式,这样也可以减少扩容次数。

这其中还隐藏着一个小细节,就是0.5倍的这个扩容的量,我们可以发现当数组较小的时候进行扩容其实每次增长的并不是特别多,所以不会占用太多空间;当数组较大时每次扩容增长的就比较多,这样在数据增长很快的情况下就不需要频繁扩容。

  Java知识库 最新文章
计算距离春节还有多长时间
系统开发系列 之WebService(spring框架+ma
springBoot+Cache(自定义有效时间配置)
SpringBoot整合mybatis实现增删改查、分页查
spring教程
SpringBoot+Vue实现美食交流网站的设计与实
虚拟机内存结构以及虚拟机中销毁和新建对象
SpringMVC---原理
小李同学: Java如何按多个字段分组
打印票据--java
上一篇文章      下一篇文章      查看所有文章
加:2022-02-04 10:54:21  更:2022-02-04 10:55:07 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/24 10:28:50-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码