先上圖:
ArrayList應(yīng)該是經(jīng)常用到的一個(gè)集合容器了,可以說(shuō)java集合的其他容器都沒(méi)用過(guò)也一定用過(guò)ArrayList笨农。ArrayList內(nèi)部是維護(hù)一個(gè)數(shù)組來(lái)存儲(chǔ)元素就缆。
1.5倍擴(kuò)容,默認(rèn)10個(gè)元素谒亦,在添加第一個(gè)元素時(shí)才創(chuàng)建一個(gè)大小為10的數(shù)組竭宰。
源碼不多做介紹了空郊。下面說(shuō)一個(gè)重點(diǎn):
遍歷問(wèn)題
先拋出4個(gè)遍歷,如代碼所示:
ArrayList<String> arrayList = new ArrayList<>();
resetArrayList(arrayList);
//1.正序刪除
for (int i = 0; i < arrayList.size(); i++) {
String re = arrayList.remove(i);
System.out.println("re=" + re);
}
System.out.println("1.arrayList.size=" + arrayList.size());
resetArrayList(arrayList);
//2.后序刪除
for (int i = arrayList.size() - 1; i >= 0; i--) {
arrayList.remove(i);
}
System.out.println("2.arrayList.size=" + arrayList.size());
resetArrayList(arrayList);
//3.增強(qiáng)for循環(huán)刪除
// for (String s : arrayList) {
// arrayList.remove(s);
// }
System.out.println("3.arrayList.size=" + arrayList.size());
//4.使用迭代器
resetArrayList(arrayList);
Iterator<String> it = arrayList.iterator();
while (it.hasNext()) {
it.next();
it.remove();
}
System.out.println("4.arrayList.size=" + arrayList.size());
// Iterator<String> listIt = arrayList.listIterator(1);
ArrayList元素重置
public static void resetArrayList(ArrayList<String> arrayList) {
arrayList.clear();
for (int i = 0; i < 3; i++) {
arrayList.add("(" + i);
}
}
打印結(jié)果:
re=(0
re=(2
1.arrayList.size=1
2.arrayList.size=0
3.arrayList.size=3
4.arrayList.size=0
分析1:
現(xiàn)象:循環(huán)跑完后arrayList中還有一個(gè)元素切揭,“ (1 ”元素沒(méi)有被刪除
原因:刪除第一個(gè)元素狞甚,就是arrayList內(nèi)部的數(shù)組的第0下標(biāo)的元素,后續(xù)的元素整體向前移動(dòng)廓旬,也就是“(1”哼审、“(2”,移動(dòng)到0、1的位置孕豹,當(dāng)?shù)诙窝h(huán)到來(lái)時(shí)涩盾,i已經(jīng)是1了,此時(shí)對(duì)應(yīng)的值就是“(2”,所以“(2”被移除励背,移除元素的同時(shí)春霍,arrayList.size值也會(huì)跟著變化,因此不會(huì)發(fā)生越界異常叶眉,循環(huán)終止址儒。
顯然,實(shí)際上很少有這種需求竟闪。
分析2:
現(xiàn)象: 打印正常
原因: 從后向前刪除杖狼,不會(huì)使數(shù)組發(fā)生整體位移炼蛤,arrayList內(nèi)部移除元素時(shí)理朋,如果是最后一位元素直接置空,不需要移動(dòng)元素嗽上。i--也能對(duì)得上最后一位元素。
分析3:
現(xiàn)象: 直接拋出異常ConcurrentModificationException
原因: 看下方源碼兽愤,曾強(qiáng)for循環(huán)就是迭代器循環(huán),當(dāng)取得第一個(gè)元素并移除浅萧,去下一個(gè)元素就會(huì)檢查出有改動(dòng)哲思,因此拋出異常。
public Iterator<E> iterator() {
// 使用增強(qiáng)for循環(huán)就是獲得一個(gè)迭代器
// 這里直接new一個(gè)返回
return new Itr();
}
/**
* An optimized version of AbstractList.Itr
*/
private class Itr implements Iterator<E> {
//這記錄操作數(shù)棚赔,modCount是記錄操作數(shù)徘郭,ArrayList添加丧肴、移除元素都++
int expectedModCount = modCount;
@SuppressWarnings("unchecked")
public E next() {
//s就是從這個(gè)方法返回的,這里檢查如果操作數(shù)不等于自己的記錄芋浮,說(shuō)明元素有添加或移除,
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[lastRet = i];
}
final void checkForComodification() {
//操作數(shù)不一致江醇,拋出ConcurrentModificationException
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
分析4:
現(xiàn)象: 打印正常
原因: 看下方代碼分析,
1>hasNext方法判斷是否有下一個(gè)元素
2>next取當(dāng)前元素
這里沒(méi)有出ConcurrentModificationException是remove方法中重新記錄了modCount
3>remove移除
public Iterator<E> iterator() {
return new Itr();
}
/**
* An optimized version of AbstractList.Itr
*/
private class Itr implements Iterator<E> {
int cursor; // index of next element to return
int lastRet = -1; // index of last element returned; -1 if no such
int expectedModCount = modCount;
Itr() {}
public boolean hasNext() {
return cursor != size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
//游標(biāo)向后移動(dòng)
cursor = i + 1;
//lastRet記錄當(dāng)前元素的下標(biāo)
return (E) elementData[lastRet = i];
}
public void remove() {
//這里沒(méi)有走next方法會(huì)直接拋出狀態(tài)不合法的異常
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
//移除當(dāng)前元素
ArrayList.this.remove(lastRet);
//因?yàn)楫?dāng)前元素被移除陶夜,后續(xù)的元素會(huì)占用這個(gè)位置裆站,游標(biāo)要移回來(lái)
cursor = lastRet;
//lastRet重新賦值-1,假如這里沒(méi)有賦值-1宏胯,remove這里調(diào)用多次的話(huà)就會(huì)刪除多個(gè)元素
lastRet = -1;
//移除元素后modCount有變,重新記錄
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
如果想從指定下標(biāo)開(kāi)始做移除操作可以使用:
//從下標(biāo)1開(kāi)始
Iterator<String> listIt = arrayList.listIterator(1);
ConcurrentModificationException肩袍,這個(gè)異常叫并發(fā)修改異常,既然叫并發(fā)氛赐,那和多線(xiàn)程有關(guān)系么?
個(gè)人認(rèn)為是無(wú)直接關(guān)系
從代碼設(shè)計(jì)角度看滓侍,是為了數(shù)組的唯一,防止數(shù)組正在使用時(shí)被修改從而取到的元素不正確撩笆,如果不能保證數(shù)組的正確,直接拋個(gè)異常夕冲〔图茫看著辦吧。
間接關(guān)系分析:拋出異常的判斷是迭代器記錄與ArrayList記錄的不一致絮姆,多線(xiàn)程大概率是多個(gè)迭代器在操作秩霍,比如其中一個(gè)迭代器移除蚁阳,那modCount改變,那另一個(gè)迭代器操作時(shí)就會(huì)發(fā)現(xiàn)自己記錄的不一致螺捐,直接拋異常。
還有定血,ArrayList本就線(xiàn)程不安全的。
總結(jié):ArrayList就是維護(hù)一個(gè)數(shù)組的容器澜沟,包括如何擴(kuò)容、如何迭代遍歷以及增刪改查茫虽。