ArrayList源码分析

ArrayList 底层数据结构

  • 是List一个可调整大小的数组

数组结构的优缺点:

  1. 数组查询快,根据地址和索引直接获取元素
  2. 数组增删慢,每次都需要创建一个新的数组,且移动元素的位置

ArrayList继承关系

Serializable 标记性接口

  1. 类的序列化由实现java.io.Serializable接口的类启用。不实现此接口的类将不会使任何状态序列化或反序列化。可序列化的所有子类型都是可序列化的。标记性接口没有方法或属性。

    序列化:将对象的数据写入文件

    反序列化:将文件中的对象数据读取出来

Cloneable 标记性接口

  1. 一个类实现Cloneable 接口来指示Object.clone() 方法,该方法对于该类的实例进行属性的复制是合法的。在不实现Cloneable 接口的实例上调用对象的克隆方法会抛出异常CloneNotSupportedException

  2. clone条件:

    • 实现 Cloneable 接口
    • 重写clone 方法
    public Object clone() {
            try {
                ArrayList<?> v = (ArrayList<?>) super.clone(); // Object.clone()
                v.elementData = Arrays.copyOf(elementData, size);
                v.modCount = 0;
                return v;
            } catch (CloneNotSupportedException e) {
                // this shouldn't happen, since we are Cloneable
                throw new InternalError(e);
            }
        }
    

    浅克隆:基本数据类型可以完全复制,引用类型不可以,仅仅是拷贝了一份引用

    深克隆:修改引用类型数据也不互相影响

RandomAccess 标记性接口

  1. 表明实现了这个接口的集合是支持快速随机访问的。

  2. 使用for循环的方式获取数据的性能优于用iterator迭代器方式

    开发过程中,先判断返回的list 是否有实现RandomAccess 接口,(list instanceof RandomAccess),再决定用那种遍历方式。

ArrayList源码分析

构造方法

无参构造

// 第一次调用add(E e)方法时才创建一个容量为10的数组
public ArrayList() {
    // 无参构造方法,创建一个初始容量为0的空数组
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    // Object[] elementData
}

带初始容量的构造

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(Collection<? extends E> c) {
    // 构造方法中的集合转数组
    elementData = c.toArray();
    if ((size = elementData.length) != 0) {
        // 防止c.toArray() 不返回Object[]
        if (elementData.getClass() != Object[].class)
            elementData = Arrays.copyOf(elementData, size, Object[].class);
    } else {
        // 空数组
        this.elementData = EMPTY_ELEMENTDATA;
    }
}

add源码分析

list末尾添加一个元素:

public boolean add(E e) {
    // 并发修改的次数+1
    modCount++;
    // size当前元素的个数
    add(e, elementData, size);
    return true;
}
private void add(E e, Object[] elementData, int s) {
    // 当前元素的个数是否等于原数组的长度
    if (s == elementData.length)
        // 进入扩容
        elementData = grow();
    // 扩容后进行元素的赋值
    elementData[s] = e;
    // 元素个数+1
    size = s + 1;
}
private Object[] grow() {
    // 元素个数+1作为最小容量
    return grow(size + 1);
}
private Object[] grow(int minCapacity) {
    // 调用newCapacity算出扩容后的容量,再调用Arrays.copyOf()把原数组的元素复制到扩容后的数组
    return elementData = Arrays.copyOf(elementData,
                                       newCapacity(minCapacity));
}
private int newCapacity(int minCapacity) {
    // 有溢出意识的代码
    int oldCapacity = elementData.length;
    // 扩容为原容量的1.5倍
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    if (newCapacity - minCapacity <= 0) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) //DEFAULTCAPACITY_EMPTY_ELEMENTDATA空数组
            return Math.max(DEFAULT_CAPACITY, minCapacity); //DEFAULT_CAPACITY = 10
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return minCapacity;
    }
    return (newCapacity - MAX_ARRAY_SIZE <= 0)
        ? newCapacity
        : hugeCapacity(minCapacity);
}

在list指定位置添加一个元素:

public void add(int index, E element) {
    // 判断下标是否合法
    rangeCheckForAdd(index);
    // 并发修改次数+1
    modCount++;
    final int s;
    Object[] elementData;
    // 判断是否需要扩容,当元素个数等于数组长度扩容
    if ((s = size) == (elementData = this.elementData).length)
        elementData = grow();
    // 将要插入的下标后所有的元素向后移动一个位置
    System.arraycopy(elementData, index,
                     elementData, index + 1,
                     s - index);
    // 将元素赋值给数组指定位置
    elementData[index] = element;
    // 元素个数+1
    size = s + 1;
}
private void rangeCheckForAdd(int index) {
    // 判断下标是否合法,下标大于元素的个数或下标小于0时不合法抛IndexOutOfBoundsException异常
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}

添加一个单列集合:

