Java集合系列02之ArrayList源码分析

系列文章

前言

本文开始分析具体集合的源码和实现原理,首先来看ArrayListArrayList可以理解为一个动态数组,与普通数组相比,它的容量能动态增长。其定义如下:

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

可以看到ArrayList继承了AbstractList类,实现了ListRandomAccessCloneablejava.io.Serializable四个接口。

上文提到,AbstractList类提供了大部分List接口的实现,帮助我们减少了实现List接口的工作。

RandomAccess接口是List实现所使用的标记接口,表明List支持快速随机访问功能(就是通过索引获取元素)。

实现Cloneable接口表明覆盖了Clone()函数,能被克隆,实现java.io.Serializable接口表明ArrayList支持序列化。

本文源码分析基于jdk 1.8.0_121

继承关系

ArrayList的继承关系

java.lang.Object
  |___java.util.AbstractCollection<E>
      |___ java.util.AbstractList<E>
          |___ java.util.ArrayList<E>
所有已实现的接口:
Serializable, Cloneable, Iterable<E>, Collection<E>, List<E>, RandomAccess
直接已知子类:
AttributeList, RoleList, RoleUnresolvedList

关系图

ArrayList关系图

ArrayList的两个成员elementDatasize比较重要:

  • elementDataObject类型的数组,保存了ArrayList的数据,是ArrayList的底层实现,它是个动态数组,在添加数据过程中不断扩展容量,最大容量为Integer.MAX_VALUE - 8;容量的增长方式在源码中具体再分析
  • size是当前动态数组的实际大小

构造函数

public ArrayList() {}       // 默认构造函数  
public ArrayList(int initialCapacity) {} // 构造一个具有特定初始容量的ArrayList
public ArrayList(Collection<? extends E> c) {} // 创建一个包含Collection的ArrayList

以上三种构造函数在不同JDK版本之间也略有差异,但思想或者做的工作都基本一致。
public ArrayList()JDK1.8.0_121中令elementData为一个空数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA,而此版本中还存在另一个空数组成员EMPTY_ELEMENTDATA,此两者的区别是用来区分ArrayList添加第一个元素时容量要扩张多少。在之前的JDK版本中默认构造函数是直接初始化一个容量为10的数组。 (具体哪一版本开始出现这一区别不知道)
public ArrayList(int initialCapacity)构造一个指定初始容量的空列表,当initialCapacity为0时,令elementDataEMPTY_ELEMENTDATA
public ArrayList(Collection<? extends E> c)构造一个包含Collection的元素的列表,这些元素按照该Collection的迭代器返回它们的顺序排列的,先将Collection转为数组,再将数组拷贝给elementData

API

// Collection中定义的API(部分)
boolean             add(E object)
boolean             addAll(Collection<? extends E> collection)
void                clear()
boolean             contains(Object object)
boolean             containsAll(Collection<?> collection)
boolean             equals(Object object)
int                 hashCode()
boolean             isEmpty()
Iterator<E>         iterator()
boolean             remove(Object object)
boolean             removeAll(Collection<?> collection)
boolean             retainAll(Collection<?> collection)
int                 size()
<T> T[]             toArray(T[] array)
Object[]            toArray()
// AbstractList API(部分)
void                add(int index, E element) 
boolean             addAll(int index, Collection<? extends E> c)
E                   get(int index)
int                 indexOf(Object o)
ListIterator<E>     listIterator()
ListIterator<E>     listIterator(final int index)
E                   remove(int index)
E                   set(int index, E element)
List<E>             subList(int fromIndex, int toIndex) 
// ArrayList API(部分)
Object              clone()
void                ensureCapacity(int minCapacity)
void                trimToSize()
void                removeRange(int fromIndex, int toIndex)

源码分析

成员对象

// List默认容量
private static final int DEFAULT_CAPACITY = 10;

// 空数组
private static final Object[] EMPTY_ELEMENTDATA = {};

