ArrayList源码分析

1.List接口

该接口继承了Collection接口的方法,除此还有一些特殊的扩展接口。

  • List是Collection体系中,一个子接口分支,该接口允许插入重复元素,支持通过索引,随机的访问集合中的元素。
  • 列表通常允许重复的元素。 更正式地,列表通常允许元素e1e2成对使得e1.equals(e2) ,并且如果它们允许空元素,它们通常允许多个空元素。
  • List接口提供了两种方法来搜索指定的对象。 从性能角度来说,谨慎使用这些方法。 在许多实现中,它们将执行昂贵的线性搜索。
  • List接口提供了一个特殊的迭代器,称为ListIterator,其允许元件插入和更换,并且除了该Iterator接口提供正常操作的双向访问。 提供了一种方法来获取从列表中的指定位置开始的列表迭代器。
public interface List<E> extends Collection<E> {
    //返回此列表中指定位置的元素。 
    E get(int index);
    //替换指定位置的元素,index索引是[0,size());
    E set(int index, E element);
    //将指定的元素插入此列表中的指定位置(可选操作)。 将当前位于该位置的元素(如果有)和任何后续元素(向其索引添加一个)移动。索引是[0,size()],当index=size()时,就是在尾部插入
    void add(int index, E element);
    //指定元素的下标,没有返回-1
    int indexOf(Object o);
    //返回列表中的列表迭代器(按适当的顺序)。
    ListIterator<E> listIterator();
    //从列表中的指定位置开始,返回列表中的元素(按正确顺序)的列表迭代器。 指定的索引表示初始调用将返回的第一个元素为next 。 初始调用previous将返回指定索引减1的元素。 
    ListIterator<E> listIterator(int index)
}

2.ListIterator<E>接口

接口是Iterator<E>的子接口,该接口除了定义实现了接口的类除了有后续迭代和移除的功能外,还支持向前遍历.
在这里插入图片描述

public interface ListIterator<E> extends Iterator<E> {
    boolean hasNext();
    
    E next();
    //是针对于当前元素位置进行判断
    boolean hasPrevious();
    //返回由后续调用返回的元素的索引next() 。 (如果列表迭代器位于列表的末尾,则返回列表大小。) 
    E previous();
    //返回由后续调用previous()返回的元素的索引。 (如果列表迭代器位于列表的开头,则返回-1)
    int previousIndex();
    //用指定的元素(可选操作)替换由next()返回的最后一个元素或previous() 。    
    void set(E e);
}

3.AbstractList<E>的实现

public abstract class AbstractList<E> extends AbstractCollection<E> implements List<E> {
    //逆序查找指定元素,支持o为null
	public int lastIndexOf(Object o) {
        //返回一个指定遍历到指定位置ListIterator对象,然后向前遍历
        ListIterator<E> it = listIterator(size());
        if (o==null) {
            while (it.hasPrevious())
                if (it.previous()==null)
                    return it.nextIndex();
        } else {
            while (it.hasPrevious())
                if (o.equals(it.previous()))
                    return it.nextIndex();
        }
        return -1;
    }
   	//返回一个元素迭代器
    //此实现返回一个简单的实现Iterator接口,依托后台列表的size(),get(int)和remove(int)方法。
    //(protected) modCount字段的规范中所描述的那样,这种实现可以面对并发修改来抛出运行时异常。
    public Iterator<E> iterator() {
        return new Itr();
    }
    
    //迭代器的简单实现
    private class Itr implements Iterator<E> {
        /**
         * Index of element to be returned by subsequent call to next.
         *集合中的元素下一次要返回的元素下表
         */
        int cursor = 0;

        /**
         * Index of element returned by most recent call to next or
         * previous.  Reset to -1 if this element is deleted by a call
         * to remove.
         * 返回下标,下表是最近一次的next()返回元素的下标。
         */
        int lastRet = -1;

