Java集合-ArrayList
前言
ArrayList作为常用的数据容器有必要学习下,这里会介绍ArrayList的特点,以及其实现原理
ArrayList上帝视角
ArrayList 继承AbstractList,实现了List, RandomAccess, Cloneable, java.io.Serializable接口
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable
-
继承AbstractList那么就具有添加、修改、删除、遍历等功能。
-
RandomAccess其实是一个空接口,RandomAccess主要是起到标记做用,实现了这个接口的那么使用for循环遍历快于迭代器;比如LinkedList没有实现,其迭代器循环快于for循环
-
其次ArrayList可以进行clone()和序列化。
-
ArrayList的实现方式基于数组,默认初始值为0,然后扩容长度的算法为
int newCapacity = oldCapacity + (oldCapacity >> 1);
新的大小是原来的1.5倍 -
ArrayList使用索引进行随机访问最快,Iterator迭代最慢
ArrayList源码介绍(jdk-11.0.2)
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 = {};
//用于存放ArrayList数据的数组
transient Object[] elementData; // non-private to simplify nested class access
//数组中包含元素的个数
private int size;
//数组所能保存的最大长度
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
//接下来是三个构造方法
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) {
//如果传入的集合有值则,使用Arrays.copyof()复制出来
if (elementData.getClass() != Object[].class)
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
...
}
boolean add(E e)
public boolean add(E e) {
//表示此列表在结构上被修改的次数,当数据添加或者删除的时候,数据就会改变
modCount++;
add(e, elementData, size);
return true;
}
private void add(E e, Object[] elementData, int s) {
//如果数组中包含元素个数和数组长度相等,就调用grow(),返回一个扩容后的数组
if (s == elementData.length)
elementData = grow();
//将数据添加到数组中
elementData[s] = e;
//当前数组长度+1
size = s + 1;
}
//如果当前size和elementData.length相等;假如size=0
private Object[] grow() {
return grow(size + 1);
}
private Object[] grow(int minCapacity) {
return elementData = Arrays.copyOf(elementData,
newCapacity(minCapacity));
}
private int newCapacity(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//旧集合长度+oldCapacity除以2,oldCapacity>>1 相当于oldCapacity除以2
int newCapacity = oldCapacity + (oldCapacity >> 1);
//当第一次扩容的时候,newCapacity=0;minCapacity=1;所有0-1<=0
if (newCapacity - minCapacity <= 0) {
//当第一次扩容的时候,elementData是为空的,所以返回DEFAULT_CAPACITY和minCapacity中较大的DEFAULT_CAPACITY
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
return Math.max(DEFAULT_CAPACITY, minCapacity);
//如果minCapacity < 0抛出异常 ,不然就返回minCapacity
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return minCapacity;
}
//新的newCapacity是否小于等于数组能容纳的最大值,小于那么就使用这个newCapacity,如果已经大于那么使用minCapacity再和Integer.MAX_VALUE做比较
return (newCapacity - MAX_ARRAY_SIZE <= 0)
? newCapacity
: hugeCapacity(minCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
//如果已经大于Integer的最大值,就使用Integer.MAX_VALUE,如果还没有,就使用数组能容纳的MAX_ARRAY_SIZE()
return (minCapacity > MAX_ARRAY_SIZE)
? Integer.MAX_VALUE
: MAX_ARRAY_SIZE;
}
E get(int index)
public E get(int index) {
Objects.checkIndex(index, size);
return elementData(index);
}
E elementData(int index) {
return (E) elementData[index];
}
E remove(int index)
public E remove(int index) {
Objects.checkIndex(index, size);
final Object[] es = elementData;
@SuppressWarnings("unchecked") E oldValue = (E) es[index];
fastRemove(es, index);
return oldValue;
}
private void fastRemove(Object[] es, int i) {
modCount++;
final int newSize;
if ((newSize = size - 1) > i)
System.arraycopy(es, i + 1, es, i, newSize - i);
es[size = newSize] = null;
}
ArrayList遍历
ArrayList<String> arrayList = new ArrayList();
int size = arrayList.size();
for (int i = 0; i < size; i++) {
arrayList.get(i);
}
Iterator<String> iterator = arrayList.iterator();
while (iterator.hasNext()) {
String value = iterator.next();
}
for (String value : arrayList) {
}
根据测试,ArrayList使用索引访问效率最高,使用Iterator最慢。所以在Collections工具类中,根据List是否实现RandomAccess或者集合长度小于5000来判断选择是使用index遍历还是迭代器遍历(LinkedList没有实现RandomAccess接口)
public class Collections {
...
public static <T>
int binarySearch(List<? extends Comparable<? super T>> list, T key) {
if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
return Collections.indexedBinarySearch(list, key);
else
return Collections.iteratorBinarySearch(list, key);
}
...
}
Iterator的fail-fast机制
调用迭代器方法的时候,返回一个Iterator,实现Iterator的是ArrayList中的Itr类;这里我们要介绍的是Iterator的fail-fast机制,当多线程操作同一个集合的时候就有可能触发Collection的fail-fast错误机制,机制的作用就是在使用不当的情况下尽量给出一些提示算是一种优化。接下来我们看源码
public Iterator<E> iterator() {
return new Itr();
}
private class Itr implements Iterator<E> {
int cursor; // 下一个要返回的元素的索引
int lastRet = -1; // 返回的最后一个元素的索引; -1 if no such
int expectedModCount = modCount;//当前集合被操作过的次数,删除和添加都会改变这个值
// prevent creating a synthetic constructor
Itr() {}
//判断还有没有下一个元素,下一个元素的索引不等于集合长度就返回true,
public boolean hasNext() {
return cursor != size;
}
//next()方法返回一个索引
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
//如果hasNext()成功,但在next()的时候i >= size那说明中间有数据被删除,抛出异常
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
//再判断当前游标是否大于集合长度
if (i >= elementData.length)
throw new ConcurrentModificationException();
//上面都没问题,再取出数据返回之前,将当前游标指向下一个游标位置,然后回到上面在hasNext()判断是否相等
cursor = i + 1;
return (E) elementData[lastRet = i];
}
//在迭代器中,要调用remove(),得先调用next();并且不能连续调用两次删除,
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.remove(lastRet);
cursor = lastRet;
//不能连续调用两次删除,删除后lastRet<0,remove()进入后就判断了astRet < 0,则会抛出异常
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
public void forEachRemaining(Consumer<? super E> action) {
Objects.requireNonNull(action);
final int size = ArrayList.this.size;
int i = cursor;
if (i < size) {
final Object[] es = elementData;
if (i >= es.length)
throw new ConcurrentModificationException();
for (; i < size && modCount == expectedModCount; i++)
action.accept(elementAt(es, i));
// update once at end to reduce heap write traffic
cursor = i;
lastRet = i - 1;
checkForComodification();
}
}
//全局变量modCount!=expectedModCount局部变量,那么直接抛出异常,如果不等于说明,集合在迭代的过程中,删除或者添加了其他元素,那肯定造成迭代数据错误
final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
}
避免出现fail-fast机制可以使用多线程集合类CopyOnWriteArrayList进行替换