// 空数组,使用默认构造函数创建,则默认对象内容默认是该值
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

// 保存ArrayList中数据的数组,transient表示不参与序列化
transient Object[] elementData;

// 实际容量大小
private int size;

// 数组最大值
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

构造函数

// 带初始容量的构造函数
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);
   }
}

// 默认构造函数   
public ArrayList() {
   this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

// 带Collection对象的构造函数
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;
   }
}

改变容量值

// 将当前容量值设置为实际元素个数
public void trimToSize() {
    modCount++;
    if (size < elementData.length) {
        elementData = (size == 0)
          ? EMPTY_ELEMENTDATA
          : Arrays.copyOf(elementData, size);
    }
}

确定容量

// 判断待设置的最小容量minCapacity和默认容量大小
// minCapacity大于minExpand时才扩张容量
public void ensureCapacity(int minCapacity) {
    int minExpand = (elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA)? 0:DEFAULT_CAPACITY;

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

private void ensureCapacityInternal(int minCapacity) {
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }

    ensureExplicitCapacity(minCapacity);
}

// 修改次数+1,如果minCapacity大于数组长度,则扩张容量
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // overflow-conscious code
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

// 设置新容量为(原始容量x3)/2
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);
}

// 当minCapacity超出Integer.MAX_VALUE时,minCapacity变为负数,抛出异常
private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

增加元素

// 添加元素e到ArrayList
public boolean add(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

// 添加元素到指定位置
public void add(int index, E element) {
   rangeCheckForAdd(index);

   ensureCapacityInternal(size + 1);  // Increments modCount!!
   // 把原数组index索引开始后的所有元素向后移动一个位置
   System.arraycopy(elementData, index, elementData, index + 1,
        size - index);
   elementData[index] = element;
   size++;
}

// 集合c追加到ArrayList中
public boolean addAll(Collection<? extends E> c) {
    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityInternal(size + numNew);  // Increments modCount
    // 将集合c转换为数组后复制到elementData中
    System.arraycopy(a, 0, elementData, size, numNew);
    size += numNew;
    return numNew != 0;
}

// 从位置index开始,将集合c添加到ArrayList中
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;
    // 先在elementData中空出长度为集合c中元素个数的位置
    if (numMoved > 0)
        System.arraycopy(elementData, index, elementData, index + numNew,
                         numMoved);
    
    // 复制集合元素到elementData中
    System.arraycopy(a, 0, elementData, index, numNew);
    size += numNew;
    return numNew != 0;  
}

toArray

// 转化为ArrayList的Object数组
public Object[] toArray() {
    return Arrays.copyOf(elementData, size);
}
    
// 返回T类型的数组
public <T> T[] toArray(T[] a) {
    // 如果数组a的大小小于ArrayList的实际大小
    // 则新建一个数组(T[])数组,将ArrayList中元素全部拷贝到新数组中
    if (a.length < size)
        // Make a new array of a's runtime type, but my contents:
        return (T[]) Arrays.copyOf(elementData, size, a.getClass());
    // 若数组a的大小大于ArrayList的实际大小
    // 则将ArrayList中全部元素拷贝到数组a中
    System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}

调用toArray方法有以下两种:

java.lang.Integer[] obj = (java.lang.Integer[])list.toArray();
java.lang.Integer[] obj1 = list.toArray(new Integer[0]);

调用第一种方法会抛出“java.lang.ClassCastException”异常,调用 toArray(T[] contents) 能正常返回 T[]
这是因为将Object[]转换为的Integer[]则会抛出“java.lang.ClassCastException”异常,因为Java不支持向下转型。

设置元素

// 设置index处元素值,并返回旧值
public E set(int index, E element) {
    rangeCheck(index);

    E oldValue = elementData(index);
    elementData[index] = element;
    return oldValue;
}

读取元素

// 获取index位置的元素值
public E get(int index) {
    rangeCheck(index);

    return elementData(index);
}

