Java ArrayList 實(shí)現(xiàn)實(shí)例講解
ArrayList概述:
ArrayList是基于數(shù)組實(shí)現(xiàn)的,是一個(gè)動(dòng)態(tài)數(shù)組,其容量能自動(dòng)增長,類似于C語言中的動(dòng)態(tài)申請內(nèi)存,動(dòng)態(tài)增長內(nèi)存。
ArrayList不是線程安全的,只能用在單線程環(huán)境下,多線程環(huán)境下可以考慮用Collections.synchronizedList(List l)函數(shù)返回一個(gè)線程安全的ArrayList類,也可以使用concurrent并發(fā)包下的CopyOnWriteArrayList類。
ArrayList實(shí)現(xiàn)了Serializable接口,因此它支持序列化,能夠通過序列化傳輸,實(shí)現(xiàn)了RandomAccess接口,支持快速隨機(jī)訪問,實(shí)際上就是通過下標(biāo)序號(hào)進(jìn)行快速訪問,實(shí)現(xiàn)了Cloneable接口,能被克隆。
每個(gè)ArrayList實(shí)例都有一個(gè)容量,該容量是指用來存儲(chǔ)列表元素的數(shù)組的大小。它總是至少等于列表的大小。隨著向ArrayList中不斷添加元素,其容量也自動(dòng)增長。自動(dòng)增長會(huì)帶來數(shù)據(jù)向新數(shù)組的重新拷貝,因此,如果可預(yù)知數(shù)據(jù)量的多少,可在構(gòu)造ArrayList時(shí)指定其容量。在添加大量元素前,應(yīng)用程序也可以使用ensureCapacity操作來增加ArrayList實(shí)例的容量,這可以減少遞增式再分配的數(shù)量。
注意,此實(shí)現(xiàn)不是同步的。如果多個(gè)線程同時(shí)訪問一個(gè)ArrayList實(shí)例,而其中至少一個(gè)線程從結(jié)構(gòu)上修改了列表,那么它必須保持外部同步。
下面對java arraylist做一個(gè)記錄和總結(jié)吧
public class arraylist<E> {
/**
* 存放集合的元素
*
*/
private transient Object[] elementData;
/** 元素的大小 */
private int size;
定義了一個(gè)泛型類,一個(gè)object的數(shù)組和一個(gè)私有變量來記錄該集合的元素?cái)?shù)量,原文多了一個(gè)私有變量,我也不知道干嘛用的,作者也沒解釋也沒提及到,我沒使用也沒事
/**
* 根據(jù)指定大小初始化
* @param initialCapacity
*/
public arraylist(int initialCapacity){
super();
if(initialCapacity<=0){
//拋異常
throw new IllegalArgumentException("初始化參數(shù)不能小于0");
}else{
//初始化數(shù)組
this.elementData=new Object[initialCapacity];
}
}
/**
* 默認(rèn)初始化
*/
public arraylist(){
this(10);
}
/**
* 根據(jù)一個(gè)集合類初始化
* @param c 一個(gè)必須繼承了Collection接口的類
*/
public arraylist(Collection<? extends E> c){
//初始化
elementData=c.toArray();
size=elementData.length;
//如果不是任意類型的數(shù)組就轉(zhuǎn)換Objec類型
if (elementData.getClass() != Object[].class){
elementData=Arrays.copyOf(elementData,size, Object[].class);
}
}
3個(gè)初始化方法,根據(jù)默認(rèn)大小進(jìn)行數(shù)組的初始化,給定大小初始化和傳遞一個(gè)繼承了Collection集合接口的類進(jìn)行轉(zhuǎn)換賦值初始化
/**
* 擴(kuò)容集合
* @param minCapacity
*/
public void ensureCapacity(int minCapacity){
/** 當(dāng)前數(shù)組的大小 */
int oldCapacity = elementData.length;
if (minCapacity > oldCapacity) {
/**
* oldData 雖然沒有被使用,但是這是關(guān)于內(nèi)存管理的原因和Arrays.copyOf()方法不是線程安全
* oldData在if的生命周期內(nèi)引用elementData這個(gè)變量,所以不會(huì)被GC回收掉
* 當(dāng)Arrays.copyOf()方法在把elementData復(fù)制到newCapacity時(shí),就可以防止新的內(nèi)存或是其他線程分配內(nèi)存是elementData內(nèi)存被侵占修改
* 當(dāng)結(jié)束是離開if,oldData周期就結(jié)束被回收
*/
Object oldData[] = elementData;
int newCapacity = (oldCapacity * 3)/2 + 1; //增加50%+1
if (newCapacity < minCapacity)
newCapacity = minCapacity;
//使用Arrays.copyOf把集合的元素復(fù)制并生成一個(gè)新的數(shù)組
elementData = Arrays.copyOf(elementData, newCapacity);
}
}
這是一個(gè)核心的方法,集合的擴(kuò)容,其實(shí)是對數(shù)組的擴(kuò)容,minCapacity集合的大小,進(jìn)行對比判斷是否應(yīng)該進(jìn)行擴(kuò)容,使用了Arrays.copyOf()方法進(jìn)行擴(kuò)容,
原文有進(jìn)行詳細(xì)的解釋,這個(gè)方法把第一個(gè)參數(shù)的內(nèi)容復(fù)制到一個(gè)新的數(shù)組中,數(shù)組的大小是第二個(gè)參數(shù),并返回一個(gè)新的數(shù)組,關(guān)于oldData的變量上文有詳細(xì)的注釋
/**
* 檢查索引是否出界
* @param index
*/
private void RangeCheck(int index){
if(index > size || index < 0){
throw new IndexOutOfBoundsException("下標(biāo)超出,Index: " + index + ", Size: " +size);
}
}
一個(gè)下標(biāo)的檢索是否出 1 /**
* 添加元素
* 將指定的元素添加到集合的末尾
* @param e 添加的元素
* @return
*/
public boolean add(E e){
ensureCapacity(size+1);
elementData[size]=e;
size++;
return true;
}
添加元素,先進(jìn)行擴(kuò)容,在賦值,然后元素加一,注意 size+1 字段size并沒有加一,這里進(jìn)行的是算術(shù)的運(yùn)算,所以在后面才需要進(jìn)行自增
/**
* 添加元素
* 將元素添加到指定的位置
* @param index 指定的索引下標(biāo)
* @param element 元素
* @return
*/
public boolean add(int index, E element){
RangeCheck(index);
ensureCapacity(size+1);
// 將 elementData中從Index位置開始、長度為size-index的元素,
// 拷貝到從下標(biāo)為index+1位置開始的新的elementData數(shù)組中。
// 即將當(dāng)前位于該位置的元素以及所有后續(xù)元素右移一個(gè)位置。
System.arraycopy(elementData, index, elementData, index+1, size-index);
elementData[index]=element;
size++;//元素加一
return true;
}
這里不同的是 System.arraycopy(elementData, index, elementData, index+1, size-index);
這是一個(gè)c的內(nèi)部方法,詳細(xì)的原文有解釋,這里就不說了,這個(gè)也是整個(gè)ArrayList的核心所在,也Arrays.copyOf()的內(nèi)部實(shí)現(xiàn)原理
/**
* 添加全部元素
* 按照指定collection的迭代器所返回的元素順序,將該collection中的所有元素添加到此列表的尾部。
* @param c
* @return
*/
public boolean addAll(Collection < ? extends E>c){
Object[] newElement=c.toArray();
int elementLength=newElement.length;
ensureCapacity(size+elementLength);
//從newElement 0的下標(biāo)開始,elementLength個(gè)元素,elementData size的下標(biāo)
System.arraycopy(newElement, 0, elementData, size, elementLength);
size+=elementLength;
return elementLength!=0;
}
基本上其他方法都只是根據(jù)不同的情況進(jìn)行不同的處理,比如通過接口把數(shù)據(jù)對象傳遞進(jìn)來然后獲取長度進(jìn)行擴(kuò)容,在把數(shù)據(jù)使用System,arraycopy復(fù)制到新的數(shù)組中
/**
* 指定位置,添加全部元素
* @param index 插入位置的下標(biāo)
* @param c 插入的元素集合
* @return
*/
public boolean addAll(int index, Collection<? extends E> c){
if(index > size || index < 0){
throw new IndexOutOfBoundsException("Index: " + index + ", Size: " +size);
}
Object[] newElement=c.toArray();
int elementLength=newElement.length;
ensureCapacity(size+elementLength);
int numMoved=size-index;
//判斷插入的位置是否在數(shù)組中間
if(numMoved>0){
//把index插入位置的后面的所有元素往后移
//elementData index下標(biāo)開始的numMoved個(gè)元素插入到elementData 的index+elementLength位置
System.arraycopy(elementData, index, elementData, index+elementLength, numMoved);
}
//把newElement里從0開始的elementLength個(gè)元素添加到elementData index開始的位置
System.arraycopy(newElement, 0, elementData, index, elementLength);
size += elementLength;
return elementLength != 0;
}
/**
* 指定下標(biāo)賦值
* @param index
* @param element
* @return
*/
public E set(int index,E element){
RangeCheck(index);
E oldElement=(E)elementData[index];
elementData[index]=element;
return oldElement;
}
/**
* 根據(jù)下標(biāo)取值
* @param index
* @return
*/
public E get(int index){
RangeCheck(index);
return (E)elementData[index];
}
/**
* 根據(jù)下標(biāo)移除元素
* @param index
*/
public E remove(int index){
RangeCheck(index);
E oldElement=(E)elementData[index];
/** 移除的下標(biāo)后面的元素?cái)?shù)量 */
int numMoved=size-index-1;
//如果在數(shù)組范圍內(nèi)就進(jìn)行移動(dòng)
if(numMoved>0)
System.arraycopy(elementData, index+1, elementData, index, numMoved);
//移除
elementData[--size]=null;
return oldElement;
}
/**
* 根據(jù)元素移除
* @param obj
* @return
*/
public boolean remove(Object obj){
//Arraylist允許存放null,所以也要進(jìn)行判斷處理
if(obj==null){
for(int index=0;index<size;index++){
if(elementData[index]==null){
remove(index);
return true;
}
}
}else{
for(int index=0;index<size;index++){
if(obj.equals(elementData[index])){
remove(index);
return true;
}
}
}
return false;
}
/**
* 根據(jù)下標(biāo)移除指定范圍內(nèi)的元素
* @param fromIndex 開始
* @param toIndex 結(jié)束
*/
protected void removeRange(int fromIndex, int toIndex){
RangeCheck(fromIndex);
RangeCheck(toIndex);
//要移動(dòng)的元素?cái)?shù)
int numMoved = size - toIndex;
//把toIndex后面的元素移動(dòng)到fromIndex
System.arraycopy(elementData, toIndex, elementData, fromIndex, numMoved);
//要移除的元素?cái)?shù)量
int newSize=size-(toIndex-fromIndex);
while(size!=newSize){
elementData[--size]=null;
}
}
/**
* 把數(shù)組容量調(diào)整到實(shí)際的容量
*/
public void trimToSize(){
int leng=elementData.length;
if(size<leng){
Object[] old=elementData;
elementData=Arrays.copyOf(elementData, size);
}
}
/**
* 把集合元素轉(zhuǎn)換成數(shù)組
* @return
*/
public Object[] toArray(){
return Arrays.copyOf(elementData, size);
}
public <T>T[] toArray(T[] a){
if(a.length<size){
return (T[]) Arrays.copyOf(elementData,size, a.getClass());
}
//把集合元素復(fù)制到a數(shù)組中
System.arraycopy(elementData, 0, a, 0, size);
if (a.length > size){
for(int index=size;index<a.length;index++){
a[index] = null;
}
}
return a;
}
基本上都是對數(shù)組進(jìn)行操作和使用c的方法進(jìn)行賦值移動(dòng)等,詳細(xì)的可以查看原文,原文中除了那個(gè)私有變量外也沒多少問題,代碼可以完美運(yùn)行,這李要注意的和難點(diǎn)就會(huì)是System,arraycopy和Arrayist.copy()這2個(gè)方法
和在擴(kuò)容方法里oldData這個(gè)變量的使用,這個(gè)變量真的很好,一開始我也不知道為什么要這么使用,在原文的末尾會(huì)進(jìn)行解釋。
以上所述是小編給大家介紹的Java ArrayList 實(shí)現(xiàn)實(shí)例講解,希望對大家有所幫助,如果大家有任何疑問歡迎給我留言,小編會(huì)及時(shí)回復(fù)大家的,在此也非常感謝大家對腳本之家網(wǎng)站的支持!
相關(guān)文章
JavaWeb使用Session和Cookie實(shí)現(xiàn)登錄認(rèn)證
本篇文章主要介紹了JavaWeb使用Session和Cookie實(shí)現(xiàn)登錄認(rèn)證,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下。2017-03-03
Java面試題沖刺第十二天--數(shù)據(jù)庫(2)
這篇文章主要為大家分享了最有價(jià)值的三道數(shù)據(jù)庫面試題,涵蓋內(nèi)容全面,包括數(shù)據(jù)結(jié)構(gòu)和算法相關(guān)的題目、經(jīng)典面試編程題等,感興趣的小伙伴們可以參考一下2021-07-07
SSH框架網(wǎng)上商城項(xiàng)目第20戰(zhàn)之在線支付平臺(tái)
這篇文章主要為大家詳細(xì)介紹了SSH框架網(wǎng)上商城項(xiàng)目第20戰(zhàn)之在線支付平臺(tái),關(guān)于第三方支付的內(nèi)容從本文開始,感興趣的小伙伴們可以參考一下2016-06-06
mybatis類型轉(zhuǎn)換器如何實(shí)現(xiàn)數(shù)據(jù)加解密
這篇文章主要介紹了mybatis類型轉(zhuǎn)換器如何實(shí)現(xiàn)數(shù)據(jù)加解密,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教2021-09-09
Java數(shù)據(jù)結(jié)構(gòu)之隊(duì)列(動(dòng)力節(jié)點(diǎn)Java學(xué)院整理)
隊(duì)列(Queue)是只允許在一端進(jìn)行插入,而在另一端進(jìn)行刪除的運(yùn)算受限的線性表。 這篇文章詳細(xì)給大家介紹了java數(shù)據(jù)結(jié)構(gòu)之隊(duì)列,感興趣的朋友跟隨小編一起學(xué)習(xí)吧2017-04-04
java中CompletableFuture異步執(zhí)行方法
本文主要介紹了java中CompletableFuture異步執(zhí)行方法,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧2023-06-06
Java獲取網(wǎng)絡(luò)文件并插入數(shù)據(jù)庫的代碼
抓取各大網(wǎng)站的數(shù)據(jù)插入數(shù)據(jù)庫,這樣就不用為沒有數(shù)據(jù)而煩惱了2010-06-06
SpringBoot整合EasyExcel?3.x的完整示例
EasyExcel 是一個(gè)基于 Java 的、快速、簡潔、解決大文件內(nèi)存溢出的 Excel 處理工具,它能讓你在不用考慮性能、內(nèi)存的等因素的情況下,快速完成 Excel 的讀、寫等功能,這篇文章主要介紹了SpringBoot整合EasyExcel3.x的過程,需要的朋友可以參考下2023-07-07

