Java集合框架(2)—ArrayList源码分析

ArrayList是Java容器类中最重要的类之一,我将根据ArrayList源码,结合自己的认识,学习ArrayList的实现,以点破面,以期对Java集合类有一个全面的深入。

以下是ArrayList源代码的outline:
《Java集合框架(2)—ArrayList源码分析》

1,类定义:

public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {

ArrayList继承AbstractList,实现List,RandomAccess,Cloneable,java.io.Serializable接口

2,elementData

transient Object[] elementData;

ArrayList实际数据存储结构,transient是一个关键词:Java的serialization提供了一种持久化对象实例的机制。当持久化对象时,可能有一个特殊的对象数据成员,我们不想用serialization机制来保存它。为了在一个特定对象的一个域上关闭serialization,可以在这个域前加上关键字transient。当一个对象被序列化的时候,transient型变量的值不包括在序列化的表示中,然而非transient型的变量是被包括进去的。

3,size

private int size;

ArrayList包含元素的个数

4,构造器

有参构造器,设置了初始容量
public ArrayList(int initialCapacity)
无参构造器,初始容量默认为10
public ArrayList()
元素是集合类的构造器
public ArrayList(Collection<? extends E> c)

5,trimToSize

public void trimToSize() {
        modCount++;
        if (size < elementData.length) {
            elementData = (size == 0)
              ? EMPTY_ELEMENTDATA
              : Arrays.copyOf(elementData, size);
        }
    }

将ArrayList的容量压缩到目前所包含元素的个数,即最小化容量

6,扩容相关

ArrayList最大的特征就是可以在初始化之后仍能改变大小。因此改变容量是其核心操作。与扩容相关的方法和变量如下:
变量:
1)size:size自然是非常关心的域
2)MAX_ARRAY_SIZE:最大容量,由JVM设定,
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE – 8;
Integer,MAX_VALUE是2^32-1,至于为什么要再减8,不是很清楚。
3)minCapacity:经常作为参数传入扩容函数,是扩容后的容积不能小于这个值。
方法:

//保证容量不小于minCapacity,如果目前容量小于,会调用ensureExplicitCapacity()扩容
public void ensureCapacity(int minCapacity) 
private void ensureCapacityInternal(int minCapacity)
//执行扩容到minCapacity,内部是调用grow()函数扩容
private void ensureExplicitCapacity(int minCapacity{
    modCount++;
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

/************important**************/
//扩容核心函数,通过创建一个新的大容量数组,然后将旧数组复制到新数组中,改变引用实现

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)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

//其中使用到hugeCapacity()函数,是用于处理minCapacity超出MAX_ARRAY_SIZE的情况
 private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

7,整体操作

//1,查询ArrayList元素数量
public int size() {
    return size;
}
//2,查询是否为空
public boolean isEmpty() 
    return size == 0;
}
//3,clone
public Object clone()
//4,toArray转化成一个数组
public Object[] toArray()//Object实现
public <T> T[] toArray(T[] a)//泛型实现

8,数据操作

//0,索引越界检查,被其他函数调用的私有方法,如果越界会抛异常
 private void rangeCheck(int index){
        if (index >= size)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
 private void rangeCheckForAdd(int index) 
//1,查询是否包含某元素
public boolean contains(Object o){
    return indexOf(o) >= 0;
}
//2,查询元素的第一次出现的索引,可以查空元素,不存在的元素返回-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;
}
//3,查询元素的最后一次出现的索引,可以查空元素,不存在的元素返回-1,实现和上一个基本一样,遍历顺序相反而已
 public int lastIndexOf(Object o) 
//4,根据索引查元素
public E get(int index) 
//5,根据索引设置元素
public E set(int index, E element) {
        rangeCheck(index);

        E oldValue = elementData(index);
        elementData[index] = element;
        return oldValue;
    }
