你好,游客 登录 注册 搜索
背景:
阅读新闻

Java8 ArrayList源码分析

[日期:2018-01-16] 来源:Linux社区  作者:Finley [字体: ]

java.util.ArrayList是最常用的工具类之一, 它是一个线程不安全的动态数组. 本文将对JDK 1.8.0中ArrayList实现源码进行简要分析.

ArrayList底层采用Object[]来存储, 每次添加元素前都会检查数组是否有足够空间容纳新的元素.

若数组空间不足则会进行扩容操作, 即创建一个容量更大的数组 并将已有的元素复制到新数组中. 默认情况下新数组的容量是当前容量的1.5倍.

ArrayList使用Arrays.copyOfSystem.arraycopy调用原生(native)方法进行数组复制, 以提高效率.

addAll, removeAll等方法中通常使用c.toArray方法来获取容器中所有元素.

ArrayList提供了iterator()listIterator()两种迭代器, 前者只能向后移动, 而后者可以双向移动.

iterator()只能删除上一个访问的元素, 而listIterator()还可以在游标位置添加元素.

两种迭代器都采用fail-fast机制, 即使用modCount记录结构性改变(添加删除元素等)的次数, 迭代器在移动前会检查modCount是否发生改变. 若modCount改变, 则抛出异常中止迭代. 该方法是为了防止其它线程修改容器造成迭代结果不一致.

数据结构与构造器

在介绍构造器之前, 首先介绍一下ArrayList的数据结构:

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

/**  * elementData是实际存储数据的缓冲区 * 其类型为Object[], 即在内部用Object类存储元素在取出时进行类型转换 * 访问控制为默认(包内访问)是为了便于内部类访问 * transient关键字表示不对该域进行序列化, ArrayList内部重写了序列化/反序列化方法 */
transient Object[] elementData; 

// 当前元素数目
private int size;

// 用于表示空实例的数组 
private static final Object[] EMPTY_ELEMENTDATA = {};

/**  * 默认构造器使用的空数组 * 当elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA时, 首次添加元素会使elementData扩容到DEFAULT_CAPACITY */ 
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

接下来可以阅读ArrayList的几个构造器:

// 按照指定初始容量进行初始化
public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        // 指定容量为0时使用EMPTY_ELEMENTDATA, 而非重新初始化空数组
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        // 不允许负容量
        throw new IllegalArgumentException("Illegal Capacity: " + initialCapacity);
    }
}

/**  * 按照DEFAULT_CAPACITY进行初始化 * 构造时并未实际建立缓冲区, 在首次添加元素时才会扩容到DEFAULT_CAPACITY */
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

/** * 根据其它集合对象创建 * 默认调用Collection.toArray方法,  * 若toArray方法返回类型不是Object[], 则利用Arrays.copyOf进行类型转换 */
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;
    }
}

Arrays.copyOf用于复制数组, 其封装了原生(native)方法System.arraycopy, 具有很高的效率.

ArrayList中广泛使用这两个方法用于扩容, 插入等操作.

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
    @SuppressWarnings("unchecked")
    T[] copy = ((Object)newType == (Object)Object[].class)
        ? (T[]) new Object[newLength]
        : (T[]) Array.newInstance(newType.getComponentType(), newLength);
    System.arraycopy(original, 0, copy, 0,
                     Math.min(original.length, newLength));
    return copy;
}

添加元素

ArrayList的底层数据结构为数组, 每次向其中添加元素前都会检查数组容量是否足够. 若数组已满则会进行扩容操作.

首先阅读添加单个元素的方法add(E):

// 向数组末尾添加一个元素, 返回值代表数组是否改变
publicbooleanadd(E e) {
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}

privatevoidensureCapacityInternal(int minCapacity) {
    // 对于默认构造器创建的实例, 保证容量不小于DEFAULT_CAPACITY
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    ensureExplicitCapacity(minCapacity);
}

privatevoidensureExplicitCapacity(int minCapacity) {
    // modCount记录了实例发生结构性变化的次数, 用于迭代器的fail-fast机制
    modCount++;

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

privatevoidgrow(int minCapacity) {
    // 计算扩容后新容量, 默认为原容量的1.5倍
    int oldCapacity = elementData.length;
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    
    // oldCapacity的1.5倍已经溢出, 所以出现反而变小的情况
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
        
    // 若大于MAX_ARRAY_SIZE则由hugeCapacity取上限
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);

    // 创建新数组并把原有元素移动到新数组中        
    elementData = Arrays.copyOf(elementData, newCapacity);
}
    
privatestaticinthugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

在制定位置添加元素的add(index, e)方法非常类似:

publicvoidadd(int index, E element) {
    rangeCheckForAdd(index);

    ensureCapacityInternal(size + 1);  // Increments modCount!!
    // elementData类型一定为Object[], 不用Arrays.copyOf进行类型检查直接调用System.arraycopy即可
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    elementData[index] = element;
    size++;
}

privatevoidrangeCheckForAdd(int index) {
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

addAll方法调用c.toArray获取c中所有元素:

publicbooleanaddAll(Collection<? extends E> c) {
    Object[] a = c.toArray();
    int numNew = a.length;
    ensureCapacityInternal(size + numNew);  // Increments modCount
    System.arraycopy(a, 0, elementData, size, numNew);
    size += numNew;
    return numNew != 0;
}

publicbooleanaddAll(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;
    if (numMoved > 0)
        System.arraycopy(elementData, index, elementData, index + numNew,
                         numMoved);

    System.arraycopy(a, 0, elementData, index, numNew);
    size += numNew;
    return numNew != 0;
}

访问元素

get方法可以访问指定位置的元素:

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

    return elementData(index);
}

privatevoidrangeCheck(int index) {
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

E elementData(int index) {
    return (E) elementData[index];
}

set方法用于修改某位置的元素, 未发生结构性改变不会修改modCount:

public E set(int index, E element) {
    rangeCheck(index);

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

toArray方法可以将ArrayList中所有元素作为数组返回:

public Object[] toArray() {
    return Arrays.copyOf(elementData, size);
}

public <T> T[] toArray(T[] a) {
    if (a.length < size)
        // Make a new array of a's runtime type, but my contents:
        return (T[]) Arrays.copyOf(elementData, size, a.getClass());
    System.arraycopy(elementData, 0, a, 0, size);
    if (a.length > size)
        a[size] = null;
    return a;
}

出于类型安全的原因, 建议使用第二个toArray方法:

List<String> list = new ArrayList<>();
// add sth in list
String[] arr = new String[list.size()];
arr = list.toArray(arr);

删除元素

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

使用System.arraycopy将index后面的元素向前移动一位, 覆盖被删除的元素.

将最后位置上的元素设为null便于GC进行回收.

remove(obj)方法会移除第一个与obj相同的元素, 相同关系使用obj.equals方法来判断:

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

privatevoidfastRemove(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
}

removeAll(c)方法移除所有包含在容器c中的元素, retainAll(c)方法移除所有未包含在容器c中的元素.

publicbooleanremoveAll(Collection<?> c) {
     Objects.requireNonNull(c);
    return batchRemove(c, false);
}

publicbooleanretainAll(Collection<?> c) {
    Objects.requireNonNull(c);
    return batchRemove(c, true);
}

实际完成该操作的是batchRemove方法:

privatebooleanbatchRemove(Collection<?> c, boolean complement) {
    final Object[] elementData = this.elementData;
    int r = 0, w = 0;
    boolean modified = false;
    try {
        // 遍历ArrayList, 使用`c.contains`判断是否包含
        for (; r < size; r++)
            if (c.contains(elementData[r]) == complement)
                // 将需要保留的元素移动到数组前部
                elementData[w++] = elementData[r];
    } finally {
        // 保持与AbstractCollection的行为一致
        // 即使c.contains抛出异常仍完成操作
        if (r != size) {
            // r != size 说明发生了contains异常. 
            // 将后部未判断的部分移动到前面予以保留
            System.arraycopy(elementData, r,
                             elementData, w,
                             size - r);
            w += size - r;
        }
        if (w != size) {
            // w != size 说明有元素被删除, 执行清理
            for (int i = w; i < size; i++)
                elementData[i] = null;
            modCount += size - w;
            size = w;
            modified = true;
        }
    }
    return modified;
}

迭代器

ArrayList提供了两个迭代器: iterator()listIterator(). 它们都采用fail-fast机制, 即当迭代器遍历过程中容器发生结构性改变时, next方法会抛出ConcurrentModificationException异常, 终止迭代.

所谓结构性改变是指modCount发生改变的情况, 所有的add, removey操作以及clear()方法都会修改modCount. fail-fast机制主要为了应对其它线程修改容器导致的不一致问题.

首先阅读iterator()源码:

// 获得迭代器实例
public Iterator<E> iterator() {
    return new Itr();
}

// 迭代器内部实现类
private class Itr implements Iterator<E> {
    int cursor;       // 下一个要返回元素的下标
    int lastRet = -1; // 上一个返回元素的下标, 默认为-1.
    int expectedModCount = modCount;

    // 检查是否可以继续遍历
    publicbooleanhasNext() {
        return cursor != size;
    }

    // 返回cursor指向的元素, 并将cursor后移一个位置
    @SuppressWarnings("unchecked")
    public E next() {
        // 检查modCount是否一致
        checkForComodification();
        int i = cursor;
        if (i >= size)
            throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i + 1;
        return (E) elementData[lastRet = i];
    }

    // 删除上一次返回的元素
    publicvoidremove() {
        // 检查是否返回过元素(成功调用过next方法), 且该元素未被删除
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();

        try {
            // 删除元素
            ArrayList.this.remove(lastRet);
            // 修正游标位置
            cursor = lastRet;
            // 标记上次返回的元素已被删除, 避免误删
            lastRet = -1;
            // 更新expectedModCount, 保证迭代器可以继续执行
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }
    
   finalvoidcheckForComodification() {
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }

listIterator()可以双向移动, 除了删除元素外还可以在游标位置添加元素:

public ListIterator<E> listIterator() {
    return new ListItr(0);
}

private class ListItr extends Itr implements ListIterator<E> {
    ListItr(int index) {
        super();
        cursor = index;
    }

    publicbooleanhasPrevious() {
        return cursor != 0;
    }

    publicintnextIndex() {
        return cursor;
    }

    publicintpreviousIndex() {
        return cursor - 1;
    }

    @SuppressWarnings("unchecked")
    public E previous() {
        checkForComodification();
        int i = cursor - 1;
        if (i < 0)
            throw new NoSuchElementException();
        Object[] elementData = ArrayList.this.elementData;
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        cursor = i;
        return (E) elementData[lastRet = i];
    }

    publicvoidset(E e) {
        if (lastRet < 0)
            throw new IllegalStateException();
        checkForComodification();

        try {
            ArrayList.this.set(lastRet, e);
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }

    publicvoidadd(E e) {
        checkForComodification();

        try {
            int i = cursor;
            ArrayList.this.add(i, e);
            cursor = i + 1;
            lastRet = -1;
            expectedModCount = modCount;
        } catch (IndexOutOfBoundsException ex) {
            throw new ConcurrentModificationException();
        }
    }
}

ArrayList还有两个内部类用于处理子序列操作:

  • SubList extends AbstractList<E>
  • ArrayListSpliterator<E> implements Spliterator<E>

序列化

ArrayList的序列化会写入modCount, size和实际的元素. 同样会检查modCount是否一致, 以避免并发问题.

privatevoidwriteObject(java.io.ObjectOutputStream s)
    throws java.io.IOException{
    // Write out element count, and any hidden stuff
    int expectedModCount = modCount;
    s.defaultWriteObject();

    // Write out size as capacity for behavioural compatibility with clone()
    s.writeInt(size);

    // Write out all elements in the proper order.
    for (int i=0; i<size; i++) {
        s.writeObject(elementData[i]);
    }

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

自定义序列化机制的根本目的在于避免写入无意义的字段. readObject也按照同样的策略进行重写:

privatevoidreadObject(java.io.ObjectInputStream s)
    throws java.io.IOException, ClassNotFoundException {
    elementData = EMPTY_ELEMENTDATA;

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

    // Read in capacity
    s.readInt(); // ignored

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

        Object[] a = elementData;
        // Read in all elements in the proper order.
        for (int i=0; i<size; i++) {
            a[i] = s.readObject();
        }
    }
}

本文永久更新链接地址http://www.chineselinuxuniversity.net/Linux/2018-01/150357.htm

linux
相关资讯       Java8  ArrayList源码 
本文评论   查看全部评论 (0)
表情: 表情 姓名: 字数

       

评论声明
  • 尊重网上道德,遵守中华人民共和国的各项有关法律法规
  • 承担一切因您的行为而直接或间接导致的民事或刑事法律责任
  • 本站管理人员有权保留或删除其管辖留言中的任意内容
  • 本站有权在网站内转载或引用您的评论
  • 参与本评论即表明您已经阅读并接受上述条款