        //集合中的元素删除,则会改变modCount的数值
        int expectedModCount = modCount;

        //当游标不等于集合大小时,则还可以元素尚未迭代完毕
        public boolean hasNext() {
            return cursor != size();
        }

        //返回当前游标对应的元素,返回后,将游标下移
        public E next() {
            //检查是否其他线程并发修改了元素
            checkForComodification();
            try {
                int i = cursor;
                E next = get(i);
                lastRet = i;//记录最新的返回元素索引
                cursor = i + 1;//游标++
                return next;
            } catch (IndexOutOfBoundsException e) {
                checkForComodification();
                throw new NoSuchElementException();
            }
        }

        //移除元素,
        public void remove() {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();//fast-fail 检查

            try {
                AbstractList.this.remove(lastRet);//移除上次操作元素
                if (lastRet < cursor)
                    cursor--;//游标上移,移除元素后,当前位置,应该是要迭代的下一个元素
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException e) {
                throw new ConcurrentModificationException();
            }
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }
    
    //返回一个可以任意方向遍历的迭代器对象
    public ListIterator<E> listIterator() {
        return listIterator(0);
    }
    
    private class ListItr extends Itr implements ListIterator<E> {
        //传入当前迭代的位置
        ListItr(int index) {
            cursor = index;
        }

        public boolean hasPrevious() {
            return cursor != 0;
        }

        //前一个元素,当前元素的前一个元素
        public E previous() {
            checkForComodification();
            try {
                int i = cursor - 1;
                E previous = get(i);
                lastRet = cursor = i;
                return previous;
            } catch (IndexOutOfBoundsException e) {
                checkForComodification();
                throw new NoSuchElementException();
            }
        }

        public int nextIndex() {
            return cursor;
        }

        public int previousIndex() {
            return cursor-1;
        }