public boolean addAll(Collection<? extends E> c) {
    // 将单列集合转为数组,底层调用Arrays.copyOf()方法
    Object[] a = c.toArray();
    // 并发修改的次数 +1
    modCount++;
    // 算出要添加的数组长度
    int numNew = a.length;
    // =0, 返回false, 表示添加失败
    if (numNew == 0)
        return false;
    Object[] elementData;
    final int s;
    // numNew > 0 - 0
    if (numNew > (elementData = this.elementData).length - (s = size))
        elementData = grow(s + numNew);
    // 调用System.arraycopy将要添加的单列集合元素添加到新的list
    System.arraycopy(a, 0, elementData, s, numNew);
    // 修改元素个数
    size = s + numNew;
    return true;
}

在list指定位置添加一个单列集合

public boolean addAll(int index, Collection<? extends E> c) {
    // 判断要添加位置的下标是否合法
    rangeCheckForAdd(index);
	// 将集合转为Object数组
    Object[] a = c.toArray();
    // 并发修改次数 +1
    modCount++;
    // 算出Object数组的长度
    int numNew = a.length;
    if (numNew == 0)
        return false;
    Object[] elementData;
    final int s;
    // 判断数组长度是否大于集合中剩余的元素个数来进行扩容
    if (numNew > (elementData = this.elementData).length - (s = size))
        elementData = grow(s + numNew);

    // 根据集合中元素的个数-要存的索引位置算出集合中要移动的个数
    int numMoved = s - index;
    // 如果大于0, 调用System.arraycopy方法进行数组元素的复制,index + numNew算出新下标
    if (numMoved > 0)  	
        System.arraycopy(elementData, index,
                         elementData, index + numNew,
                         numMoved);
    // 才真正将数据源中的元素添加到集合中
    System.arraycopy(a, 0, elementData, index, numNew);
    // 重新计算集合元素的个数
    size = s + numNew;
    return true;
}

set源码分析

public E set(int index, E element) {
    // 判断index是否合法, 当index下标小于0或大于等于集合中元素的个数时抛异常
    Objects.checkIndex(index, size);
    // 获得index下标的旧值
    E oldValue = elementData(index);
    // 修改下标的值
    elementData[index] = element;
    // 返回旧值
    return oldValue;
}
public static int checkIndex(int index, int length) {
    return Preconditions.checkIndex(index, length, null);
}
int checkIndex(int index, int length,
                   BiFunction<String, List<Integer>, X> oobef) {
        if (index < 0 || index >= length)
            throw outOfBoundsCheckIndex(oobef, index, length);
        return index;
    }

get源码解析

public E get(int index) {
    // 判断index是否合法, 当index下标小于0或大于等于集合中元素的个数时抛异常
    Objects.checkIndex(index, size);
    // 返回index位置的元素
    return elementData(index);
}

toString() 源码解析

public abstract class AbstractCollection<E> implements Collection<E> {
	public String toString() {
        // 获取迭代器
        Iterator<E> it = iterator();
        // 如果迭代器没有元素,直接返回空
        if (! it.hasNext())
            return "[]";

        // 创建一个StringBuilder对象
        StringBuilder sb = new StringBuilder();
        sb.append('[');
        for (;;) {
            // 调用迭代器的方法取出元素
            E e = it.next();
            sb.append(e == this ? "(this Collection)" : e);
            // 如果没有元素了, 调用toString()将StringBulilder对象转成字符串
            if (! it.hasNext())
                return sb.append(']').toString();
            // 如果有元素就调用StringBuilder的append方法追加元素
            sb.append(',').append(' ');
        }
    }
}

迭代器iterator源码分析

public Iterator<E> iterator() {
    return new Itr();
}
// ArrayList的迭代器源码, 不同集合实现迭代器源码的方式不同
private class Itr implements Iterator<E> {
    int cursor;       // 光标, 默认值是0
    int lastRet = -1; // 记录-1
    // 将集合的实际修改次数赋值给预期修改次数
    int expectedModCount = modCount;
	
    // prevent creating a synthetic constructor
    Itr() {}

    // 调用hasNext() 判断集合是否有元素, 比较光标cursor和集合中元素的个数size是否不相等
    public boolean hasNext() {
        return cursor != size;
    }

    @SuppressWarnings("unchecked")
    public E next() {
        // 检查集合实际修改次数是否不等于预期修改次数, 不等于则抛出并发修改异常
        checkForComodification();
        // 将光标赋值给i
        int i = cursor;
        // 如果i >= 集合中的元素,说明没有元素了,抛出没有元素异常
        if (i >= size)
            throw new NoSuchElementException();
        // 把集合存储数据的数组地址赋值该方法的局部变量
        Object[] elementData = ArrayList.this.elementData;
        // 如果 i >= 数组长度,抛出并发修改异常
        if (i >= elementData.length)
            throw new ConcurrentModificationException();
        // 光标向下移动
        cursor = i + 1;
        // 从数组中取出元素并返回
        return (E) elementData[lastRet = i];
    }


