ArrayList源码学习(JDK8)

本文是配合部分源码分析的,若有错误之处,欢迎大家指出,共同学习,共同进步,谢谢!(ps:翻译不到位的地方大家忽略 ==、)废话不多说,咱们直接开始!

进入ArrayList类,大致看看上面描述:

 
 /** Resizable-array implementation of the <tt>List</tt> interface.  Implements
 * all optional list operations, and permits all elements, including
 * <tt>null</tt>.  In addition to implementing the <tt>List</tt> interface,
 * this class provides methods to manipulate the size of the array that is
 * used internally to store the list.  (This class is roughly equivalent to
 * <tt>Vector</tt>, except that it is unsynchronized.) */
 list接口可变大小数组的实现。可以操作集合内所有元素,包含null。除了实现接口, 这个类
还提供了操作内部储存集合的数组大小的方法。(这个类大体和vector相似,但是它是不同步的。)

/** Note that this implementation is not synchronized.</strong>
* If multiple threads access an <tt>ArrayList</tt> instance concurrently,
* and at least one of the threads modifies the list structurally, it
* <i>must</i> be synchronized externally.  (A structural modification is
* any operation that adds or deletes one or more elements, or explicitly
* resizes the backing array; merely setting the value of an element is not
* a structural modification.)  This is typically accomplished by
* synchronizing on some object that naturally encapsulates the list. */
注意该实现是不同步的,如果有多个线程同时访问集合实例,且至少有一个线程改变了集合构造,
那么必须在外部同步。(修改集合构造的操作有添加、删除一个或多个元素,以及变化集合容量等,
设置元素值不包含在内)这一般通过自然封装对象的方式来完成同步。

/** If no such object exists, the list should be "wrapped" using the
* {@link Collections#synchronizedList Collections.synchronizedList}
* method.  This is best done at creation time, to prevent accidental
* unsynchronized access to the list:
* List list = Collections.synchronizedList(new ArrayList(...));*/
如果不存在这样的对象,那么集合应该用(Collections.synchronizedList)list这种方式转换。
这个最好在开始创建之前就完成,以防止出现对列表的非同步访问:

该集合继承了AbstractList<E>类并实现了List<E>, RandomAccess, Cloneable, java.io.Serializable等接口,其中 RandomAccess可以保证我们可以随机对集合元素进行访问,这也是为什么AbstractList查询速度快的原因。(JDK对RandomAccess的官方解释:RandomAccess 是 List 实现所使用的标记接口,用来表明其支持快速(通常是固定时间)随机访问。此接口的主要目的是允许一般的算法更改其行为,从而在将其应用到随机或连续访问列表时能提供良好的性能。)

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

基本属性: 

private static final long serialVersionUID = 8683452581122892189L;//序列化ID
private static final int DEFAULT_CAPACITY = 10;//默认容量
private static final Object[] EMPTY_ELEMENTDATA = {};//空的存放数据的对象
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};//默认的空的存放数据的对象
transient Object[] elementData; //数据存放的对象,不参与序列化
private int size;//数组大小

无参构造方法: 

public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

有参构造方法(int类型参数):

public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
判断数值大小,大于0则初始化一个int大小的集合,,等于0则初始化为空集合,小于0抛异常

有参构造方法(collections集合): 

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;
        }
    }
将集合转换为数组并赋给elementData,判断集合长度,若为0则初始化空集合,不为0则将数据完整赋给elementData

1、调整集合容量: trimToSize(): 

/**
     * Trims the capacity of this <tt>ArrayList</tt> instance to be the
     * list's current size.  An application can use this operation to minimize
     * the storage of an <tt>ArrayList</tt> instance.
  */
/** 调整ArrayList实例的容量为当前list的大小,程序可以使用这个操作来最小化这个ArrayList实例的存储 */
    public void trimToSize() {
        modCount++;
        if (size < elementData.length) {
            elementData = (size == 0)
              ? EMPTY_ELEMENTDATA
              : Arrays.copyOf(elementData, size);
        }
    }