        public void set(E e) {
            if (lastRet < 0)
                throw new IllegalStateException();
            checkForComodification();

            try {
                AbstractList.this.set(lastRet, e);
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }

        public void add(E e) {
            checkForComodification();

            try {
                int i = cursor;
                AbstractList.this.add(i, e);
                lastRet = -1;
                cursor = i + 1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
    }
}

4.ArrayList<E>的实现

ArrayList<E>是List接口的具体实现子类,实现了List的所有接口,存储的数据是有序的,并且允许元素重复,以及null值的添加。每一个ArrayList实例都有一个容量。容量是用于存储列表中的元素的数组的大小。除此之外,该类是非线程同步的。

4.1 成员变量
 	/**
     * Default initial capacity.默认的初始化容量
     */
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * Shared empty array instance used for empty instances.
     * 表示空集合
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     * 空list集合元素,如果空集合,将此作为返回元素,表示是元素个数为0,不是集合为null。
     * 此时,也有表示空构造方法调用默认的初始赋值。
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     * 默认被赋值为DEFAULTCAPACITY_EMPTY_ELEMENTDATA,当第一次添加元素时,会进行扩容
     */
    transient Object[] elementData; // non-private to simplify nested class access

 	//元素大小
    private int size;

	/**
     * The maximum size of array to allocate.
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * OutOfMemoryError: Requested array size exceeds VM limit
     * 最多允许存储的元素个数
     */
	private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
	//AbstractList中的变量,for fast-fail
	protected transient int modCount = 0;
4.2 构造函数

​ 关于ArraysSystem参见 :基本使用

public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        //表示当前集合大小是0,当集合扩容时,扩容大小选择是 oldCapacity + (oldCapacity >> 1)
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

public ArrayList() {
    //表示使用默认的构造函数,集合中的数组是空数组,集合扩容大小是 10
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

public ArrayList(Collection<? extends E> c) {
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        // c.toArray might (incorrectly) not return Object[] (see 6260652)
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // replace with empty array.
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

在构造函数中,有一个地方需要注意,EMPTY_ELEMENTDATADEFAULTCAPACITY_EMPTY_ELEMENTDATA使用的区别。这两个常量都表示的是{}是Object类型的数组,他们这是两个不同的数组对象。对于这两个常量,有不同的作用,常量EMPTY_ELEMENTDATA纯粹表示的是集合中元素是null,集合中存储数据的elementData大小是0,如果需要添加元素,则需要按指定扩容规则扩容。DEFAULTCAPACITY_EMPTY_ELEMENTDATA表示创建集合使用的是默认的构造方法,当第一次添加元素时,扩容的大小是采用默认的大小,就是10。关于扩容的,后面会详细讲解到,下面显示的是哪儿用到了DEFAULTCAPACITY_EMPTY_ELEMENTDATA

private static int calculateCapacity(Object[] elementData, int minCapacity) {
    //elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA只有采用默认的构造方法成立
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        //在数组默认大小和要求最小之间选择较大的
        return Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}
4.3 基本方法
//通过size字段维护

public int size() {
    return size;
}

public boolean isEmpty() {
    return size == 0;
}

//集合中是否包含o元素,判断通过 o==null?e==null:o.equals(e);
public boolean contains(Object o) {
	return indexOf(o) >= 0;
}
//返回指定元素的下标,不存在返回-1,重写了父类方法
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;
}
//如果a的size够大,存储到a中,否则创建一个新的数组返回
public <T> T[] toArray(T[] a) {
    if (a.length < size)
        //创建一个新的数组返回,内容是 elementData
        return (T[]) Arrays.copyOf(elementData, size, a.getClass());
    //elementData元素拷贝到数组a中,从0开始,元素个数为size
    System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}

//获取值,会进行范围检查
public E get(int index) {
    rangeCheck(index);

    return elementData(index);
}
//范围检查
private void rangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
4.4 添加和修改元素
//添加元素
public boolean add(E e) {
    //会进行容量检查,modCount也会发生更改
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}
//指定位置添加元素,可知index及其以后的元素要后移一位
public void add(int index, E element) {
    rangeCheckForAdd(index);

    ensureCapacityInternal(size + 1);  // Increments modCount!!
    //将下标index的元素和往后元素,拷贝到数组中,从index+1位置开始,长度是移动位数
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    elementData[index] = element;//插入元素
    size++;
}
//批量添加元素
public boolean addAll(int index, Collection<? extends E> c) {
    rangeCheckForAdd(index);

    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityInternal(size + numNew);  // Increments modCount

    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;
}
4.5 扩容

扩容主要是调用方法 ensureCapacityInternal(size + numNew); // Increments modCount,分析方法调用。集合是用的扩容是原size的1.5倍。

	//确保容量够元素添加使用,保证元素添加,所需要minCapacity最小容量
	private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

	//区分是空集合,还是调用了默认的构造方法,如果是默认的构造方法,指定容量应该为10
	private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
	
	private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // 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)//检测是否找过int最大值
            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) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
4.7 删除元素
 	//可以知道数组中删除元素是通过数组前移实现的,list实现也是如此,是通过数组拷贝
	//将index+1及其之后的元素,复制到index位置
	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; // clear to let GC do its work

        return oldValue;
    }
	//自行分析
	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; // clear to let GC do its work
    }

	/***             批量删除                ***/
	//将集合中c的元素删除
	public boolean removeAll(Collection<?> c) {
        Objects.requireNonNull(c);
        return batchRemove(c, false);
    }
	//将非集合c中的元素移除
	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++)
              //complement为false,则是移除元素调用的,elementData[w++]存储的是不在数组中元素
              //complement为false,则是保留元素调用的,elementData[w++]存储的是在数组中的元素
                if (c.contains(elementData[r]) == complement)
                    elementData[w++] = elementData[r];
        } finally {
            // Preserve behavioral compatibility with AbstractCollection,
            // even if c.contains() throws.
            if (r != size) {
                System.arraycopy(elementData, r,
                                 elementData, w,
                                 size - r);
                w += size - r;
            }
                
            //让GC工作,回收空闲位置
            if (w != size) {       
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                modCount += size - w;
                size = w;
                modified = true;
            }
        }
        return modified;
    }
