1、ArrayList源码解析
1 概述
- ArrayList实现了List接口,是 顺序容器,允许放入null元素
- 有一个容量(capacity),表示底层数组的实际大小。如果容量不足,容器会 自动增大底层数组的大小
- 支持泛型,泛型擦除后,容器的元素都是 Object类型
- ArrayList没有实现同步(synchronized),因此它是 线程不安全的。(vector线程安全)
- 关于数组:一旦数组初始化完成,则长度不可改变 因此ArrayList扩容时会涉及数组的拷贝
2 底层数据结构
两个重要成员变量
- Object[] elementData:
存储列表元素的数组;该数组的长度就是列表的容量
列表的容量是指它所能存储元素的最大个数
- size:
列表的大小。指当前列表包含的元素个数,跟容量不是一个概念
size 跟 elementData数组长度是不一样的。elementData 允许长度大于元素的个数
transient Object[] elementData; //存储列表元素的数组
private int size;//元素的数量
protected transient int modCount = 0; //list的修改次数
3 构造函数
有三个构造函数:
- 指定初始容量大小时,创建一个容量为参数的Object数组,并赋值给数据数组
- 不指定初始容量大小时,数据数组赋值为一个无限容量的空数组
- 构造参数为集合类型,将参数转换成Object类型数组,并赋值给数据数组
- 注意,只有当第一次add元素时,才会指定数组的长度
参照源码:
//指定初始容量大小时,创建一个容量为参数的Object数组,并赋值给数据数组
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);
}
}
// 不指定初始容量大小时,数据数组赋值为一个无限容量的空数组
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
// 构造参数为集合类型,将参数转换成Object类型数组,并赋值给数据数组
public ArrayList(Collection<? extends E> c) {
elementData = c.toArray();
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
4 自动扩容
- 添加元素时,会判断添加后是否超出当前数组长度,超出则会执行数组扩容;
- 数组扩容时,会将老数组中的元素重新 拷贝一份到新的数组中。(因为数组长度不可变,因此需要创建新数组)
- 数组执行扩容,扩容后的容量为扩容前的 1.5倍
- 尽可能评估所需要容量的大小,避免扩容。(因为扩容占用更多的内存)
参考源码:
重点!!!!!:添加前,都判断是否需要扩容:(如果size+1后,超过elementData的长度,则执行扩容,扩容为原来的1.5倍)
public boolean add(E e) {
ensureCapacityInternal(size + 1); // size 初始化时是0,
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
//计算所需容量:第一次添加时,容量为10;反则,容量为当前长度+1
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);//DEFAULT_CAPACITY = 10,即第一次添加时,数组长度变为10
}
return minCapacity;//如果数组不为空时,最小长度是当前长度+1
}
//再次计算数组所需容量
private void ensureExplicitCapacity(int minCapacity) {
modCount++;//列表修改次数递增
//所需容量大于数组的长度,则执行扩容
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
//扩容,数组的内存状态已经发生变化了
private void grow(int minCapacity) {
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);// 新的长度为旧长度的1.5倍 【右移1位(除以2)】
if (newCapacity - minCapacity < 0) // 如果扩容后的长度小于所需要的最小长度,则使用最小长度(基本不会发生)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0) //这里是极限的情况,即逼近数组分配的最大内存空间
newCapacity = hugeCapacity(minCapacity);
elementData = Arrays.copyOf(elementData, newCapacity); // 执行数组拷贝
}
5 set() get() remove()
- set()方法也就变得非常简单,直接对数组的指定位置赋值即可。
public E set(int index, E element) {
rangeCheck(index);//下标越界检查
E oldValue = elementData(index);
elementData[index] = element;//赋值到指定位置,复制的仅仅是引用
return oldValue;//返回原先位置上的元素
}
- get()方法同样很简单,唯一要注意的是由于底层数组是Object[],得到元素后需要进行类型转换。
public E get(int index) {
rangeCheck(index);
return (E) elementData[index];//注意类型转换
}
- remove()方法也有两个版本,一个是remove(int index)删除指定位置的元素,另一个是remove(Object o)删除第一个满足o.equals(elementData[index])的元素。删除操作是add()操作的逆过程,需要将删除点之后的元素向前移动一个位置。需要注意的是为了让GC起作用,必须显式的为最后一个位置赋null值。
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)
System.arraycopy(elementData, index+1, elementData, index, numMoved);
elementData[--size] = null; //清除该位置的引用,让GC起作用
return oldValue;
}
6 Fail-Fast机制
ArrayList也采用了快速失败的机制,通过记录 modCount 参数来实现。在面对并发的修改时,迭代器很快就会完全失败,而不是冒着在将来某个不确定时间发生任意不确定行为的风险。
1、ArrayList源码解析的更多相关文章
- ArrayList源码解析
ArrayList简介 ArrayList定义 1 public class ArrayList<E> extends AbstractList<E> implements L ...
- 顺序线性表 ---- ArrayList 源码解析及实现原理分析
原创播客,如需转载请注明出处.原文地址:http://www.cnblogs.com/crawl/p/7738888.html ------------------------------------ ...
- 面试必备:ArrayList源码解析(JDK8)
面试必备:ArrayList源码解析(JDK8) https://blog.csdn.net/zxt0601/article/details/77281231 概述很久没有写博客了,准确的说17年以来 ...
- ArrayList源码解析(二)
欢迎转载,转载烦请注明出处,谢谢. https://www.cnblogs.com/sx-wuyj/p/11177257.html 自己学习ArrayList源码的一些心得记录. 继续上一篇,Arra ...
- Java中的容器(集合)之ArrayList源码解析
1.ArrayList源码解析 源码解析: 如下源码来自JDK8(如需查看ArrayList扩容源码解析请跳转至<Java中的容器(集合)>第十条):. package java.util ...
- Collection集合重难点梳理,增强for注意事项和三种遍历的应用场景,栈和队列特点,数组和链表特点,ArrayList源码解析, LinkedList-源码解析
重难点梳理 使用到的新单词: 1.collection[kəˈlekʃn] 聚集 2.empty[ˈempti] 空的 3.clear[klɪə(r)] 清除 4.iterator 迭代器 学习目标: ...
- ArrayList源码解析--值得深读
ArrayList源码解析 基于jdk1.8 ArrayList的定义 类注释 允许put null值,会自动扩容: size isEmpty.get.set.add等方法时间复杂度是O(1): 是非 ...
- 【源码解析】- ArrayList源码解析,绝对详细
ArrayList源码解析 简介 ArrayList是Java集合框架中非常常用的一种数据结构.继承自AbstractList,实现了List接口.底层基于数组来实现动态容量大小的控制,允许null值 ...
- ArrayList源码解析(一)
源码解析系列主要对Java的源码进行详细的说明,由于水平有限,难免出现错误或描述不准确的地方,还请大家指出. 1.位置 ArrayList位于java.util包中. package java.uti ...
- Java集合-ArrayList源码解析-JDK1.8
◆ ArrayList简介 ◆ ArrayList 是一个数组队列,相当于 动态数组.与Java中的数组相比,它的容量能动态增长.它继承于AbstractList,实现了List, RandomAcc ...
随机推荐
- Sys_Bulkload 工具使用
一.介绍 sys_bulkload是KingbaseES提供的快速加载数据的命令行工具.用户使用sys_bulkload工具能够把一定格式的文本数据简单.快速的加载到KingbaseES数据库中,或将 ...
- 微服务系列之网关(二) konga配置操作
1.konga核心对象 Kong 的四大核心对象:upstream,target,service,route.下面分别说: (1)upstream,字面意思上游,实际项目理解是对某一个服务的一个或者多 ...
- 谣言检测——《MFAN: Multi-modal Feature-enhanced Attention Networks for Rumor Detection》
论文信息 论文标题:MFAN: Multi-modal Feature-enhanced Attention Networks for Rumor Detection论文作者:Jiaqi Zheng, ...
- Centos7安装redash
一.更改yum国内源: (1)cd /etc/yum.repos.d/ sudo yum install wget (2)备份:sudo mv /etc/yum.repos.d/CentOS-Base ...
- ATT&CK系列一 知识点总结
一.环境搭建1.环境搭建测试2.信息收集二.漏洞利用3.漏洞搜索与利用4.后台Getshell上传技巧5.系统信息收集6.主机密码收集三.内网搜集7.内网--继续信息收集8.内网攻击姿势--信息泄露9 ...
- 常量的定义(const和#define)
定义常量的方法 //均要在调用前(区别全局变量!!) 1.使用#define预处理器 2.使用const关键字 1.#define #define 常量名 常量值 //定义形式,常量名不可以是数字开头 ...
- PHP全栈开发(八):CSS Ⅸ dispaly & visibility
display用来设置一个元素如何显示: visibility用来设置一个元素可见还是隐藏. visibility:hidden: 这个语句会使元素在HTML页面中不可见.但是这个元素仍然会占用HTM ...
- PAT甲级英语单词整理
proper 正确 合适 vertex(vertices)顶点 respectively 个别 分别 indices 指标 索引 shipping 运输 incompatible 不相容 oxidiz ...
- Python生成10个八位随机密码
#生成10个八位随机密码 import random lst1=[ chr(i) for i in range(97,123) ] #生成26为字母列表 lst2=[i for i in range( ...
- SQL--插入单引号值
sql语句中插入单引号的值需要多写一个单引号进行转义 例如:插入一个值为student' name的值 update table set name='student'' name'