java集合核心源码01——ArrayList

Posted 自带锋芒

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了java集合核心源码01——ArrayList相关的知识,希望对你有一定的参考价值。

首先呢,对于ArrayList,相当于一个动态数组,里面可以存储重复数据,并且支持随机访问,不是线程安全的。对于更多的底层东西,且听分解。

打开源码,先看继承了哪些类,实现了哪些接口,然后继承的这些类或接口是否还有父类,一直深挖到顶部

 

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

可以看出,ArrayList集合继承了AbstractList类,实现了List,RandomAccess,Cloneable,Serializable这一系列接口,一个一个分析。

 

其中呢AbstractList类最终也是实现了Collection接口,List也是继承了Collection接口,同时呢Collection接口继承了Iterable接口。

对于Iterable接口呢,源码中解释说,for-each循环可以与任何实现了Iterable接口的对象一起工作,就是说该集合可以使用for-each循环。

对于Cloneable,Serializable这两个接口,代表该类支持克隆和序列化。

对于RandomAccess,实现了该接口,那么该类就支持快速的随机访问。LinkedList就没有实现该接口,需要用迭代器进行遍历。

对于AbstractList类,里面有一个属性modCount,这个属性主要是由集合的迭代器使用的,在迭代的过程中,会检查当前的List的modCount和自己保存的比较,是否发生了变化(变化是指该集合是否被其他线程并发修改),如果发生变化,就抛出java.util.ConcurrentModificationException异常,这种行为就是fail-fast机制

 

接下来,我们真正的走进ArrayList源码:

 transient Object[] elementData;

首先,ArrayList底层也是封装的一个数组,这个数组是被transient关键字修饰的,代表对象的临时数据,持久化对象时,不需要维持,所以不需要参与序列化,即用该关键字修饰。(该关键字修饰的实例变量不参与序列化)

然后呢,看一下ArrayList有哪些构造器:

  private static final int DEFAULT_CAPACITY = 10;//默认容量大小为10
  private int size;//提供私有变量保存数组的长度
  /**
  *接收一个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);
        }
    }

    /**
     * 不接收任何参数,采用默认容量10
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * 接收一个Collection类型的集合,把集合的元素复制到数组中
     * 
     */
    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

 

另外呢讲一下ArrayList的扩容:

ensureCapacity()方法是用来首先计算出一个最小扩展的容量minExpand,然后所需要的最小容量和最小扩展容量相比较,如果大于,就会进入ensureExplicitCapacity方法,也是进行判断后,然后进入主要的方法grow方法,计算新的容量大小为旧容量+旧容量/2(右移一位相当于除以2)即为原来长度的1.5倍,为什么是1.5倍呢?因为由实验可得出,1.5倍不会浪费太大的内存。

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

 private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

public
void ensureCapacity(int minCapacity) { int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) ? 0 : DEFAULT_CAPACITY; if (minCapacity > minExpand) { ensureExplicitCapacity(minCapacity); } } private void ensureExplicitCapacity(int minCapacity) { modCount++; if (minCapacity - elementData.length > 0) grow(minCapacity); } private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8; private void grow(int minCapacity) { int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); 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; }

 

除此之外呢,还有一个方法,这个方法是来判断数组中元素个数和数组长度的,来进行适时的释放内存空间,适时的调用此方法减少内存占用:

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

 

从源码中可以看出,ArrayList没有实现任何的同步,所以不是线程安全的,其他的添加,删除方法可以自行点开源码查看,相应的很简单。

 





以上是关于java集合核心源码01——ArrayList的主要内容,如果未能解决你的问题,请参考以下文章

Java小白集合源码的学习系列:ArrayList

Java小白集合源码的学习系列:ArrayList

JAVA——底层源码阅读——集合ArrayList的实现底层源码分析

Java集合之ArrayList源码解析

ArrayList精讲(源码分析)---Java集合

ArrayList精讲(源码分析)---Java集合