java集合(2):ArrayList源码分析

前言

List列表接口及其实现类是Collection体系结构中的重要一环。List是有序的 collection(也称为序列)。此接口的用户可以对列表中每个元素的插入位置进行精确地控制。用户可以根据元素的整数索引(在列表中的位置)访问元素,并搜索列表中的元素。接口的定义:

public interface List<E>extends Collection<E>

正文

一,List接口概述

  1. List接口解决插入元素的问题,List接口中的特有方法都是围绕着“索引”来定义的,并支持对元素的增删改查。

    get(int index),返回指定位置的元素。
    set(int index,E element),用指定元素替换指定位置的元素。
    add(int index,E element),在指定位置插入指定元素。
    remove(int index),删除指定位置的元素。

  2. 与 set 不同,列表通常允许重复的元素。更确切地讲,列表通常允许满足 e1.equals(e2) 的元素对 e1 和e2,并且如果列表本身允许 null 元素的话,通常它们允许多个 null 元素。

  3. List 接口提供了 4 种对列表元素进行定位(索引)访问方法。列表(像 Java 数组一样)是基于 0的。注意,这些操作可能在和某些实现(例如 LinkedList类)的索引值成比例的时间内执行。因此,如果调用者不知道实现,那么在列表元素上迭代通常优于用索引遍历列表。

  4. 通常,某个线程在Collection上进行迭代时,不允许另一个线程线性修改该Collection,如果检测到这种行为,一些Iterator可能会抛出ConcurrentModificationException异常。原因是在迭代过程中,对集合进行了修改,而迭代器并不知带集合中的变化。解决方法是,List接口提供了特殊的迭代器,称为 ListIterator,除了允许 Iterator接口提供的正常操作外,该迭代器还允许元素插入和替换,以及双向访问。还提供了一个方法来获取从列表中指定位置开始的列表迭代器。

二,ArrayList类

此类提供 List 接口的骨干实现,以最大限度地减少实现“随机访问”数据存储(如数组)支持的该接口所需的工作。ArrayList类底层维护一个数组,所以本身具有“随机访问快,增删慢”的特点。下面我们来看一下ArrayList的部分源码:

  • ArrayList类名
public class ArrayList<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable 
  • ArrayList类成员变量,可见底层维护的是Object数组
    private static final int DEFAULT_CAPACITY = 10;   // ArrayList默认长度

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

    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; // 默认的空Object数组 

    transient Object[] elementData; // 缓存数组,不可序列化

    private int size;  // List长度
  • ArrayList类3个构造方法
    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() {  // 默认长度为10的构造方法
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }


    public ArrayList(Collection<? extends E> c) { // 参数为Collection子类对象的构造方法

        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常用方法之add()方法
    public boolean add(E e) { // 在List末位添加元素
        ensureCapacityInternal(size + 1);  // 首先扩容
        elementData[size++] = e; // 添加元素
        return true;
    }

    public void add(int index, E element) {
        rangeCheckForAdd(index);  // 检查脚标是否越界

        ensureCapacityInternal(size + 1);  // 扩容
        // 下面函数表示index处元素整体往后挪一位
        System.arraycopy(elementData, index, elementData, index + 1, size - index);
        elementData[index] = element; // 放入新元素
        size++;
    }
  • ArrayList常用方法之add()方法的依赖方法
    private void ensureCapacityInternal(int minCapacity) { // 计算所需最小空间,并调用开辟空间的方法
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { // 计算所需最小空间
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity); // 调用开辟空间的方法
    }

    private void ensureExplicitCapacity(int minCapacity) { // 开辟空间的方法
        modCount++;
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);  // 数组扩容方法
    }

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

        // Arrays.copyOf()最终调用native方法实现数组扩容
        elementData = Arrays.copyOf(elementData, newCapacity); 
    }
  • ArrayList获取迭代器的方法:iterator(),listIterator()
详见本人这篇博客中的迭代器详解部分:http://blog.csdn.net/qq_32166627/article/details/72683124
  • ArrayList其他方法要么很简单,要么与add方法类似。不再一一列举。

总结

ArrayList是使用比较多的集合结构,其使用要牢牢掌握。

    原文作者:java集合源码分析
    原文地址: https://blog.csdn.net/qq_32166627/article/details/72704364
    本文转自网络文章,转载此文章仅为分享知识,如有侵权,请联系博主进行删除。
点赞