//6,插入到尾,内部要调用扩容函数,这时候可以理解一系列扩容方法的意义,每次可能需要扩容的时候,根据实际元素数量和现有容量比较看是否真的需要扩容,如果需要就执行扩容,如果不需要,保持当前容量。
public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
//7,向指定索引位置插入元素,观察它的实现,是先将原有数组中元素,想后移动一格,(通过复制数组函数实现),然后将带插入元素复制给空出的位置。
 public void add(int index, E element) {
        rangeCheckForAdd(index);

        ensureCapacityInternal(size + 1);  // Increments modCount!!
        System.arraycopy(elementData, index, elementData, index + 1,
                         size - index);
        elementData[index] = element;
        size++;
    }
//8,删除索引位置元素,和插入元素方法类似,使用数组复制操作移动一格,尾元素清空
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;
    }
//9,删除第一个指定元素,删除成功返回true,没有这个元素,返回false
 public boolean remove(Object o)
//10,快速删除,不反回bool值,实际上上面的函数是调用快速删除的
private void fastRemove(int index)
//11,批量插入,插入集合类
public boolean addAll(Collection<? extends E> c)
//12,插入集合类到指定位置
public boolean addAll(int index, Collection<? extends E> c)
//13,删除指定范围内元素
protected void removeRange(int fromIndex, int toIndex)
//14,删除所有参数中有的元素
public boolean removeAll(Collection<?> c) {
    Objects.requireNonNull(c);
    return batchRemove(c, false);
}
//15,删除所有参数中没有的元素
public boolean retainAll(Collection<?> c) {
    Objects.requireNonNull(c);
    return batchRemove(c, true);
}
//16,上两个函数都调用了
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++)
                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;
            }
            if (w != size) {
                // clear to let GC do its work
                for (int i = w; i < size; i++)
                    elementData[i] = null;
                modCount += size - w;
                size = w;
                modified = true;
            }
        }
        return modified;
}

9,流操作

ObjectInputStream和ObjectOutputStream是针对类对象的输入输出流,配合ArrayList可以组成灵活的操作,ArrayList有两个和他们相关的方法:

//1,将ArrayList中元素些入到输出流中
private void writeObject(java.io.ObjectOutputStream s)
//2,将输入流中的元素读到ArrayList中
private void readObject(java.io.ObjectInputStream s)
//具体实现暂不深究,在深入研究输入输出流时再学习

10,List转化与ListIterator

//返回从特定位置开始的迭代器ListIterator
public ListIterator<E> listIterator(int index) {
    if (index < 0 || index > size)
        throw new IndexOutOfBoundsException("Index: "+index);
    return new ListItr(index);
}
//返回从头开始的迭代器ListIter
public ListIterator<E> listIterator() {
    return new ListItr(0);
}
//返回一个普通迭代器
public Iterator<E> iterator() {
    return new Itr();
}
//Itr和ListItr是Iterator和ListIterator接口的实现类,详情需要学习迭代器相关内容,再做解释。
//子List
public List<E> subList(int fromIndex, int toIndex) {
        //检测参数是否合规
        subListRangeCheck(fromIndex, toIndex, size);
        return new SubList(this, 0, fromIndex, toIndex);
    }

11,函数式编程相关
在Java1.8中加入了对函数式编程的一些支持,ArrayList也有一些相应方法,需要理解函数相关接口才好理解。暂跳过。
http://ifeve.com/有很多相关的中文博客

12,sort

跟据Comparator进行排序

 public void sort(Comparator<? super E> c) {
        final int expectedModCount = modCount;
        Arrays.sort((E[]) elementData, 0, size, c);
        if (modCount != expectedModCount) {
            throw new ConcurrentModificationException();
        }
        modCount++;
    }

总结,通读了一遍ArrayList源码,发现了不少精巧的设计,加深了对容器类的理解。后续要继续努力学习其他源码——2017/1/16

    原文作者:java集合源码分析
    原文地址: https://blog.csdn.net/picway/article/details/54576235
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