ArrayList源码解析
前言ArrayList源码解析ArrayList简介ArrayList核心源码并发修改异常 ConcurrentModificationExceptionforeach循环为什么会出现ConcurrentModificationException异常
前言
这里首先建议小伙伴们自己去阅读源码,然后有条件的可以自己手写一遍,先看,然后根据自己的感觉去写,不会的可以抄源码,但是要知道这段代码的作用是什么。
ArrayList源码解析
ArrayList简介
ArrayList 的底层是数组队列,相当于动态数组。与 Java 中的数组相比,它的容量能动态增长。在添加大量元素前,应用程序可以使用ensureCapacity操作来增加 ArrayList 实例的容量。这可以减少递增式再分配的数量。 它继承于 AbstractList,实现了 List, RandomAccess, Cloneable, java.io.Serializable 这些接口。
ArrayList 继承了AbstractList,实现了List。它是一个数组队列,它将数组队列中,一部分公共方法抽取出来,进行抽象实现,这就使得用户在自定义实现集合的时候,可以不用修改核心代码,只要添加自己需要的部分就行了。
ArrayList 实现了RandomAccess 接口, RandomAccess 是一个标志接口,表明实现这个这个接口的 List 集合是支持快速随机访问的。在 ArrayList 中,我们即可以通过元素的序号快速获取元素对象,这就是快速随机访问。
ArrayList 实现了Cloneable 接口,即覆盖了函数 clone(),能被克隆(没有实现这个接口调用Object.clone()方法会抛出不可克隆异常)。
ArrayList 实现java.io.Serializable 接口,这意味着ArrayList支持序列化,能通过序列化去传输。
和 Vector 不同,ArrayList 中的操作不是线程安全的!所以,建议在单线程中才使用 ArrayList,而在多线程中可以选择 Vector 或者 CopyOnWriteArrayList。
ArrayList核心源码
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess
, Cloneable
, java
.io
.Serializable
{
private static final long serialVersionUID
= 8683452581122892189L
;
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
;
public ArrayList(int initialCapacity
) {
if (initialCapacity
> 0) {
this.elementData
= new Object[initialCapacity
];
} else if (initialCapacity
== 0) {
this.elementData
= EMPTY_ELEMENTDATA
;
} else {
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity
);
}
}
public ArrayList() {
this.elementData
= DEFAULTCAPACITY_EMPTY_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 {
this.elementData
= EMPTY_ELEMENTDATA
;
}
}
public void trimToSize() {
modCount
++;
if (size
< elementData
.length
) {
elementData
= (size
== 0)
? EMPTY_ELEMENTDATA
: Arrays
.copyOf(elementData
, size
);
}
}
public void ensureCapacity(int minCapacity
) {
int minExpand
= (elementData
!= DEFAULTCAPACITY_EMPTY_ELEMENTDATA
)
? 0
: DEFAULT_CAPACITY
;
if (minCapacity
> minExpand
) {
ensureExplicitCapacity(minCapacity
);
}
}
private void ensureCapacityInternal(int minCapacity
) {
if (elementData
== DEFAULTCAPACITY_EMPTY_ELEMENTDATA
) {
minCapacity
= Math
.max(DEFAULT_CAPACITY
, minCapacity
);
}
ensureExplicitCapacity(minCapacity
);
}
private void ensureExplicitCapacity(int minCapacity
) {
modCount
++;
if (minCapacity
- elementData
.length
> 0)
grow(minCapacity
);
}
private static final int MAX_ARRAY_SIZE
= Integer
.MAX_VALUE
- 8;
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
);
elementData
= Arrays
.copyOf(elementData
, newCapacity
);
}
private static int hugeCapacity(int minCapacity
) {
if (minCapacity
< 0)
throw new OutOfMemoryError();
return (minCapacity
> MAX_ARRAY_SIZE
) ?
Integer
.MAX_VALUE
:
MAX_ARRAY_SIZE
;
}
public int size() {
return size
;
}
public boolean isEmpty() {
return size
== 0;
}
public boolean contains(Object o
) {
return indexOf(o
) >= 0;
}
public int indexOf(Object o
) {
if (o
== null
) {
for (int i
= 0; i
< size
; i
++)
if (elementData
[i
]==null
)
return i
;
} else {
for (int i
= 0; i
< size
; i
++)
if (o
.equals(elementData
[i
]))
return i
;
}
return -1;
}
public int lastIndexOf(Object o
) {
if (o
== null
) {
for (int i
= size
-1; i
>= 0; i
--)
if (elementData
[i
]==null
)
return i
;
} else {
for (int i
= size
-1; i
>= 0; i
--)
if (o
.equals(elementData
[i
]))
return i
;
}
return -1;
}
public Object
clone() {
try {
ArrayList
<?> v
= (ArrayList
<?>) super.clone();
v
.elementData
= Arrays
.copyOf(elementData
, size
);
v
.modCount
= 0;
return v
;
} catch (CloneNotSupportedException e
) {
throw new InternalError(e
);
}
}
public Object
[] toArray() {
return Arrays
.copyOf(elementData
, size
);
}
@SuppressWarnings("unchecked")
public <T> T
[] toArray(T
[] a
) {
if (a
.length
< size
)
return (T
[]) Arrays
.copyOf(elementData
, size
, a
.getClass());
System
.arraycopy(elementData
, 0, a
, 0, size
);
if (a
.length
> size
)
a
[size
] = null
;
return a
;
}
@SuppressWarnings("unchecked")
E
elementData(int index
) {
return (E
) elementData
[index
];
}
public E
get(int index
) {
rangeCheck(index
);
return elementData(index
);
}
public E
set(int index
, E element
) {
rangeCheck(index
);
E oldValue
= elementData(index
);
elementData
[index
] = element
;
return oldValue
;
}
public boolean add(E e
) {
ensureCapacityInternal(size
+ 1);
elementData
[size
++] = e
;
return true;
}
public void add(int index
, E element
) {
rangeCheckForAdd 对index进行界限检查;
rangeCheckForAdd(index
);
ensureCapacityInternal(size
+ 1);
System
.arraycopy(elementData
, index
, elementData
, index
+ 1,
size
- index
);
elementData
[index
] = element
;
size
++;
}
public E
remove(int index
) {
rangeCheck(index
);
modCount
++;
E oldValue
= elementData(index
);
int numMoved
= size
- index
- 1;
if (numMoved
> 0)
System
.arraycopy(elementData
, index
+1, elementData
, index
,
numMoved
);
elementData
[--size
] = null
;
return oldValue
;
}
public boolean remove(Object o
) {
if (o
== null
) {
for (int index
= 0; index
< size
; index
++)
if (elementData
[index
] == null
) {
fastRemove(index
);
return true;
}
} else {
for (int index
= 0; index
< size
; index
++)
if (o
.equals(elementData
[index
])) {
fastRemove(index
);
return true;
}
}
return false;
}
private void fastRemove(int index
) {
modCount
++;
int numMoved
= size
- index
- 1;
if (numMoved
> 0)
System
.arraycopy(elementData
, index
+1, elementData
, index
,
numMoved
);
elementData
[--size
] = null
;
}
public void clear() {
modCount
++;
for (int i
= 0; i
< size
; i
++)
elementData
[i
] = null
;
size
= 0;
}
public boolean addAll(Collection
<? extends E> c
) {
Object
[] a
= c
.toArray();
int numNew
= a
.length
;
ensureCapacityInternal(size
+ numNew
);
System
.arraycopy(a
, 0, elementData
, size
, numNew
);
size
+= numNew
;
return numNew
!= 0;
}
public boolean addAll(int index
, Collection
<? extends E> c
) {
rangeCheckForAdd(index
);
Object
[] a
= c
.toArray();
int numNew
= a
.length
;
ensureCapacityInternal(size
+ numNew
);
int numMoved
= size
- index
;
if (numMoved
> 0)
System
.arraycopy(elementData
, index
, elementData
, index
+ numNew
,
numMoved
);
System
.arraycopy(a
, 0, elementData
, index
, numNew
);
size
+= numNew
;
return numNew
!= 0;
}
protected void removeRange(int fromIndex
, int toIndex
) {
modCount
++;
int numMoved
= size
- toIndex
;
System
.arraycopy(elementData
, toIndex
, elementData
, fromIndex
,
numMoved
);
int newSize
= size
- (toIndex
-fromIndex
);
for (int i
= newSize
; i
< size
; i
++) {
elementData
[i
] = null
;
}
size
= newSize
;
}
private void rangeCheck(int index
) {
if (index
>= size
)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index
));
}
private void rangeCheckForAdd(int index
) {
if (index
> size
|| index
< 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index
));
}
private String
outOfBoundsMsg(int index
) {
return "Index: "+index
+", Size: "+size
;
}
public boolean removeAll(Collection
<?> c
) {
Objects
.requireNonNull(c
);
return batchRemove(c
, false);
}
public boolean retainAll(Collection
<?> c
) {
Objects
.requireNonNull(c
);
return batchRemove(c
, true);
}
private boolean batchRemove(Collection
<?> c
, boolean complement
) {
final Object
[] elementData
= this.elementData
;
int r
= 0, w
= 0;
boolean modified
= false;
try {
for (; r
< size
; r
++)
if (c
.contains(elementData
[r
]) == complement
)
elementData
[w
++] = elementData
[r
];
} finally {
if (r
!= size
) {
System
.arraycopy(elementData
, r
,
elementData
, w
,
size
- r
);
w
+= size
- r
;
}
if (w
!= size
) {
for (int i
= w
; i
< size
; i
++)
elementData
[i
] = null
;
modCount
+= size
- w
;
size
= w
;
modified
= true;
}
}
return modified
;
}
public ListIterator
<E> listIterator(int index
) {
if (index
< 0 || index
> size
)
throw new IndexOutOfBoundsException("Index: "+index
);
return new ListItr(index
);
}
public ListIterator
<E> listIterator() {
return new ListItr(0);
}
public Iterator
<E> iterator() {
return new Itr();
}
}
并发修改异常 ConcurrentModificationException
观察源码我们可以发现,List集合中有一个modCount变量,每次对集合进行修改都会将modCount++,我们看一个小例子。
public class iteratorTest {
public static void main(String
[] args
) {
List
<Integer> list
= new ArrayList<>(Arrays
.asList(1,2,3,4,5,6,7,8,9));
for (int i
= 0; i
< 30; i
++) {
if (i
% 2 == 0) {
new Thread(() -> list
.add(9)).start();
} else {
final int b
= i
% list
.size();
new Thread(() -> list
.set(b
, 8)).start();
}
}
for (Integer i
: list
) {
System
.out
.println(i
);
}
}
}
我首先开了30个线程对list集合进行了并发的修改,然后最后对list进行遍历,遍历的结果如下: 我们可以看到报错了,ConcurrentModificationException并发修改异常。然后我们再用另一种方式对list进行遍历:
public class iteratorTest {
public static void main(String
[] args
) throws IOException
{
List
<Integer> list
= new ArrayList<>(Arrays
.asList(1,2,3,4,5,6,7,8,9));
for (int i
= 0; i
< 30; i
++) {
if (i
% 2 == 0) {
new Thread(() -> list
.add(9)).start();
} else {
final int b
= i
% list
.size();
new Thread(() -> list
.set(b
, 8)).start();
}
}
for (int i
= 0; i
< list
.size(); i
++) {
System
.out
.println(list
.get(i
));
}
}
}
结果: 没有报错。下面我们来解释一下为什么会这样。
foreach循环
java的foreach循环其实就是将被循环的数组或者集合转成一个iterator迭代器进行迭代,此时是不允许对数组或集合进行修改的(add,remove),如果进行修改,就会报ConcurrentModificationException异常
public static void main(String
[] args
) throws IOException
{
List
<Integer> list
= new ArrayList<>(Arrays
.asList(1,2,3,4,5,6,7,8,9));
for (Integer integer
: list
) {
list
.add(5);
}
报错如下: 和并发修改发生的异常一样。
为什么会出现ConcurrentModificationException异常
我们来看一下迭代器的方法: next():
private class Itr implements Iterator<E> {
int cursor
;
int lastRet
= -1;
int expectedModCount
= modCount
;
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;
return (E
) elementData
[lastRet
= i
];
}
checkForComodification():
final void checkForComodification() {
if (modCount
!= expectedModCount
)
throw new ConcurrentModificationException();
}
看完上面这两个方法,大概就能明白为什么在迭代遍历的时候对集合修改会引发异常(也不能说是在遍历的时候,是遇到checkForComodification()方法)。我们知道,每次对集合的操作会让modCount++,那当迭代器调用next()方法时,它会首先进行checkForComodification()检查,如果expectedModCount与modCount的值不一样,证明集合已经被并发修改了,就会抛出并发修改的异常。