该方法一般用于:扩容后会产生很多填充的null,这个方法可以将这些null都去掉以节省内存,具体做法就是
判断将扩容后数组的值复制到新创建的数组中(Arrays.copyOf()),保证空间的高效利用。

 集合扩容,当添加元素时,会调用判断集合大小的函数,若为第一次添加,则初始化大小为10,若添加元素后容量大小满足要求,则直接添加元素,若添加元素后容量大小不满足,则需要扩容,扩展容量为原容量的1.5倍,若扩容后仍然不满足,则执行hugeCapacity(int minCapacity)函数,返回结果为Integer.MAX_VALUE 或者 MAX_ARRAY_SIZE,其中MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8 ;

以下方法为扩容部分代码:

public void ensureCapacity(int minCapacity) {
        int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)
            // any size if not default element table
            ? 0
            // larger than default for default empty table. It's already
            // supposed to be at default size.
            : DEFAULT_CAPACITY;

        if (minCapacity > minExpand) {
            ensureExplicitCapacity(minCapacity);
        }
    }

添加操作时判断(size + 1)容量大小:
private void ensureCapacityInternal(int minCapacity) {
        //判断是否需要扩容
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity)/*与elementData比较*/) ;

与当前elementData对象比较:
private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        //若为第一次添加,则容量初始化为10,否则返回size+1
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

判断集合容量大小,是否需要扩容:
private void ensureExplicitCapacity(int minCapacity) {
        modCount++;//修改次数加一

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)//如果增加元素后size+1大于elementData长度
            grow(minCapacity);//执行扩容
    }
    
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

计算扩容:
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);//扩容为原来1.5倍(ps:位运算oldCapacity >> 1) = oldCapacity/2,即:newCapacity = 1.5倍oldCapacity )
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;//扩容后容量
    if (newCapacity - MAX_ARRAY_SIZE > 0)//扩容后容量不够
        newCapacity = hugeCapacity(minCapacity);//执行hugeCapacity()
    // 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;
}

2、ArrayList添加方法:add(E e),若为第一次添加,则容量初始化为10。

public boolean add(E e) {
        //以下ensureCapacityInternal()见扩容部分
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;//将元素放在size+1位置
        return true;
    }

private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        //若为第一次添加,则容量初始化为10,否则返回size+1
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

添加(指定位置):
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++;  //容量加一
    }

//不符合规范抛异常
private void rangeCheckForAdd(int index) {
        if (index > size || index < 0)
            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    }

 3、ArrayList删除方法:

(1)remove(int index)。其中rangeCheck(index);首先判断下标是否合法,不合法则抛出异常;然后根据下标删除对应位置的元素,并将后面元素整体向前移一位,以空出最后一个null位置供垃圾回收器回收。

根据下标删除
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;
    }

 

(2) remove(Object o):先遍历并找到相匹配数据下标,然后删除下标对应数据并返回true,否则返回false。

删除对象:
public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)//循环遍历
                if (elementData[index] == null) {//找到对应元素下标
                    fastRemove(index);//删除下标对应元素
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)//循环遍历
                if (o.equals(elementData[index])) {//判断元素值是否相等
                    fastRemove(index);//删除对应下标元素
                    return true;
                }
        }
        return false;//否则返回false
    }

 fastRemove(int index):根据删除元素下标,将后面元素统一向删除元素位置方向移动一位,并将最后一个元素置为null,供垃圾回收器回收。

快速删除:
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
    }

 4、clear():遍历集合将所有下标对应元素置为null,并将size置为0,供垃圾回收器回收。

public void clear() {
        modCount++;

        // clear to let GC do its work
        for (int i = 0; i < size; i++)
            elementData[i] = null;

        size = 0;
    }

5、get(int index):根据元素下标,返回对应元素。 

 public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }

6、set(int index, E element):首先判断下标是否合法,不合法则抛出异常;合法则获取到对应下标的原始数据,并用新数据替换原始数据,并返回原始数据。

