arraylist 并发_Java集合--- ArrayList的实现原理

本文介绍了ArrayList在Java中的实现原理,包括其基于动态数组的结构、非线程安全特性及如何在多线程环境中使用。同时,文章详细讨论了ArrayList的构造方法、元素的添加、读取和删除操作,以及其容量管理。ArrayList并非同步,如果需要线程安全,可以使用Collections.synchronizedList或CopyOnWriteArrayList。
摘要由CSDN通过智能技术生成

一,ArrayList概述:

ArrayList是基于副本实现的,是一个动态数组,其容量能自动增长,串行C语言中的动态申请内存,动态增长内存。

ArrayList不是线程安全的,只能用在单线程环境下,多线程环境下可以考虑用Collections.synchronizedList(List l)函数返回一个线程安全的ArrayList类,也可以使用并发并发包下的CopyOnWriteArrayList类。

ArrayList实现了一个可序列化的接口,因此它支持序列化,能够通过序列化传输,实现了RandomAccess接口,支持快速随机访问,实际上就是通过下标序号进行快速访问,实现了Cloneable接口,能被克隆。

每个ArrayList实例都有一个容量,该容量是指使用存储列表元素的数组的大小。它总是至少等于列表的大小。依次向ArrayList中不断添加元素,其容量也自动增长。带来数据向新副本的重新复制,因此,如果可预知数据量的多少,可在构造ArrayList时指定其容量。在添加大量元素前,应用程序也可以使用确保容量操作来增加ArrayList实例的容量,这可以减少递增式再分配的数量。

注意,此实现不是同步的。如果多个线程同时访问一个ArrayList实例,而其中至少一个线程从结构上修改了列表,那么它必须保持外部同步。

2b47789b292e304b9a2dad6a1776cb1d.png

二,ArrayList的实现:

对于ArrayList而言,它实现List接口,并使用交替保存所有元素。其操作基本上是对阵列的操作。下面我们来分析ArrayList的源代码:

1)私有属性:

ArrayList定义只定义类两个私有属性:

/ ** 
*用于存储ArrayList元素的数组缓冲区。
* ArrayList的容量是此数组缓冲区的长度。
* / 
私有瞬时Object [] elementData; 
 
/ ** 
* ArrayList的大小(它包含的元素数)。
* 
* @序列
* /

私有int大小;

很容易理解,elementDataStorageArrayList内部的元素,大小表示它包含的元素的数量。

有个关键字需要解释:transient。

Java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想用serialization机制来保存它。为了在一个特定对象的一个​​域上关闭serialization,可以在这个域前加上关键字transient。

2)构造方法:

ArrayList提供了三种方式的构造器,可以构造一个初始容量为10的空列表,构造一个指定初始容量的空列表以及构造一个包含指定集合的​​元素的列表,这些元素按照该集合的迭代器返回它们的顺序排列的。

// ArrayList带容量大小的构造函数。

公共ArrayList(int initialCapacity){ 
super(); 
如果(initialCapacity <0)
抛出新的IllegalArgumentException(“ Illegal Capacity:” + 
initialCapacity); 
//新建一个
摘要this.elementData = new Object [initialCapacity]; 
} 
 
// ArrayList无参构造函数。替代容量是10。public 
ArrayList(){ 
this(10); 
} 
 
//创建一个包含集合的ArrayList 
public ArrayList(Collection <?
extended E> c){ elementData = c.toArray(); 
大小= elementData.length; 
如果(elementData.getClass()!= Object []。class)
elementData = Arrays.copyOf(elementData,size,Object []。class); 
}

8494a01269f08628f02c869006be24db.png

3)元素存储:

ArrayList提供了set(int index,E element),add(E e),add(int index,E element),addAll(Collection <?extends E> c),addAll(int index,Collection <?extends E> c) )这些添加元素的方法。下面我们一一讲解:

//用指定的元素替代此列表中指定位置上的元素,并返回以前位于该位置上的元素。