删除元素

// 删除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;
}

// 删除指定元素
public boolean remove(Object o) {
    // 判断o是否为null
    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;
}

// 快速删除第index个元素
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
}

// 删除一定索引范围内元素值
protected void removeRange(int fromIndex, int toIndex) {
    modCount++;
    int numMoved = size - toIndex;
    System.arraycopy(elementData, toIndex, elementData, fromIndex,
                     numMoved);

    // clear to let GC do its work
    int newSize = size - (toIndex-fromIndex);
    for (int i = newSize; i < size; i++) {
        elementData[i] = null;
    }
    size = newSize;
}

// 删除所有元素
public boolean removeAll(Collection<?> c) {
    Objects.requireNonNull(c);
    return batchRemove(c, false);
}

序列化

// 将ArrayList的“容量,所有的元素值”都写入到输出流中
private void writeObject(java.io.ObjectOutputStream s)
    throws java.io.IOException{
    // Write out element count, and any hidden stuff
    int expectedModCount = modCount;
    s.defaultWriteObject();

    // 写入数组容量
    s.writeInt(size);

    // 写入所有元素
    for (int i=0; i<size; i++) {
        s.writeObject(elementData[i]);
    }

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

// 将ArrayList的“容量”读出,再将“所有的元素值”读出
private void readObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    elementData = EMPTY_ELEMENTDATA;

    // Read in size, and any hidden stuff
    s.defaultReadObject();

    // 读出容量
    s.readInt(); // ignored

    if (size > 0) {
        // be like clone(), allocate array based upon size not capacity
        ensureCapacityInternal(size);

        Object[] a = elementData;
        // 读取所有元素值
        for (int i=0; i<size; i++) {
            a[i] = s.readObject();
        }
    }
}

遍历方式

  • 迭代器遍历,即通过Iterator遍历
Iterator iter = list.iterator();
while(iter.hasNext()) {
    iter.next();
}
  • 随机访问,索引值
for (int i=0; i<list.size(); i++) {
    list.get(i);        
}
  • foreach循环
for (Integer integ:list) {
    ;
}

通过比较以上三种遍历方式,我们发现通过随机访问方式效率最高,而另两种方式效率都不高。

参考内容

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 217,509评论 6 504
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 92,806评论 3 394
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 163,875评论 0 354
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 58,441评论 1 293
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 67,488评论 6 392
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 51,365评论 1 302
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 40,190评论 3 418
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 39,062评论 0 276
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 45,500评论 1 314
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 37,706评论 3 335
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 39,834评论 1 347
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 35,559评论 5 345
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 41,167评论 3 328
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 31,779评论 0 22
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 32,912评论 1 269
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 47,958评论 2 370
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 44,779评论 2 354

推荐阅读更多精彩内容

  • 嗡、嗡、嗡的空调声响个不停,充斥在耳边,加剧着我脑海里的空白,日更2K啊,甚至1W啊,但是,一点头绪都没有。写不出...
    把可可放飞阅读 283评论 0 1
  • CSS css样式编写到head中的style标签里,称为内部样式表 方法一:将样式写入style标签中,,然后通...
    小乖很不乖阅读 389评论 0 0
  • ———007er第37篇成长记录——— 周日,我跟几个朋友约好拖家带口去目睹捷克最大城堡,卡尔斯坦城堡。捷克语...
    傲雪玫瑰阅读 1,562评论 3 2
  • 我的心里如果没有温柔, 再温和的话语也只是一把暗利的尘沙。 若我的双眼没有灵动的期盼, 再美丽的景色也写不出诗行。...
    营盘阅读 352评论 5 9
  • 牧尘的双手,在那众多惊愕目光之中,突然结成一道奇特手印,而后他眼神一凝,只见得其身后的空中,突然爆发出璀璨的红光,...
    混沌天书阅读 227评论 0 0