jdk 源码 --ArrayList

本贴最后更新于 2000 天前,其中的信息可能已经物是人非

本文章基于 jdk1.8_171

ArrayList 介绍

java 中用的最多(个人感觉)的一个集合,内部维护着一个数组,方便,不用像数组一样事先给定大小。

成员变量

private static final int DEFAULT_CAPACTIY = 10;

默认容量,如果新建一个对象时没有指定容量,会新建一个空数组,并在第一次添加元素的时候把容量改为 10(DEFAULT_CAPACTIY ),当然,前提是添加元素的个数不大于 10 个。

private static final Object[] EMPTY_ELEMENTDATA = {};

一个空数组。

private  static  final  Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA  = {};

也是一个空数组,新建对象时如果没有指定容量,就把这个数组作为内部的数组。与 EMPTY_ELEMENTDATA 的区别是,第一次添加元素时,如果数组是 DEFAULTCAPACITY_EMPTY_ELEMENTDATA ,容量就变为 DEFAULT_CAPACTIY 。(添加元素不超过 10 个)。

transient Object [] elementData;

集合里真正放数据的地方, 这个数组的长度就是集合的容量。

private  int size;

集合里实际的元素个数,不是数组长度。

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

可以设置数组的最大长度,为什么-8,因为数组需要 8byte 存储本身的大小。参考链接

构造器

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

无参构造器,把 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 作为内部数据,添加第一个元素时容量增长为 DEFAULT_CAPACTIY

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);
        }
}

新建一个容量为 initialCapacity 的对象,initialCapacity 小于 0 时抛出异常。

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;
        }
}

新建一个对象,并把集合 c 的数组赋予 elementData ,如果集合 c 为空,elementData = EMPTY_ELEMENTDATA。注意,此时数组长度就是 size。

方法

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

常用方法,向集合内添加一个元素。先看容量够不够添加元素,不够就扩容。然后在末尾添加元素,size+1。

private void calculateCapacity(Object[] elementDate,int  minCapacity){
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    return minCapacity;
}
private  void  ensureCapacityInternal(int  minCapacity) {
    ensureExplicitCapacity(calculateCapacity(elementDate,minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

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

这三个放在一起说,表示扩容前的检查。如果内部数组是 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 并且需要的最小容量不大于
DEFAULT_CAPACITY,那么就把容量定成 DEFAULT_CAPACITY。最后如果需要的容量大于内部数组长度,则进行扩容。注意:modCount 表示此集合被修改的次数,这个变量定义在父类 AbstractList 中。

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);
}

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

真正的扩容方法,首先扩容 1.5 倍,如果还小于所需最小容量,那就把所需最小容量设置为新的容量,如果新的容量大于 MAX_ARRAY_SIZE,把 int 的最大值作为容量。注意,如果你的集合需要这么多容量,那应该考虑一下拆分了。

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);
    }
}

ArrayList 对外提供的扩容方法,需要大量添加元素时可以先手动扩容。

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));
}

在指定位置添加元素。首先检查 index 是否合法,然后看情况进行扩容,再然后把 index 和 index 之后的元素往后移一位,最后把要添加的元素放在下标 index 的位置上。

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;
}

查找指定元素的位置,显示的是第一次出现的位置,支持 null 的查找。如果找不到,返回-1。lastIndexOf 方法同理。

未完待续。。。

  • Java

    Java 是一种可以撰写跨平台应用软件的面向对象的程序设计语言,是由 Sun Microsystems 公司于 1995 年 5 月推出的。Java 技术具有卓越的通用性、高效性、平台移植性和安全性。

    3190 引用 • 8214 回帖 • 1 关注
  • 集合
    14 引用 • 8 回帖

相关帖子

欢迎来到这里!

我们正在构建一个小众社区,大家在这里相互信任,以平等 • 自由 • 奔放的价值观进行分享交流。最终,希望大家能够找到与自己志同道合的伙伴,共同成长。

注册 关于
请输入回帖内容 ...