public E set(int index, E element) {
        rangeCheck(index);//判断下标是否合法

        E oldValue = elementData(index);//获取到原始数据
        elementData[index] = element;//新数据赋值对应下标
        return oldValue;//返回原始数据
    }

7、contains(Object o):判断集合内是否包含已知对象 。此方法主要根据indexOf(Object o)判断,如果对象为null,则遍历集合查询是否有对应数据并返回其下标;不为null,则通过equals方法进行比对,判断是否有相同的元素值并返回对应下标;否则返回-1上层函数根据里层函数返回值确定是否存在。

public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }

public int indexOf(Object o) {
    if (o == null) {
        for (int i = 0; i < size; i++)
            if (elementData[i]==null)//null判断
            return i;
    } else {
        for (int i = 0; i < size; i++)
            if (o.equals(elementData[i]))//通过equals比较元素值
                return i;
    }
    return -1;
}

8、iterator():方法返回一个内部类。内部类里三个常量分别是:cursor下一个元素索引,lastRet上一个元素索引,expectedModCount预期修改次数(对应modCount);类中提供一个无参构造函数Itr() {},以及我们常用的hasNext(),next(),remove()方法。

hasNext()方法很容易看出,只要下一个元素索引比集合size小,那么就存在下一个元素。

next()方法也不难得出,只要返回cursor对应索引的元素即可,然后将然后将cusor值赋值给lastRet,并将cursor+1,以此类推。但是要注意的是checkForComodification()方法,该方法用来判断集合在迭代过程中是否被修改,这在多线程操作集合时容易抛出并发修异常的原因。

remove()方法实际调用的是集合自身remove方法实现删除lastRet索引位置的元素,然后修改cusor及expectedModCount 。

    public Iterator<E> iterator() {
        return new 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
        int expectedModCount = modCount;

        Itr() {}

        public boolean hasNext() {
            return cursor != size;//判断下一个索引是否等于集合容量,不等于则存在下一个元素
        }

        @SuppressWarnings("unchecked")
        public E next() {
            checkForComodification();//判断修改次数是否合法,用来确定迭代过程中集合是否被修改
            int i = cursor;
            if (i >= size)//下一个元素索引大于size抛异常
                throw new NoSuchElementException();
            Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length)//下一个元素索引大于elementData长度抛异常
                throw new ConcurrentModificationException();
            cursor = i + 1;//cursor加一
            return (E) elementData[lastRet = i];//返回索引对应的元素并将索引赋值给lastRet 
        }

        public void remove() {
            if (lastRet < 0)//判断数值是否合法
                throw new IllegalStateException();
            checkForComodification();

            try {
                ArrayList.this.remove(lastRet);//调用集合自身remove方法
                cursor = lastRet;
                lastRet = -1;
                expectedModCount = modCount;
            } catch (IndexOutOfBoundsException ex) {
                throw new ConcurrentModificationException();
            }
        }
    
        @Override
        @SuppressWarnings("unchecked")
        public void forEachRemaining(Consumer<? super E> consumer) {
            Objects.requireNonNull(consumer);
            final int size = ArrayList.this.size;
            int i = cursor;
            if (i >= size) {
                return;
            }
            final Object[] elementData = ArrayList.this.elementData;
            if (i >= elementData.length) {
                throw new ConcurrentModificationException();
            }
            while (i != size && modCount == expectedModCount) {
                consumer.accept((E) elementData[i++]);
            }
            // update once at end of iteration to reduce heap write traffic
            cursor = i;
            lastRet = i - 1;
            checkForComodification();
        }

        final void checkForComodification() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
        }
    }

以上是我基于JDK8的ArrayList源码学习,在这里记录下来,希望同大家交流学习,欢迎大家指出其中不足之处,谢谢大家。下期预告:Vector源码学习(基于jdk8)。

参考资料:ArrayList 源码分析,非常感谢!

传送门:

              1、Vector源码学习(JDK8)

              2、LinkedList源码学习(JDK8)

如果喜欢本文,请为我点赞,您的支持是我继续下去的动力,您也可以在评论区与我探讨或指正错误,最后别忘了关注一下我哦,谢谢。

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值