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问题:

  1. 对ArrayList外部加锁(方法加synchronized关键字,对多线程竞争访问的资源加synchronized方法块)
  2. 索性地,直接用Vector替换ArrayList,Vector同样也是继承AbstractList类,但他是线程安全的类。
  3. 采用 对ArrayList包装,List llist = Coll
  4. 按照业务需求,采用juc下支持并发的列表
  • ArrayList有哪些遍历方式?
    ListIterator Iterator for(😃 forEach等方法,其中ListIterator 是双向迭代器,Iterator是顺序单向迭代器,forEach 是java8 新增的方法,接受一个lambda表达式,for(:)就是迭代器Iterator的语法糖

  • java8中ArrayList加入了哪些新特性?
    ArrayList再java8中引入removeIf forEach sort 等支持函数式编程方法,特别地,继承了Collection祖先接口的stream()方法,让使用ArrayList做数据处理和写sql语言一样方法。

posted @ 2020-07-16 14:34  ahpuched  阅读(167)  评论(0)    收藏  举报