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 = {}; //默认空数组,用来和EMPTY_ELEMENTDATA区分什么时候扩容多少 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; //存储ArrayList元素的数组缓冲区,ArrayList的容量是此数组缓冲区的长度,添加第一个元素时,*将扩展为DEFAULT_CAPACITY transient Object[] elementData; //ArrayList的大小,也就是包含的元素数 private int size; public ArrayList(int initialCapacity) { if (initialCapacity > 0) { //初始容量大于0 this.elementData = new Object[initialCapacity]; //生成一个新的存储数组 } else if (initialCapacity == 0) { //初始容量等于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; } } }
从上述源码中,我们可以得到以下信息:源码基于JDK1.8
1、implements List<E>, RandomAccess(可以随机访问), Cloneable, java.io.Serializable
2、初始容量10,有看过大佬博客说,默认创建ArrayList的时候,调用默认构造器this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA,此时初始容量为0,而不是10
我的理解:
默认构造器注解:
Constructs an empty list with an initial capacity of ten
构造一个初始容量为10的空集合
elementData的注解如下:
The capacity of the ArrayList is the length of this array buffer. Any empty ArrayList with elementData ==
DEFAULTCAPACITY_EMPTY_ELEMENTDATA will be expanded to DEFAULT_CAPACITY when the first element is added
第一次添加元素,容量会变成DEFAULT_CAPACITY,也就是初始容量
所以,只是容量为0,初始容量DEFAULT_CAPACITY是final类型,永远为10,和elementData具体等于什么没关系,容量Capacity才会变
3、并且通过transient Object[] elementData保存数据
add()添加数据
public boolean add(E e) { ensureCapacityInternal(size + 1); //确保容量 elementData[size++] = e; //在数组尾部添加数据 return true; }
private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { modCount++; //修改次数+1 if (minCapacity - elementData.length > 0) //如果最小容量大于数组的长度,进行扩容 grow(minCapacity); }
grow()扩容
private void grow(int minCapacity) { int oldCapacity = elementData.length; //oldCapacity当前数组的长度 int newCapacity = oldCapacity + (oldCapacity >> 1); //新容量=oldCapacity*1.5 if (newCapacity - minCapacity < 0) //newCapacity < 最小容量,赋值 newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) //大于最大容量,newCapacity赋值为Integer最大值0x7fffffff newCapacity = hugeCapacity(minCapacity); elementData = Arrays.copyOf(elementData, newCapacity); //通过Arrays重新生成一个新容量的数组,包含之前数组的数据,赋值原数组 }
remove()删除数据
1、按照数组下标删除
public E remove(int index) { rangeCheck(index); //检查数组是否下标越界 modCount++; E oldValue = elementData(index); //取出index位置的数据 int numMoved = size - index - 1; // if (numMoved > 0)
//将index后面的所有元素,都前移一位 System.arraycopy(elementData, index+1, elementData, index, numMoved); elementData[--size] = null; //最后一位置为null return oldValue; //返回原来的老数据 }
2、按照元素删除
public boolean remove(Object o) { if (o == null) { for (int index = 0; index < size; index++) if (elementData[index] == null) { //删除第一个null fastRemove(index); return true; } } else { for (int index = 0; index < size; index++) if (o.equals(elementData[index])) { //删除第一个和object相同的数据 fastRemove(index); return true; } } return false; }
都是找个第一个和参数匹配的数据,然后执行的代码和第一种方式相同
复制方法:public static void arraycopy(Object src, int srcPos, Object dest, int destPos, int length)
Object src : 原数组
int srcPos : 从元数据的起始位置开始
Object dest : 目标数组
int destPos : 目标数组的开始起始位置
int length : 要copy的数组的长度
set():
public E set(int index, E element) { rangeCheck(index); //检查是否下标越界 E oldValue = elementData(index); //获取原值 elementData[index] = element; //覆盖原值 return oldValue; //返回原值 }
get():
public E get(int index) { rangeCheck(index); return elementData(index); }
indexOf():
public int indexOf(Object o) { //和contain()一样,整体就是遍历是否包含该元素,然后返回下标,如果不包含,返回-1 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; }
size():和elementData的长度没有关系,指的是实际包含元素的个数
public int size() { return size; }
isEmpty():是否为空
public boolean isEmpty() { return size == 0; }
trimToSize():缩减集合的容量为size,也就是剔除空节点
public void trimToSize() { modCount++; if (size < elementData.length) { elementData = (size == 0) ? EMPTY_ELEMENTDATA : Arrays.copyOf(elementData, size); } }
ArrayList的for循环:
for、foreach、Iterator,for效率最好,Iterator效率最差
只有for循环中调用add()和remove(),才不会发生java.util.ConcurrentModificationException异常,其他两种都会,set()不会出现异常。
因为add() remove()都会执行modCount++
ConcurrentModificationException主要为了防止线程并发修改的,因为ArrayList是线程不安全的,做了实现fast-fail机制
public static void main(String[] args) throws IOException { ArrayList<String> list = new ArrayList<>(); list.add("abc"); list.add("def"); list.forEach(s -> { list.add(s); list.remove(s); }); Iterator iterator = list.iterator(); while (iterator.hasNext()) { String s = (String)iterator.next(); list.remove(iterator.next()); } }
输出结果:java.util.ConcurrentModificationException
Itr:
public Iterator<E> iterator() { return new Itr(); }
ArrayList实现了自定义的迭代器
private class Itr implements Iterator<E> { int cursor; //游标,下个元素的索引 int lastRet = -1; //最后一个元素的索引,如果没有返回-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]; } public void remove() { if (lastRet < 0) throw new IllegalStateException(); checkForComodification(); try { ArrayList.this.remove(lastRet); //删除当前元素 cursor = lastRet; //cursor指的是下个元素的索引,由于删除一个元素,所以还是等于当前位置 lastRet = -1; expectedModCount = modCount; } catch (IndexOutOfBoundsException ex) { throw new ConcurrentModificationException(); } } @Override @SuppressWarnings("unchecked") public void forEachRemaining(Consumer<? super E> consumer) { Objects.requireNonNull(consumer); final int size = ArrayList.this.size; int i = cursor; if (i >= size) { return; } final Object[] elementData = ArrayList.this.elementData; if (i >= elementData.length) { throw new ConcurrentModificationException(); } while (i != size && modCount == expectedModCount) { consumer.accept((E) elementData[i++]); } // update once at end of iteration to reduce heap write traffic cursor = i; lastRet = i - 1; checkForComodification(); } final void checkForComodification() { //检查是否发生过并发修改 if (modCount != expectedModCount) throw new ConcurrentModificationException(); } }
ListIterator:
在之前对比Iterator和Iterable有讲过,支持双向遍历,既可以向前,又可以向后
序列化:
ArrayList本身实现了序列化,transient Object[] elementData,用来保存数据的数组确实用transient修饰的,意味着不能该字段不能实现
序列化,那ArrayList到底如何实现的序列化的呢?
通过重写writeObject()和readObject(),因为数组中容量Capacity一般都是大于size的,意味着有很多空节点,ArrayList为了节省网络资源
,自定义了序列化,只序列化非空的部分
如果对序列化不够了解的话,可以参考:Java基础(十一)--Serializable和Externalizable接口实现序列化
到目前为止,我们对ArrayList有了基本的了解:
1、通过数组保存数据,可以随机访问数据,无论是get(i)、add(Object)还是set(int index, E element)效率都是比较高的
2、remove()和add(int index, Object object),都会发生System.arraycopy(),如果index后面的元素比较多的情况下,效率降低很多
3、线程不安全,如果需要保证安全性的话,建议使用CopyOnWriteArrayList
4、初始容量10,发生扩容为原来的1.5倍
总结:ArrayList适合顺序插入,按下标访问/修改,不适合删除,按下标插入