先上图:
![](https://img.haomeiwen.com/i13321205/ea22c0a77e4dd939.png)
ArrayList应该是经常用到的一个集合容器了,可以说java集合的其他容器都没用过也一定用过ArrayList。ArrayList内部是维护一个数组来存储元素。
![](https://img.haomeiwen.com/i13321205/0a8c1b562f6e16a4.png)
1.5倍扩容,默认10个元素,在添加第一个元素时才创建一个大小为10的数组。
源码不多做介绍了。下面说一个重点:
遍历问题
先抛出4个遍历,如代码所示:
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.增强for循环删除
// 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);
}
}
打印结果:
re=(0
re=(2
1.arrayList.size=1
2.arrayList.size=0
3.arrayList.size=3
4.arrayList.size=0
分析1:
现象:循环跑完后arrayList中还有一个元素,“ (1 ”元素没有被删除
原因:删除第一个元素,就是arrayList内部的数组的第0下标的元素,后续的元素整体向前移动,也就是“(1”、“(2”,移动到0、1的位置,当第二次循环到来时,i已经是1了,此时对应的值就是“(2”,所以“(2”被移除,移除元素的同时,arrayList.size值也会跟着变化,因此不会发生越界异常,循环终止。
显然,实际上很少有这种需求。
分析2:
现象: 打印正常
原因: 从后向前删除,不会使数组发生整体位移,arrayList内部移除元素时,如果是最后一位元素直接置空,不需要移动元素。i--也能对得上最后一位元素。
分析3:
现象: 直接抛出异常ConcurrentModificationException
原因: 看下方源码,曾强for循环就是迭代器循环,当取得第一个元素并移除,去下一个元素就会检查出有改动,因此抛出异常。
public Iterator<E> iterator() {
// 使用增强for循环就是获得一个迭代器
// 这里直接new一个返回
return new Itr();
}
/**
* An optimized version of AbstractList.Itr
*/
private class Itr implements Iterator<E> {
//这记录操作数,modCount是记录操作数,ArrayList添加、移除元素都++
int expectedModCount = modCount;
@SuppressWarnings("unchecked")
public E next() {
//s就是从这个方法返回的,这里检查如果操作数不等于自己的记录,说明元素有添加或移除,
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() {
//操作数不一致,抛出ConcurrentModificationException
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
分析4:
现象: 打印正常
原因: 看下方代码分析,
1>hasNext方法判断是否有下一个元素
2>next取当前元素
这里没有出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();
//游标向后移动
cursor = i + 1;
//lastRet记录当前元素的下标
return (E) elementData[lastRet = i];
}
public void remove() {
//这里没有走next方法会直接抛出状态不合法的异常
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
//移除当前元素
ArrayList.this.remove(lastRet);
//因为当前元素被移除,后续的元素会占用这个位置,游标要移回来
cursor = lastRet;
//lastRet重新赋值-1,假如这里没有赋值-1,remove这里调用多次的话就会删除多个元素
lastRet = -1;
//移除元素后modCount有变,重新记录
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
如果想从指定下标开始做移除操作可以使用:
//从下标1开始
Iterator<String> listIt = arrayList.listIterator(1);
ConcurrentModificationException,这个异常叫并发修改异常,既然叫并发,那和多线程有关系么?
个人认为是无直接关系
从代码设计角度看,是为了数组的唯一,防止数组正在使用时被修改从而取到的元素不正确,如果不能保证数组的正确,直接抛个异常。看着办吧。
间接关系分析:抛出异常的判断是迭代器记录与ArrayList记录的不一致,多线程大概率是多个迭代器在操作,比如其中一个迭代器移除,那modCount改变,那另一个迭代器操作时就会发现自己记录的不一致,直接抛异常。
还有,ArrayList本就线程不安全的。
总结:ArrayList就是维护一个数组的容器,包括如何扩容、如何迭代遍历以及增删改查。
网友评论