ArrayList 底层实现原理
ArrayList的底层实现原理
1, 属性:
private static final int DEFAULT_CAPACITY = 10;
private static final Object [] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
transient Object [] elementData;
private int size; // 动态数组的实际大小
2,构造方法:
public ArrayList(){
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
调用无参的构造方法,则会将elementData赋上为空的一个数组.
public ArrayList(Collection<? extends E> c){
elementData = c.toArray();
if(size = elementData.length != 0){
if(elementData.getClass()!=Object[].class)
elementData = Arrays.copyOf(elementData,size,Object[].class);
}else{
elementData = EMPTY_ELEMENTDATA;
}
}
调用有参的构造方法,参数是一个带范型的集合。生成一个可以不为空的集合。
如果参数使用toArray(),返回类型不是Object[],则使用copyOf() 复制下并赋值给elementData,
public ArrayList(int initialCapacity){
if(initialCapacity > 0){
this.elementData = new Object[initialCapacity];
}else if(initialCapacicty == 0){
this.elementData = EMPTY_ELEMENTDATA;
}else{
throw new IlleagalArgumentException("illagal initialCapacity"+initialCapcity);
}
}
调用有参的构造函数,初始化储存元素的容量。定义之后,内存会为开辟初始化容量大小的空间。
随着ArrayList 里面元素的增加直到initialCapacity,不需要数组向新数组的拷贝。
Tips:
Arraylsit list = new ArrayList(10);
默认的Object[] elementData 的长度为10;
list.size();的长度还是为0
因为size 代表的逻辑长度,内存中实际存在的元素的长度。
3,add;
public boolean add(E e){
ensureCapacityInternal(size + 1);
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity){
if(elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA){
minCapacity = Math.max(DEFAILT_CAPACITY,minCapacity);
}
ensureExplicitCapacity(minCapacity);
}
向ArrayList 里面增添元素的时候,当ArrayList 的为空的时候,add元素时,如果没有设定Arraylist的Object elementData的长度,
会设定默认的Object[] elementData 的长度为10,所以在长度为10 之前能够避免在每添加一个新的元素时候,就要复制原来的元素的数组到新的数组的里面去。
private void ensureExplicitCapacity(int minCapacity){
modCount ++;//extends from AbstactArrayList
if(minCapacity - elementData.length > 0)
grow(minCapacity);//长度不够,每次扩充1.5倍<粗略>
}
private void grow(int minCapacity){
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);
if(newCapacity - minCapacity <0)
newCapacity = minCapacity;
if(newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
//MAX_ARRAY_SIZE 2147483639
elementData = Arrays.copyOf(elementData,newCapacity);//扩充了Obect elementData[]的长度
}
private void hugeCapacity(int minCapacity){
if(minCapacity < 0)//因为minCapacity int 型,超过int 的最大值就会<0,就代表内存溢出
throw new OutOfMemoryError();
return(minCapacity > Max_ARRAY_SIZE)?Integer.MAX_VALUE:MAX_ARRAY_SIZE;
//最大上限就是Integer的最大值-8;
}
4,add
public void add(int index,E element){
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); //开辟空间
System.arraycopy(elemntData,index,elementData,index + 1,size - index);
size ++;
}
private void rangeCheckForAdd(int index){
if(index > size || index < 0 ){
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
}
add(int index,E elemenmt)中使用System.arraycopy的思想,是因为elementData的容器长度大于逻辑长度,即它本身的留有一个空位null,可用作填充数据。
例:
Object [] elementData = {"one","two","three","four","five","null","null","null"};
要想在index为3的位置插入"six",
System.arraycopy(elementData,3,elementData,4,2);
elementData:源数组
3:源数组的起始位置
elementData:目标数组
4:目标数组的开始位置
2:size - index,size代表的是elementData中不为null的元素的长度
所以,就可以将index + 1 后半段的不为null 的元素复制到源数组的本身,同时将index 这个位置空出来,然后插入就可以实现add插入指定位置的功能。
elementData[index] = "six";
size ++;
Arrays.copyOf(),System.copyof()
system.arraycopy(Object src,int srcPos,Object dest,destPos,length);native 方法,源码是由C++ 实现
Arrays.copyOf(),内部实现的也是System.arraycopy(),只是ArrayCopy()会创建一个新的数组,将源数组向新数组复制。
5,remove
public E remove(int index){
rangeCheck(index);//先check下index有没有有越界
modCount++;//非安全线程
E oldValue = elementData[index];
int numMoved = size - index - 1;
if(numMoved > 0)
System.arraycopy(elementData,index + 1,elementData,numMoved);
//如果numMoved == 0 的情况也就是源数组只有一个元素的情况,就无需复制数组,只需执行elemnetData[--size]即elementData[0] =null
elementData[--size] = null;//将elementData 的空间变成null
return oldValue;
}
private void rangeCheck(int index){
if(index >= size){
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
}
从一定程度上来说,remove(int index)的原理是和add(int index, E element)的逻辑是差不多的。
6,remove
public boolean remove(Object o){
if(o == null){
for(int index = 0;index < size;i++){
if(elementData[index]==null){
fastRemove(index);
return true;
}
}
}else{
for(int index = 0;index < size;i++){
if(o.equals(elementData[index])){
fastRemove(index);
return true;
}
}
}
return false;
}
private void fastRemove(index){
modCount++;//非安全线程
int numMoved = size - index - 1;
if(numMoved > 0)
System.arraycopy(elementData,index + 1,elementData,numMoved);
elementData[--size] = null;//将elementData 的空间变成null
}
remove(Object o) 删除集合中匹配的元素<最先匹配的元素>,其思路和remove(index)是一样的
remove(index)是直接告知index,remove(Obejct o)是先找到匹配的index,然后再进行和remove(index) 大致的操作。
其中,remove(Obejct o)需考虑元素o 是否为null,关于两元素是否相等的比较方法== 和 equals
== 在值类型中,就是比较值是否相等,但是在引用类型中,实际比较的是在内存中的地址是否相等
equals,是Obejct 中的方法,如下:
public boolean equals(Object o){
return (this == o);
}
Object 是所有类的父类,Obejct 中的equals 方法就是==,是比较内存中的地址是否相等,
许多类会重写Obejct 中的equals方法,例如String 类,重写之后就是对字符串值进行比较
在remove(Obejct o) 中的equals 方法实际比较的就是内存中地址的相等。但是如果对于对Object中equals 方法重写的类,
则实际比较的就是值是否相等。
ArrayList 底层实现原理的更多相关文章
- ArrayList底层实现原理
ArrayList概述: ArrayList是List接口的可变数组的实现.实现了所有可选列表操作,并允许包括null在内的所有元素.除了实现列表接口外,此类还提供一些方法来操作内部用来存储列表的数组 ...
- ArrayList的底层实现原理
ArrayList源码分析 1.java.util.ArrayList<E> : List 接口的大小可变数组的实现类 ArrayList 内部基于 数组 存储 各个元素. 所谓大小可变数 ...
- ArrayList底层原理
ArrayList底层采用数组实现,访问特别快,它可以根据索引下标快速找到元素.但添加插入删除等写操作效率低,因为涉及到内存数据复制转移. ArrayList对象初始化时,无参数构造器默认容量为10, ...
- Java集合:ArrayList的实现原理
Java集合---ArrayList的实现原理 目录: 一. ArrayList概述 二. ArrayList的实现 1) 私有属性 2) 构造方法 3) 元素存储 4) 元素读取 5) 元素删除 ...
- 简单复习一下ArrayList的扩容原理
刚刚跟几个好朋友喝完小酒回家,简单大概复习一下ArrayList的扩容原理,由于头有点小晕,就只大概说一下扩容的原理哈: 首先ArrayList实现了List接口,继承了AbstractList,大家 ...
- HashMap底层实现原理/HashMap与HashTable区别/HashMap与HashSet区别(转)
HashMap底层实现原理/HashMap与HashTable区别/HashMap与HashSet区别 文章来源:http://www.cnblogs.com/beatIteWeNerverGiveU ...
- ArrayList使用及原理
之前面试时,经常被问到ArrayList的原理,今天整理了一些ArrayList的使用原理和必问的知识点. ArrayList的继承关系 定义一个ArrayList的方法 ArrayList的三个构造 ...
- PHP底层工作原理
最近搭建服务器,突然感觉lamp之间到底是怎么工作的,或者是怎么联系起来?平时只是写程序,重来没有思考过他们之间的工作原理: PHP底层工作原理 图1 php结构 从图上可以看出,php从下到上是一个 ...
- ArrayList/Vector的原理、线程安全和迭代Fail-Fast
疑问 * ArrayList是非线程非安全的,具体是指什么?具体会产生什么问题?* ArrayList的内部原理是什么?为什么可以动态扩容?* Vector是线程安全的,具体是如何实现的?为什么不再推 ...
随机推荐
- 1_02 Vue Slot
slot 插槽 插槽内容 const component ={ template: ` <div> <slot></slot> </div> ` } n ...
- fiddler 实现代理的操作
- VS2013 opencv配置
有三个地方需要配置,在配置之前首先要将platform配置好,下面的例子是x64 Release的“ 然后需要将include.lib的路径配置好 然后将dll拷贝至编译生成的Release文件夹中即 ...
- Spring 注解 @Scheduled(cron = "0 0/10 * * * ? ") 动态改变时间
import java.util.Date; import java.util.concurrent.Executor; import java.util.concurrent.Executors; ...
- ios 10 新特性
UITextField添加了textContentType枚举,指示文本输入区域所期望的语义意义. 使用此属性可以给键盘和系统信息,关于用户输入的内容的预期的语义意义. 当您提供有关您期望用户在文本输 ...
- (4.5)mysql备份还原——深入解析二进制日志(1)binlog的3种工作模式与配置
(4.5)mysql备份还原——深入解析二进制日志(binlog) 关键词:二进制日志,binlog日志 0.建议 (1)不建议随便去修改binlog格式(数据库级别) (2)binlog日志的清理 ...
- 林兴爆料小程序很快可以支持各个 App 直接打开小程序
在微信开放平台基础高级产品经理林兴演讲的当场,他爆料了微信小程序一个轰动性新能力:小程序很快可以支持各个 App 直接打开小程序!没错,你没有听错,简单来说,在不久以后,所有的 App 里面都可以看到 ...
- 网站分析参数(实例分析)SimilarWeb插件参数
网站分析参数(实例分析)SimilarWeb插件参数 那么这些指标是什么意思呢? SimilarWeb Rank:类似网站排名Global Rank:全球网站排名第三栏一般是类别,大概网站从事范围 ...
- golang 对struct进行Serialize的方法,即将存取二进制文件到struct的方法
方法一: serialize 的标准方法: 使用gob 和 base64 或 base58. 方法二: 下面是自己实现的 serialize 方法,不推荐自己实现,应该用标准方法. 代码如下: pac ...
- 小程 序swiper自适应宽高
https://blog.csdn.net/qq_31604363/article/details/73715944 小程 序swiper自适应宽高 小程 序swiper自适应宽高