公共E set(int index,E element){ 
RangeCheck(index); 
 
E oldValue =(E)elementData [index]; 
elementData [index] =元素;
返回oldValue; boolean add(E e){ sureCapacity(size + 1); 
} 
//将指定的元素添加到此列表的尾部。elementData [size ++] = e; 返回true;}





//将指定的元素插入此列表中的指定位置。

//如果当前位置有元素,则向右移动当前位于该位置的元素以及所有后续元素(将其索引加1)。

public void add(int index,E element){

如果(索引>大小||索引<0)

抛出新的IndexOutOfBoundsException(“ Index:” + index +“,Size:” + size);

//如果长度不足,将进行扩容。

sureCapacity(size + 1); //增加modCount!

//将elementData中从索引位置开始,长度为size-index的元素,

//复制到从下标为索引+1位置开始的新的elementData数组中。

//即将当前位于该位置的元素以及所有后续元素右移一个位置。

System.arraycopy(elementData,index,elementData,index + 1,size-index);

elementData [index] =元素;

大小++;

}

//按照指定集合的​​继承器所返回的元素顺序,依次集合中的所有元素添加到此列表的尾部。

公共布尔addAll(Collection <?扩展E> c){ 
Object [] a = c.toArray(); 
int numNew = a.length; 
sureCapacity(size + numNew); //增加modCount 
System.arraycopy(a,0,elementData,size,numNew); 
大小+ = numNew; 
返回numNew!= 0; 
} 
//从指定的位置开始,将指定集合中的所有元素插入到此列表中。public 
boolean addAll(int index,Collection <?extends E> c){ 
if(index> size || index <0)
throw new IndexOutOfBoundsException(
“ Index:” + index +“,Size:” + size); 
 
Object [] a = c.toArray(); 
int numNew = a.length; 
sureCapacity(size + numNew); //增加modCount 
 
int numMoved = size-index; 
如果(numMoved>
System.arraycopy(elementData,index,elementData,index + numNew,numMoved); 
 
System.arraycopy(a,0,elementData,index,numNew); 
大小+ = numNew; 
返回numNew!= 0; 
}

看到add(E e)中先调用了确保容量(size + 1)方法,之后将元素的索引赋值给elementData [size],而后size自增。例如初次添加时,size为0,add将elementData [0 ]赋值e,然后大小设置为1(类似执行以下两条语句elementData [0] = e; size = 1)。将元素的索引赋值给elementData [size]不是会出现多个越界的情况吗?这里关键就在ensureCapacity(size + 1)中了。

585c0ea73aaeaa4cb0036721edd4dfed.png

4)元素读取:

公共E get(int index){ 
RangeCheck(index); //返回此列表中指定位置上的元素。
 
返回(E)elementData [index]; 
}

)元素删除:

ArrayList提供了根据下标或指定对象两种方式的删除功能。如下:

romove(int索引):

// public E remove(int index){ 
RangeCheck(index); //删除此列表中指定位置上的元素。
5个modCount ++; 
E oldValue =(E)elementData [index]; 
8 int numMoved =大小-索引-1; 
如果(numMoved> 0)
System.arraycopy(elementData,index + 1,elementData,index,numMoved); 
elementData [-size] = null; //让gc进行工作12 13 return oldValue; 
}

首先是检查范围,修改modCount,保留将要被除去的元素,将可移除位置之后的元素向前挪动一个位置,将列表末尾元素置空(null),返回被移除的元素。

remove(对象o)

首先通过代码可以看到,当可删除成功后返回true,否则返回false。了index,不通过remove(index)来删除元素呢?因为fastRemove跳过了判断边界的处理,因为找到元素就相当于确定了index不会超过边界,而且fastRemove并不返回被删除的元素。下面是fastRemove的代码,基本和remove(index)一致。

私有无效fastRemove(int index){ 
modCount ++; 
int numMoved =大小-索引-1; 
如果(numMoved> 0)
System.arraycopy(elementData,index + 1,elementData,index,
numMoved); 
elementData [-size] = null; //让gc进行工作8}

removeRange(int fromIndex,int toIndex)

受保护的void removeRange(int fromIndex,int toIndex){ 
modCount ++; 
int numMoved = size-toIndex; 
System.arraycopy(elementData,toIndex,elementData,fromIndex,
numMoved); 
//让gc进行工作
int newSize = size-(toIndex-fromIndex); 
而(size!= newSize)
elementData [-size] = null; 
}

执行过程是将elementData从到索引位置开始的元素向前移动到索引,然后将到索引位置之后的元素全部放置空顺便修改大小。

————————————————————————————————————

最后,我自己是一名从事了多年开发的Java程序员,辞职目前在做自己的Java私人定制课程,今年年初我花了一个月整理了一份最适合2019年学习的Java学习干货,可以送给每一位喜欢Java的小伙伴,想要获取的可以申请加入我自建的Java交流学习裙:钱面六三零中间四七三最后711。即可免费获取。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值