4.8 迭代元素

都是采用的迭代器的设计模式,实现思路和AbstractListiterator()方法一样,可以参看以前的实现博客。

	
	public ListIterator<E> listIterator() {
        return new ListItr(0);
    }

	public Iterator<E> iterator() {
        return new Itr();
    }
			/****************新增修改 2019年9月3日00:07:51*******************/

5. 补充内容: RandomAccess 接口

public interface RandomAccess {

}

RandomAccess是一个空接口,这种空接口是起标识作用的,标识实现了这个接口的类具有随机访问功能。

Collections.binarySearch()方法中,它要判断传入的list是否是RamdomAccess 的实例,如果是,调用indexedBinarySearch()方法,如果不是,那么调用iteratorBinarySearch()方法。

为什么要这样做:

RandomAccess 的实例是支持随机访问的,通过下标获取元素的值复杂度是O(1) , 所以ArrayList实现了随机访问接口,根据下标获取的元素。但是对于LinkedList,他也是List的子类,也具有get(index)方法,但是这种获取的复杂度确实O(n)(是通过遍历元素,直到第index个元素),所以如果排序的时候,通过get获取元素比较,则时间复杂度太大。

public static <T>
    int binarySearch(List<? extends Comparable<? super T>> list, T key) {
        if (list instanceof RandomAccess || list.size()<BINARYSEARCH_THRESHOLD)
        	//RandomAccess实例,通过下标获取元素比较。
            return Collections.indexedBinarySearch(list, key);
        else
        	//不支持随机访问,则通过迭代获取元素进行比较。
            return Collections.iteratorBinarySearch(list, key);
    }
//用于支持随机存储的二分查找,获取中间值数值是通过get(mid)
private static <T>
    int indexedBinarySearch(List<? extends Comparable<? super T>> list, T key) {
        int low = 0;
        int high = list.size()-1;

        while (low <= high) {
            int mid = (low + high) >>> 1;
            Comparable<? super T> midVal = list.get(mid);
            int cmp = midVal.compareTo(key);

            if (cmp < 0)
                low = mid + 1;
            else if (cmp > 0)
                high = mid - 1;
            else
                return mid; // key found
        }
        return -(low + 1);  // key not found
    }

	//不支持随机访问,为避免get()查找浪费,使用ListIterator从头开始查找中间元素
    private static <T>
    int iteratorBinarySearch(List<? extends Comparable<? super T>> list, T key)
    {
        int low = 0;
        int high = list.size()-1;
        ListIterator<? extends Comparable<? super T>> i = list.listIterator();

        while (low <= high) {
            int mid = (low + high) >>> 1;
            Comparable<? super T> midVal = get(i, mid);
            int cmp = midVal.compareTo(key);

            if (cmp < 0)
                low = mid + 1;
            else if (cmp > 0)
                high = mid - 1;
            else
                return mid; // key found
        }
        return -(low + 1);  // key not found
    }

    /**
     * Gets the ith element from the given list by repositioning the specified
     * list listIterator.
     */
    private static <T> T get(ListIterator<? extends T> i, int index) {
        T obj = null;
        int pos = i.nextIndex();
        if (pos <= index) {
            do {
                obj = i.next();
            } while (pos++ < index);
        } else {
            do {
                obj = i.previous();
            } while (--pos > index);
        }
        return obj;
    }

总结List遍历方式的选择:

  • 实现了RandomAccess接口的list,可以通过普通for循环,其次是foreach。

  • 未实现RandomAccess 接口的list,通过迭代器去遍历,或者foreach(也是迭代器),但不可通过for,思考复杂度是O(n2),思考原因 ?

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值