ArrayList源码详解(基于jdk1.8.0_231)
告读者:这是基于jdk1.8.0_231的ArrayList源码解析,主要总结了ArrayList类的扩容机制,fail-fast机制,迭代器,视图,和java8中引入的函数式编程,以及相关API的使用,对add addAll remove removeAll removeIf set get等增删改查源码不做解释,如果是为了准备面试,建议直接看本文底部 面试session 小结,如有疑问和错误,欢迎交流批评指正,谢谢!
动机: ArrayList类基本原理是十分显然的,然实现的细节是有一些技巧性的东西,如果你恰巧看见了本文,你会发现写得又臭又长,看的还不一定清楚明了。是的,是这样的,阅读源码主要的目的 一方面 学习类库作者的代码规范和编码技巧,另一方面,是为了深入体会设计的细节。所以,纸上得来终觉浅,如果你对源码感兴趣,强烈建议你自己利用IDE阅读源码,偶对照本文或其他同行的介绍或许更有体会。
1. ArrayList 简介
- ArrayList一种支持自动扩容的动态数组,继承了AbstractList
实现了List , RandomAccess, Cloneable, java.io.Serializable,故支持随机访问,可克隆,可序列化等特性; - 粗略地讲,ArrayList和Vector功能基本等价,但是不同的是,ArrayList在多线程中式不安全,而Vector是线程安全的。如果存在多个线程访问ArrayList实例,且存在一个线程在修改该实例结构。可考虑使用同步的list或外部加锁或使用List list = Collections.synchronizedList(new ArrayList
()); - fail-fast机制:iterator ListIterator 两种迭代在遍历列表时,发现此列表结构正在别改变(增加元素add addll,删除元素remove removeIf removeAll retainAll,清除clear都可以认为时结构改变)就会抛出一个ConcurrentModificationException异常,称这样机制为fail-fast机制。
- 可存入null
- java8 引入了函数式编程,ArrayList对元素的处理更灵活。
注 检测结构改变的方法是:ArrayList类从AbstraList父类继承了一个modCount,每次执行add remove等方法改变结构时,modCount都会记录共修改了列表结构多少次,(如执行add(E e) 时,增加了一个元素,modCounth会加1),迭代器初始化时,会令一个成员变量int expectedModCount = modCount 每次迭代数组时(如执行next() previous()等方法),都会检测一下expectedModCount 是否 仍然等于 modCount(调用checkForComodification()),若发现不等说明,立即抛出ConcurrentModificationException,当然这种利用迭代器遍历时,尽最大努力的保障fail-fast机制。(尽最大努力 是由于并发的不确定性导致的,如刚刚检查完expectedModCount modCount的大小后,另外一个线程修改了列表结构,可能就检查不到)
2. ArrayList API概述
字段
- ArrayList类定义的字段
private static final long serialVersionUID = 8683452581122892189L;
private static final int DEFAULT_CAPACITY = 10;//默认初始化容量
transient Object[] elementData; //存储ArrayList元素的数组,其长度(elementData.length())为ArrayList数组的容量
private int size; //ArrayList中存放的实际元素个数
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//当利用ArrayList<>()构造函数初始化时,elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA
private static final Object[] EMPTY_ELEMENTDATA = {}; // 当利用ArrayList(int initialCapacity)构造函数初始化时,elementData = EMPTY_ELEMENTDATA
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; //有些虚拟机种支持ArrayList中允许最大的size,有些虚拟机也可以扩容为Integer.MAX_VALUE,
- 继承的字段
protected transient int modCount = 0; //从AbstractList继承而来,主要用于fail-fast机制,检查列表结构在迭代过程中有没有被修改
构造函数
public ArrayList(int initialCapacity)
public ArrayList()
public ArrayList(Conlection<? extends E> c)
- ArrayList(int),ArrayList(),ArrayList(Collection<? extends E>)三个构造函数,ArrayList(int) 参数为初始化列表大小,默认情况为10, 当需要反复扩容时,建议先预估列表大小,提高性能;ArrayList()构造一个初始化容量为10的列表,ArrayList(Collection<? extends E>) 构造一个有具体元素的列表;
ArrayList API
- Override或新增的public方法
void trimToSize() //将列表的容量收缩为size,去掉capacity中没用元素用的位置
void ensureCapacity(int minCapacity) //确保列表的容量可容纳所有元素,否则扩容
int size() //返回列表大小
boolean isEmpty() //列表为空返回true,否则false
boolean contains(Object o) //判断列表是否包含对象o,
int indexOf(Object o) //顺序查找,返回对象o在列表中首次出现的位置, o不在列表中返回-1
int lastIndexOf(Object o) //逆序查找,返回对象o在首次出现的位置,不在返回-1
Object clone() //浅拷贝列表,如果列表中是基本类型,就是深拷贝,若是自定义类,就是浅拷贝
Object[] toArray() //列表转数组,是数组和列表之间的桥梁
<T> T[] toArray(T[] a) //列表转数组,返回数组的运行时类型是指定数组的运行时类型。
E elementData(int index) //按位置返回,对index的范围不做检查,返回第index位置上的元素
E get(int index) //按位置返回,检查index的范围,返回第index位置上的元素
E set(int index, E element) //将index位置的元素换为element,返回原来的element
boolean add(E e) // 增加element新元素
void add(int index,E element) //按位置插入,在index位置插入element,从index位置(包括index位置)开始整体后移1位
E remove(int index) //按位置移除,将index位置的元素移除,从index+1位置开始每个元素依次往前移动1位
boolean remove(Object o) //按元素移除,顺序找到待移除元素o,移除后,后面元素依次往前移动1位
void clean() //清空列表
boolean addAll(Collection<? extends E> c) //增加一个集合(多个元素),【先时后面的每个元素依次往后移动多个位置让出空间】
boolean addAll(int index, Collection<? extends E> c) //从index位置开始,增加一个集合(多个元素)【先时后面的每个元素依次往后移动多个位置让出空间】
boolean removeAll(Collection<? extends E> c) //如果列表中的元素存在在集合c中,那么将从列表中移除,即为保留列表与c的差集[列表-c]
boolean retainAll(Collection<? extends E> c) //如果列表中的元素存在在集合c中,那么将保留,即为取列表和c的交集
ListIterator<E> listIterator(int index) //双向迭代器【既可以前移,也可以往后移】,从第index位置开始迭代,可以利用该类型迭代器增插删元素【由于实现get,set,remove】
ListIterator<E> listIterator() //双向迭代器【既可以前移,也可以往后移】,从0位置开始迭代,
Iterator<E> iterator() //单向(顺序从前往后),可以利用该类型迭代器移除元素【由于实现remove】
List<E> subList(int fromIndex, int toIndex) //返回列表的一个子列表
void forEach(Consumer<? super E> action) //遍历每个元素,对每个元素做action操作
Spliterator<E> spliterator()
boolean removeIf(Predicate<? super E> filter) //移除列表中符合filter动作的元素
void replaceAll(UnaryOperator<E> operator)
void sort(Comparator<? super E> c) //根据c的规则排序
- 超类或接口直接继承的default和常规方法
public int hashCode() //从AbstractList类直接继承,且ArrayList类中没有Override
public boolean equals(Object O) //从AbstractList类直接继承,且ArrayList类中没有Override
default Stream<E> stream() //串行流
default Stream<E> parallelStream() //并行流
- ArrayList类内部private方法
void ensureCapacityInternal(int minCapacity) //检查扩容
static int calculateCapacity(Object[] elementData ,int) //检查是否需要扩容
void ensureExplicitCapacty(int),grow(int ) //检查是否需要扩容
void grow(int minCapacity) //常规扩容+ 按需扩容
static int hugeCapacity(int minCapacity) //当容量快达最大容量时的扩容操作
void fastRemove(int index) //按位置快速remove,快速体现在不对index做范围检查
void rangeCheck(int index) //对index做边界检查,防止越上界
void rangeCheckForAdd(int index) //对index做边界检查,防止越上下界
String outOfBoundsMsg(int index) //越界异常的提醒信息
boolean batchRemove(Collection<?> c, boolean complement) //批量remove,主要为removeAll retainAll两个public方法服务
void writeObject(java.io.ObjectOutputStream s) //序列化
void readObject(java.io.ObjectInputStream s) //序列化
3. ArrayList源码详解(基于jdk1.8.0_261)
扩容
ArrayList的扩容机制简单来讲,就是我们在添加元素前,会检查现有的容量(容量就是elementData.length())够不够,不够,容量就扩大为原来的1.5倍,够了,就直接添加。扩容的具体细节由ArrayList中如下几个私有函数完成:ensureCapacityInternal(int minCapacity),calculateCapacity(Object[] elementData ,int),ensureExplicitCapacty(int),grow(int ),hugeCapacity(int)
private void ensureCapacityInternal(int minCapacity) { //minCapacity指需要的最小容量,如使用add增加1个元素,minCapacity=size+1,使用addAll增加n个元素,minCapacity=size+n
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
肯定的讲,只要添加元素(add addAll),添加前,一定会检查是否需要扩容,add调用ensureCapacityInternal检查是否需要扩容,addAll直接调用ensureExplicitCapacity是否需要扩容。
private static int calculateCapacity(Object[] elementData, int minCapacity) { //elementData就是我们存放列表的地方(值或者引用)
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity); //
}
return minCapacity;
}
当我们使用无参构造函数ArrayList<>()初始化类时,会执行this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA,根据上面DEFAULTCAPACITY_EMPTY_ELEMENTDATA字段定义,刚刚初始化时,elementData的容量为0,当第一次add时,直接扩容为10(即elementData.length = 10),后面当minCapacity大于10时,都不会扩容,当minCapacity大于10时,newCapacity = element.length()>>1 + elementData.length(),即扩容成原容量的1.5倍。
private void ensureExplicitCapacity(int minCapacity) {
modCount++; //当结构可能会改变时,modCount也修改了,多线程环境下,某个线程迭代器可能检测到,执行fail-fast机制
// overflow-conscious code
if (minCapacity - elementData.length > 0) //需要的最小容量都大于现有容量,那就需要扩容,这条语句会有数值溢出风险
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length; //得到当前容量
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0) //扩容后上溢了或者是扩容了还没达到最小需求,这个时候就按需扩容了(存多少元素,就开多大空间)
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0) //当扩容达到允许的容量上限后,(MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8 ),不在按照1.5倍扩容,也不按需扩容
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) // overflow
throw new OutOfMemoryError();
return (minCapacity > MAX_ARRAY_SIZE) ? //如果大于 MAX_ARRAY_SIZE,直接扩充成Integer.MAX_VALUE,否则直接扩为 MAX_ARRAY_SIZE
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
小结 : 首次利用无参初始化ArrayList示例的时候,列表容量为0,当加入第一个元素时,立即扩容为10,以后每次需要的最小容量达到当前容量时,每次按当前容量1.5倍扩容,当扩容扩到一定程度时候,理想情况下,会出现两种情况:第一种扩容后,新容量比原容量小,则执行按需扩容(有多少元素,就开多大空间),扩容后,容量超过容量上限,如果原容量小于容量上限就直接扩充成容量上限,如果原容量已经大于容量上限,直接扩成Integer.MAX_VALUE.
值得考虑的是,这种"弹性"的扩容机制在极大数据量下肯定是不安全的,比如,我们目前已经扩到Integer.MAX_VALUE,然后我们在add一个元素(minCapacity = -2147483648),从ensureCapacityInternal走一套下来,我们发现添加不进去,原数组也不会变,这无可厚非,但是在大数据情况下,这种扩容是否是完备的呢?即我们在不超过Integer.MAX_VALUE时,是否总能安全存放数据? 我认为扩容后只要不溢出且不达到最大容量,都是安全的,否则只要溢出数据就不安全了.
迭代
ArrayList中实现了两种迭代器:Iterator,ListIterator。我们可以通过iterator() ,listIterator() 函数得到这两种两种迭代器。
- iterator()源码
public Iterator<E> iterator() {
return new Itr(); //返回一个匿名Itr()类
}
- listIterator()源码
public ListIterator<E> listIterator() {
return new ListItr(0); //从第0位置开始迭代
}
- listIterator(int index)源码
public ListIterator<E> listIterator(int index) {
if (index < 0 || index > size)
throw new IndexOutOfBoundsException("Index: "+index);
return new ListItr(index); //从index位置开始迭代
}
- ArrayList 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 上一个已返回的元素的索引,如果还没开始迭代,初始值为-1
int expectedModCount = modCount; //初始化expectedModCount = modCount,检测列表结构是否被改变
Itr() {} //无参空的构造函数
public boolean hasNext() {
return cursor != size; //cursor == size 表示目前已到列表尾部
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification(); //检测 expectedModCount 是否等于modCount,相等则认为目前无其他线程在修改此列表结构,否则直接抛异常
int i = cursor;
if (i >= size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData; //让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;
lastRet = -1; //
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
@Override
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) { //得到Itr()类后,我们可以iterator.forEachRamaining(System.out::println)
Objects.requireNonNull(consumer); //由于Comsumer函数式接口中的accept没有返回值,所以不会对this.elementData做任何修改
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();
}
}
注意到使用iterator()返回的Itr()匿名内部类,只可以对列表元素进行遍历和移除操作。想要得到列表的索引,一定要先执行next()
- ArrayList ListItr内部类源码
private class ListItr extends Itr implements ListIterator<E> { //继承了内部类Itr(),[cursor,lasRet,hasNext,next,remove,forEachRemaining]
ListItr(int index) {
super();
cursor = index;
}
public boolean hasPrevious() {
return cursor != 0;
}
public int nextIndex() { //返回cursor位置
return cursor;
}
public int previousIndex() { //返回cursor-1位置
return cursor - 1;
}
@SuppressWarnings("unchecked")
public E previous() { //往前迭代
checkForComodification();
int i = cursor - 1;
if (i < 0)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i;
return (E) elementData[lastRet = i];
}
public void set(E e) {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.set(lastRet, e);
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
public void add(E e) {
checkForComodification();
try {
int i = cursor;
ArrayList.this.add(i, e);
cursor = i + 1;
lastRet = -1;
expectedModCount = modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
}
调用listIterator(int index),可以得到ListIterator迭代器,这是一个双向迭代器,cursor既可以往前,也可以往后。Iterator是一个顺序单想迭代器,只能往后。不管时ListIterator还是Iterator在想要得到索引之前,一定要先执行next()或previous().利用listIterator迭代器可以对列表实现增删改查操作,Iterator迭代器可以对列表实现remove操作,通过源码可以发现这些操作实质都是对原列表直接操作的。
视图
利用subList(int fromIndex, int toIndex)可以返回列表的子列表,我们对子列表的操作,可以直接反映在原列表上。
- subList(int fromIndex, int toIndex)源码
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size); // 参数合法性检查 即需要保证 fromIndex < toIndex && toIndex < size && fromIndex >= 0
return new SubList(this, 0, fromIndex, toIndex); //返回一个SubList匿名内部类
}
- SubList内部类源码
private class SubList extends AbstractList<E> implements RandomAccess {
private final AbstractList<E> parent;
private final int parentOffset;
private final int offset;
int size;
SubList(AbstractList<E> parent,
int offset, int fromIndex, int toIndex) {
this.parent = parent; // parent其实就是this,即列表引用,所以我们对子列表的一切操作,都会直接反映在原列表上
this.parentOffset = fromIndex;
this.offset = offset + fromIndex;
this.size = toIndex - fromIndex;
this.modCount = ArrayList.this.modCount;
}
public E set(int index, E e) {
rangeCheck(index);
checkForComodification();
E oldValue = ArrayList.this.elementData(offset + index);
ArrayList.this.elementData[offset + index] = e;
return oldValue;
}
public E get(int index) {
rangeCheck(index);
checkForComodification();
return ArrayList.this.elementData(offset + index);
}
public int size() {
checkForComodification();
return this.size;
}
public void add(int index, E e) {
rangeCheckForAdd(index);
checkForComodification();
parent.add(parentOffset + index, e);
this.modCount = parent.modCount;
this.size++;
}
public E remove(int index) {
rangeCheck(index);
checkForComodification();
E result = parent.remove(parentOffset + index);
this.modCount = parent.modCount;
this.size--;
return result;
}
protected void removeRange(int fromIndex, int toIndex) {
checkForComodification();
parent.removeRange(parentOffset + fromIndex,
parentOffset + toIndex);
this.modCount = parent.modCount;
this.size -= toIndex - fromIndex;
}
public boolean addAll(Collection<? extends E> c) {
return addAll(this.size, c);
}
public boolean addAll(int index, Collection<? extends E> c) {
rangeCheckForAdd(index);
int cSize = c.size();
if (cSize==0)
return false;
checkForComodification();
parent.addAll(parentOffset + index, c);
this.modCount = parent.modCount;
this.size += cSize;
return true;
}
public Iterator<E> iterator() {
return listIterator();
}
public ListIterator<E> listIterator(final int index) {
checkForComodification();
rangeCheckForAdd(index);
final int offset = this.offset;
return new ListIterator<E>() {
int cursor = index;
int lastRet = -1;
int expectedModCount = ArrayList.this.modCount;
public boolean hasNext() {
return cursor != SubList.this.size;
}
@SuppressWarnings("unchecked")
public E next() {
checkForComodification();
int i = cursor;
if (i >= SubList.this.size)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (offset + i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i + 1;
return (E) elementData[offset + (lastRet = i)];
}
public boolean hasPrevious() {
return cursor != 0;
}
@SuppressWarnings("unchecked")
public E previous() {
checkForComodification();
int i = cursor - 1;
if (i < 0)
throw new NoSuchElementException();
Object[] elementData = ArrayList.this.elementData;
if (offset + i >= elementData.length)
throw new ConcurrentModificationException();
cursor = i;
return (E) elementData[offset + (lastRet = i)];
}
@SuppressWarnings("unchecked")
public void forEachRemaining(Consumer<? super E> consumer) {
Objects.requireNonNull(consumer);
final int size = SubList.this.size;
int i = cursor;
if (i >= size) {
return;
}
final Object[] elementData = ArrayList.this.elementData;
if (offset + i >= elementData.length) {
throw new ConcurrentModificationException();
}
while (i != size && modCount == expectedModCount) {
consumer.accept((E) elementData[offset + (i++)]);
}
// update once at end of iteration to reduce heap write traffic
lastRet = cursor = i;
checkForComodification();
}
public int nextIndex() {
return cursor;
}
public int previousIndex() {
return cursor - 1;
}
public void remove() {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
SubList.this.remove(lastRet);
cursor = lastRet;
lastRet = -1;
expectedModCount = ArrayList.this.modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
public void set(E e) {
if (lastRet < 0)
throw new IllegalStateException();
checkForComodification();
try {
ArrayList.this.set(offset + lastRet, e);
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
public void add(E e) {
checkForComodification();
try {
int i = cursor;
SubList.this.add(i, e);
cursor = i + 1;
lastRet = -1;
expectedModCount = ArrayList.this.modCount;
} catch (IndexOutOfBoundsException ex) {
throw new ConcurrentModificationException();
}
}
final void checkForComodification() {
if (expectedModCount != ArrayList.this.modCount)
throw new ConcurrentModificationException();
}
};
}
public List<E> subList(int fromIndex, int toIndex) { //这个可以继续得到子列表的子列表,子子孙孙,看我们需求
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, offset, fromIndex, toIndex);
}
private void rangeCheck(int index) {
if (index < 0 || index >= this.size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private void rangeCheckForAdd(int index) {
if (index < 0 || index > this.size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+this.size;
}
private void checkForComodification() {
if (ArrayList.this.modCount != this.modCount)
throw new ConcurrentModificationException();
}
public Spliterator<E> spliterator() {
checkForComodification();
return new ArrayListSpliterator<E>(ArrayList.this, offset,
offset + this.size, this.modCount);
}
}
int hashCode() 和 boolean equals(Object o)
这两个方法都是直接继承自AbstractList,ArrayList没有重写Override 者两个方法
- hashCode源码
public int hashCode() {
int hashCode = 1;
for (E e : this) //遍历每个元素,计算一个总的hashCode
hashCode = 31*hashCode + (e==null ? 0 : e.hashCode()); //e.hashCode()的计算根据e的类型
return hashCode;
}
注释: :
若 e 是 Integer类型,则e.hashCode就是数字本身,如 Integer i = 20; i.hashCode()就是20;其他类型如Double Float String hashCode的实现方法各异。
- equals源码
值得一说的是,这类库代码写得好简洁,优雅呀!
public boolean equals(Object o) {
if (o == this) //指向同一个引用,直接认为相等
return true;
if (!(o instanceof List)) //如果都不是实现了List接口的类或子类,直接认为不相等
return false;
//是实现了List接口类或其子类
ListIterator<E> e1 = listIterator();
ListIterator<?> e2 = ((List<?>) o).listIterator(); //先强制转换迭代器的类型
while (e1.hasNext() && e2.hasNext()) { //一次迭代每个元素,看是否相等
E o1 = e1.next();
Object o2 = e2.next();
if (!(o1==null ? o2==null : o1.equals(o2)))
return false;
}
return !(e1.hasNext() || e2.hasNext()); //其中一个较短的已经比较完,且两个列表都相等,若其中一个列表还有下个元素,则返回false,否则返回true
}
特别地,equals 和 hashCode 具有相同的语义,即为 a.equals(b)为 true,则 a.hashCode() 一定等于 b.hashCode(),所以这两个方法在类中一定是同时重写的。
补充
- 对其他一些API的解释和使用
void forEach(Consumer<? super E> action) //遍历每个元素,对每个元素做accept操作
Spliterator<E> spliterator() //to do list
boolean removeIf(Predicate<? super E> filter) //移除列表中符合filter动作的元素
void replaceAll(UnaryOperator<E> operator)
void sort(Comparator<? super E> c) //根据c的规则排序
Object[] toArray() //列表转数组,是数组和列表之间的桥梁
<T> T[] toArray(T[] a) //列表转数组,返回数组的运行时类型是指定数组的运行时类型。
- void forEach(Consumer<? super E> action)源码分析
调用forEach函数时,传入一个lambda表达式作为参数,lambda表达式可以看作是实现了函数接口的匿名内部类。
@Override
public void forEach(Consumer<? super E> action) {
Objects.requireNonNull(action); //action对象不能为空
final int expectedModCount = modCount; //使用forEach迭代时,也会运行fail-fast机制
@SuppressWarnings("unchecked")
final E[] elementData = (E[]) this.elementData;
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) { //fail-fast,每次迭代元素时都检查
action.accept(elementData[i]); //对每个元素执行accept动作,
} //action即为传入的lambda表达式(类),调用accept其实就是对每个elemenData[i]执行lambda表示式的->后的语句。
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
}
- boolean removeIf(Predicate<? super E> filter)源码分析
removeIf源码分析设计到一个BitSet类,这个类是个位集,主要做一些标记工作.深入了解可参考另外一篇blog BitSet源码详解
@Override
public boolean removeIf(Predicate<? super E> filter) {
Objects.requireNonNull(filter); //filter对象不能为空
// figure out which elements are to be removed
// any exception thrown from the filter predicate at this stage
// will leave the collection unmodified
int removeCount = 0;
final BitSet removeSet = new BitSet(size); //开一个size(准确来讲应该是64的整数倍)大小的位集,如size=0-64 开的位集大小为64,65-128开的大小为128
final int expectedModCount = modCount; //fast-fail机制
final int size = this.size;
for (int i=0; modCount == expectedModCount && i < size; i++) { //迭代每个元素时都运行fast-fail机制
@SuppressWarnings("unchecked")
final E element = (E) elementData[i];
if (filter.test(element)) { //如果element符合fliter规则,
removeSet.set(i); //则讲第i比特位置置1
removeCount++;
}
}
if (modCount != expectedModCount) {
throw new ConcurrentModificationException();
}
// shift surviving elements left over the spaces left by removed elements
final boolean anyToRemove = removeCount > 0; //如果removeCount>0,说明才需要移除元素, =0,则没有元素需要移除
if (anyToRemove) { //大于0执行
final int newSize = size - removeCount;
for (int i=0, j=0; (i < size) && (j < newSize); i++, j++) {
i = removeSet.nextClearBit(i); //从i开始返回第一个比特为0的,既需要保留的元素位置
elementData[j] = elementData[i]; //保留的元素往前移动
}
for (int k=newSize; k < size; k++) {
elementData[k] = null; // Let gc do its work 后面元素清理掉
}
this.size = newSize; //size更新
if (modCount != expectedModCount) {
throw new ConcurrentModificationException(); //removeAll也执行fail-fast机制
}
modCount++;
}
return anyToRemove;
}
- Spliterator
spliterator()源码分析
4. ArrayList使用实例
- 增删改查 遍历 迭代 java8新特性:forEach sort removeIf
package collectionlearn;
import java.util.*;
import java.util.stream.Collectors;
public class ArrayListTest {
public static void main(String[] args) {
List<Integer> list = new Random(34)
.ints(1,10000)
.distinct()
.limit(20).boxed().collect(Collectors.toList());
/**
* 创建一个列表
*/
ArrayList<Integer> myList = new ArrayList<>(list);
System.out.println(myList);
/**
* 增
* add addAll
*/
myList.add(1);
myList.add(2,2);
myList.addAll(Arrays.asList(1,1,1,1));
myList.addAll(0,Arrays.asList(0,0,0));
System.out.println(myList);
/**
* 删
* remove removeall retainAll clean removeIf
*/
myList.remove(0); //按位置remove
myList.remove((Integer) 2); //按元素remove
System.out.println(myList);
myList.removeAll(Arrays.asList(0)); //求差集
System.out.println(myList);
myList.removeIf(x -> x==1 ); //按条件remove
System.out.println(myList);
myList.addAll(Arrays.asList(1,1,1,1));
System.out.println(myList);
myList.retainAll(list); //求交集
System.out.println(myList);
myList.clear(); //清空
System.out.println(myList);
myList.addAll(list);
System.out.println(myList);
/**
* 改
* set toArray
*/
myList.add(0,0);
System.out.println(myList);
myList.set(0,100);
System.out.println(myList);
//两个toArray
Object[] lists = myList.toArray();
System.out.println(lists);
System.out.println(Arrays.toString(lists));
Integer[] a = new Integer[myList.size()];
a = myList.toArray(a);
System.out.println(a);
System.out.println(Arrays.toString(a));
/**
* 查
* isEmpty ensureCapacity size
*/
System.out.println(myList.isEmpty());
System.out.println(myList.size());
myList.ensureCapacity(20);
/**
* 迭代 listIterator Iterator for(:) forEach
*/
ListIterator<Integer> listIterator = myList.listIterator();
while(listIterator.hasNext()) {
int i = listIterator.next();
System.out.print(i+",");
}
System.out.println();
Iterator<Integer> iterator = myList.iterator();
while(iterator.hasNext()){
int i = iterator.next();
System.out.print(i+"/");
}
System.out.println();
for (int i:myList){
System.out.print(i+"|");
}
System.out.println();
myList.forEach(x -> System.out.print(x+"-"));
/**
* subList
*/
System.out.println();
List<Integer> sublist = myList.subList(0,10);
System.out.println(sublist);
sublist.add(0);
sublist.addAll(Arrays.asList(1,1,1,1));
System.out.println(sublist);
System.out.println(myList); //对子列表视图的修改反映在原列表上
/**
* 排序 sort
*/
System.out.println(myList);
myList.sort((x,y) -> (x-y));
System.out.println(myList);
myList.sort((x,y) ->(y-x));
System.out.println(myList);
/**
* stream() parallelStream()
*/
List<Integer> myStreamList = myList.stream().filter(x -> x > 1).collect(Collectors.toList());
System.out.println(myStreamList);
List<Integer> myPstreamList = myList.parallelStream().filter(x -> x > 1).collect(Collectors.toList()); //简单地讲,在数据量比较大(几万起步)的时候使用parallerlStream()性能会比串行提升
System.out.println(myPstreamList);
}
}
- 结果
[6539, 9213, 4549, 1587, 3405, 2532, 8712, 6821, 7554, 8198, 4025, 1521, 9838, 3696, 5242, 180, 5531, 6179, 3204, 9935]
[0, 0, 0, 6539, 9213, 2, 4549, 1587, 3405, 2532, 8712, 6821, 7554, 8198, 4025, 1521, 9838, 3696, 5242, 180, 5531, 6179, 3204, 9935, 1, 1, 1, 1, 1]
[0, 0, 6539, 9213, 4549, 1587, 3405, 2532, 8712, 6821, 7554, 8198, 4025, 1521, 9838, 3696, 5242, 180, 5531, 6179, 3204, 9935, 1, 1, 1, 1, 1]
[6539, 9213, 4549, 1587, 3405, 2532, 8712, 6821, 7554, 8198, 4025, 1521, 9838, 3696, 5242, 180, 5531, 6179, 3204, 9935, 1, 1, 1, 1, 1]
[6539, 9213, 4549, 1587, 3405, 2532, 8712, 6821, 7554, 8198, 4025, 1521, 9838, 3696, 5242, 180, 5531, 6179, 3204, 9935]
[6539, 9213, 4549, 1587, 3405, 2532, 8712, 6821, 7554, 8198, 4025, 1521, 9838, 3696, 5242, 180, 5531, 6179, 3204, 9935, 1, 1, 1, 1]
[6539, 9213, 4549, 1587, 3405, 2532, 8712, 6821, 7554, 8198, 4025, 1521, 9838, 3696, 5242, 180, 5531, 6179, 3204, 9935]
[]
[6539, 9213, 4549, 1587, 3405, 2532, 8712, 6821, 7554, 8198, 4025, 1521, 9838, 3696, 5242, 180, 5531, 6179, 3204, 9935]
[0, 6539, 9213, 4549, 1587, 3405, 2532, 8712, 6821, 7554, 8198, 4025, 1521, 9838, 3696, 5242, 180, 5531, 6179, 3204, 9935]
[100, 6539, 9213, 4549, 1587, 3405, 2532, 8712, 6821, 7554, 8198, 4025, 1521, 9838, 3696, 5242, 180, 5531, 6179, 3204, 9935]
[Ljava.lang.Object;@15aeb7ab
[100, 6539, 9213, 4549, 1587, 3405, 2532, 8712, 6821, 7554, 8198, 4025, 1521, 9838, 3696, 5242, 180, 5531, 6179, 3204, 9935]
[Ljava.lang.Integer;@7b23ec81
[100, 6539, 9213, 4549, 1587, 3405, 2532, 8712, 6821, 7554, 8198, 4025, 1521, 9838, 3696, 5242, 180, 5531, 6179, 3204, 9935]
false
21
100,6539,9213,4549,1587,3405,2532,8712,6821,7554,8198,4025,1521,9838,3696,5242,180,5531,6179,3204,9935,
100/6539/9213/4549/1587/3405/2532/8712/6821/7554/8198/4025/1521/9838/3696/5242/180/5531/6179/3204/9935/
100|6539|9213|4549|1587|3405|2532|8712|6821|7554|8198|4025|1521|9838|3696|5242|180|5531|6179|3204|9935|
100-6539-9213-4549-1587-3405-2532-8712-6821-7554-8198-4025-1521-9838-3696-5242-180-5531-6179-3204-9935-
[100, 6539, 9213, 4549, 1587, 3405, 2532, 8712, 6821, 7554]
[100, 6539, 9213, 4549, 1587, 3405, 2532, 8712, 6821, 7554, 0, 1, 1, 1, 1]
[100, 6539, 9213, 4549, 1587, 3405, 2532, 8712, 6821, 7554, 0, 1, 1, 1, 1, 8198, 4025, 1521, 9838, 3696, 5242, 180, 5531, 6179, 3204, 9935]
[100, 6539, 9213, 4549, 1587, 3405, 2532, 8712, 6821, 7554, 0, 1, 1, 1, 1, 8198, 4025, 1521, 9838, 3696, 5242, 180, 5531, 6179, 3204, 9935]
[0, 1, 1, 1, 1, 100, 180, 1521, 1587, 2532, 3204, 3405, 3696, 4025, 4549, 5242, 5531, 6179, 6539, 6821, 7554, 8198, 8712, 9213, 9838, 9935]
[9935, 9838, 9213, 8712, 8198, 7554, 6821, 6539, 6179, 5531, 5242, 4549, 4025, 3696, 3405, 3204, 2532, 1587, 1521, 180, 100, 1, 1, 1, 1, 0]
[9935, 9838, 9213, 8712, 8198, 7554, 6821, 6539, 6179, 5531, 5242, 4549, 4025, 3696, 3405, 3204, 2532, 1587, 1521, 180, 100]
[9935, 9838, 9213, 8712, 8198, 7554, 6821, 6539, 6179, 5531, 5242, 4549, 4025, 3696, 3405, 3204, 2532, 1587, 1521, 180, 100]
- 测试fail-fast机制
import java.util.ArrayList;
import java.util.Arrays;
import java.util.ListIterator;
public class ArrayListFailFastTest {
private static class ThreadAddElement implements Runnable {
ArrayList<Integer> list;
public ThreadAddElement(ArrayList<Integer> list) {
this.list = list;
}
@Override
public void run() {
for (int i = 9; i <= 20; i++ ) {
try {
list.add(i);
Thread.sleep(1000); //加一个元素,addElementThread线程就休眠1s,让调度器去执行main线程
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public static void main(String[] args) throws InterruptedException {
ArrayList<Integer> llist = new ArrayList<>(Arrays.asList(1,2,3,4,5,6,7,8));
Thread addElemenThread = new Thread(new ThreadAddElement(llist));
addElemenThread.start(); //启动线程,往llist加元素
ListIterator<Integer> listIterator = llist.listIterator();
while (listIterator.hasNext()) { //main线程迭代llist
int num = listIterator.next(); //next()函数中会运行fail-fast机制,检查迭代中llist结构是否改变
System.out.println(num);
Thread.sleep(1000); //迭代一次,main线程也休眠1s,让调度器去执行addElmentThread线程
}
}
}
- 结果
1
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
at java.util.ArrayList$Itr.next(ArrayList.java:859)
at collectionlearn.ArrayListFailFastTest.main(ArrayListFailFastTest.java:31)
- 避免fail-fast
5. 面试session
-
ArrayList的数据结构?
ArrayList本质上是自动扩容的数组,由于数据的特性,故实现了随机访问,在不扩容情况下,他的get set 时间复杂度都是O(1),add remove addAll removeAll retainAll removeIf时间复杂为O(n),值得注意的是removeIf removeAll retainAll空间复杂度为O(n). -
谈谈ArrayList扩容机制?
简单来讲,每次扩容都是原来容量的1.5,注意到ArrayList源码是利用 原容量+原容量>>1 来时实现。特别地,注意到可能存在扩容后容量小于原容量(或仍然达不到最小容量要求),这时我们采用按需扩容策略,即为我们需要存储多少元素,仅给多少空间,另一方面,由于为了兼容某些java虚拟机(一些虚拟机保持了header words 再数组里),某些虚拟机中的容量最大值为Integer.MAX_VALUE - 8,所以扩容后的容量要是大于 Integer.MAX_VALUE - 8,我门再一次改变策略,看扩容前的容量是否大于Interge.MAX_VALUE-8,若大于【说明该虚拟机header words 不放在数组中】直接扩成Interge.MAX_VALUE,若不大于,直接扩成Interge.MAX_VALUE-8。以后机制主要来源于ArrayList源码grow 和 hugeCapacity两个函数。 -
什么是fail-fast?怎么避免fail-fast?
利用listIterator Iterator forEach forRetaining 在迭代列表的时候,会“尽最大努力”检查当前列表结构有没有被其他线程正在改变,如果监测到列表结构被改变,则立即抛出ConcurrentModificationException,表示改列表正在被其他线程修改其结构。fail-fast是类库设计者善意提醒我们程序员“当前是多线程环境,线程不安全的ArrayList抗不住了,满足不了业务需求,再用会出现一系类生产事故”。我们可以通过以下几个方面解决fail-fast问题:
- 对ArrayList外部加锁(方法加synchronized关键字,对多线程竞争访问的资源加synchronized方法块)
- 索性地,直接用Vector替换ArrayList,Vector同样也是继承AbstractList类,但他是线程安全的类。
- 采用 对ArrayList包装,List
llist = Coll - 按照业务需求,采用juc下支持并发的列表
-
ArrayList有哪些遍历方式?
ListIterator Iterator for(😃 forEach等方法,其中ListIterator 是双向迭代器,Iterator是顺序单向迭代器,forEach 是java8 新增的方法,接受一个lambda表达式,for(:)就是迭代器Iterator的语法糖 -
java8中ArrayList加入了哪些新特性?
ArrayList再java8中引入removeIf forEach sort 等支持函数式编程方法,特别地,继承了Collection祖先接口的stream()方法,让使用ArrayList做数据处理和写sql语言一样方法。

浙公网安备 33010602011771号