    @Override
    public void forEachRemaining(Consumer<? super E> action) {
        Objects.requireNonNull(action);
        final int size = ArrayList.this.size;
        int i = cursor;
        if (i < size) {
            final Object[] es = elementData;
            if (i >= es.length)
                throw new ConcurrentModificationException();
            for (; i < size && modCount == expectedModCount; i++)
                action.accept(elementAt(es, i));
            // update once at end to reduce heap write traffic
            cursor = i;
            lastRet = i - 1;
            checkForComodification();
        }
    }

    final void checkForComodification() {
        // 判断集合实际修改次数是否不等于预期修改次数
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
    }
}

并发修改异常

/**
测试代码=====
**/
ArrayList<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String s = iterator.next();
    if (s.equals("3")) {
        list.remove("3");
    }
}
public boolean remove(Object o) {
    final Object[] es = elementData;
    final int size = this.size;
    int i = 0;
    // java的label语法, 通过命名一个语句块name, 然后调用 break name跳出该语句块
    found: {
        // 判断要删除的元素是否为null
        if (o == null) {
            for (; i < size; i++)
                if (es[i] == null)
                    break found;
        } else {
            for (; i < size; i++)
                if (o.equals(es[i]))
                    break found;
        }
        return false;
    }
    // 调用fastRemove进行删除
    fastRemove(es, i);
    return true;
}
private void fastRemove(Object[] es, int i) {
    // 集合实际修改次数 +1
    modCount++;
    final int newSize;
    if ((newSize = size - 1) > i)
        // (newSize - i) = 计算要被移动的元素个数
        System.arraycopy(es, i + 1, es, i, newSize - i);
    // 让删除的元素置为null, 为了尽快被垃圾回收
    es[size = newSize] = null;
}

在调用add()方法时,实际修改次数会+1,而在获取iterator迭代器时只会执行一次将实际修改次数赋值给预期修改次数,在调用remove(Object o)删除元素时,实际修改次数也会+1,最终导致在下一次next() 的时候检查到实际修改次数不等于预期修改次数,从而抛出并发修改异常。

并发修改异常特殊情况

/**
测试代码=====
**/
ArrayList<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String s = iterator.next();
    if (s.equals("2")) {
        list.remove("2");
    }
}

当要删除的元素在集合的倒数第二个位置的时候,不会产生并发修改异常。

原因是 :因为在调用hasNext方法时,光标cursor的值和元素个数的值size一样,返回false,不会继续调用next方法,也就不会去检查实际修改次数是否不等于预期修改次数。

iterator的默认方法remove

/**
测试代码=====
**/
ArrayList<String> list = new ArrayList<>();
list.add("1");
list.add("2");
list.add("3");
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
    String s = iterator.next();
    if (s.equals("3")) {
        iterator.remove();
    }
}
public void remove() {
    if (lastRet < 0)
        throw new IllegalStateException();
    checkForComodification();

    try {
        ArrayList.this.remove(lastRet);
        cursor = lastRet;
        lastRet = -1;
        // 调用remove()方法,每次会对预期修改次数重新赋值,不会产生并发修改异常
        expectedModCount = modCount;
    } catch (IndexOutOfBoundsException ex) {
        throw new ConcurrentModificationException();
    }
}
 public E remove(int index) {
     Objects.checkIndex(index, size);
     final Object[] es = elementData;

     @SuppressWarnings("unchecked") E oldValue = (E) es[index];
     fastRemove(es, index);

     return oldValue;
 }

clear源码分析

public void clear() {
    // 并发修改次数 +1
    modCount++;
    final Object[] es = elementData;
    for (int to = size, i = size = 0; i < to; i++)
        // 把数组的每一个位置元素都置为null,让垃圾回收器尽快回收
        es[i] = null;
}

contains源码分析

public boolean contains(Object o) {
    // 调用 indexOf(o)方法,如果返回-1,说明没有找到,返回false; 如果返回值>=0,说明找到了,返回true
    return indexOf(o) >= 0;
}
public int indexOf(Object o) {
    return indexOfRange(o, 0, size);
}

int indexOfRange(Object o, int start, int end) {
    Object[] es = elementData;
    // 如果contains的参数是null
    if (o == null) {
        for (int i = start; i < end; i++) {
            if (es[i] == null) {
                return i;
            }
        }
    } else {
        for (int i = start; i < end; i++) {
            if (o.equals(es[i])) {
                // 返回数组下标
                return i;
            }
        }
    }
    return -1;
}

isEmpty源码分析

public boolean isEmpty() {
    // 判断元素个数是否等于0
    return size == 0;
}
posted @ 2020-11-17 22:19  西凉马戳戳  阅读(344)  评论(1编辑  收藏  举报