freyhe

导航

List源码分析

源码分析

Vector源码

public Vector() {
    this(10);//指定初始容量initialCapacity为10
}
public Vector(int initialCapacity) {
    this(initialCapacity, 0);//指定capacityIncrement增量为0
}
public Vector(int initialCapacity, int capacityIncrement增量为0) {
    super();
    //判断了形参初始容量initialCapacity的合法性
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    //创建了一个Object[]类型的数组
    this.elementData = new Object[initialCapacity];//默认是10
    //增量,默认是0,如果是0,后面就按照2倍增加,如果不是0,后面就按照你指定的增量进行增量
    this.capacityIncrement = capacityIncrement;
}
//synchronized意味着线程安全的   
public synchronized boolean add(E e) {
    modCount++;
    //看是否需要扩容
    ensureCapacityHelper(elementCount + 1);
    //把新的元素存入[elementCount],存入后,elementCount元素的个数增1
    elementData[elementCount++] = e;
    return true;
}

private void ensureCapacityHelper(int minCapacity) {
    // overflow-conscious code
    //看是否超过了当前数组的容量
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);//扩容
}
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;//获取目前数组的长度
    //如果capacityIncrement增量是0,新容量 = oldCapacity的2倍
    //如果capacityIncrement增量是不是0,新容量 = oldCapacity + capacityIncrement增量;
    int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
                                     capacityIncrement : oldCapacity);

    //如果按照上面计算的新容量还不够,就按照你指定的需要的最小容量来扩容minCapacity
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;

    //如果新容量超过了最大数组限制,那么单独处理
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);

    //把旧数组中的数据复制到新数组中,新数组的长度为newCapacity
    elementData = Arrays.copyOf(elementData, newCapacity);
}
public boolean remove(Object o) {
    return removeElement(o);
}
public synchronized boolean removeElement(Object obj) {
    modCount++;
    //查找obj在当前Vector中的下标
    int i = indexOf(obj);
    //如果i>=0,说明存在,删除[i]位置的元素
    if (i >= 0) {
        removeElementAt(i);
        return true;
    }
    return false;
}
public int indexOf(Object o) {
    return indexOf(o, 0);
}
public synchronized int indexOf(Object o, int index) {
    if (o == null) {//要查找的元素是null值
        for (int i = index ; i < elementCount ; i++)
            if (elementData[i]==null)//如果是null值,用==null判断
                return i;
    } else {//要查找的元素是非null值
        for (int i = index ; i < elementCount ; i++)
            if (o.equals(elementData[i]))//如果是非null值,用equals判断
                return i;
    }
    return -1;
}
public synchronized void removeElementAt(int index) {
    modCount++;
    //判断下标的合法性
    if (index >= elementCount) {
        throw new ArrayIndexOutOfBoundsException(index + " >= " +
                                                 elementCount);
    }
    else if (index < 0) {
        throw new ArrayIndexOutOfBoundsException(index);
    }

    //j是要移动的元素的个数
    int j = elementCount - index - 1;
    //如果需要移动元素,就调用System.arraycopy进行移动
    if (j > 0) {
        //把index+1位置以及后面的元素往前移动
        //index+1的位置的元素移动到index位置,依次类推
        //一共移动j个
        System.arraycopy(elementData, index + 1, elementData, index, j);
    }
    //元素的总个数减少
    elementCount--;
    //将elementData[elementCount]这个位置置空,用来添加新元素,位置的元素等着被GC回收
    elementData[elementCount] = null; /* to let gc do its work */
}

ArrayList 源码

JDK1.6源码

public ArrayList() {
    this(10);//指定初始容量为10
}
public ArrayList(int initialCapacity) {
    super();
    //检查初始容量的合法性
    if (initialCapacity < 0)
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    //数组初始化为长度为initialCapacity的数组
    this.elementData = new Object[initialCapacity];
}

JDK1.7源码

private static final int DEFAULT_CAPACITY = 10;//默认初始容量10
private static final Object[] EMPTY_ELEMENTDATA = {};
public ArrayList() {
    super();
    this.elementData = EMPTY_ELEMENTDATA;//数组初始化为一个空数组
}
public boolean add(E e) {
    //查看当前数组是否够多存一个元素
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    elementData[size++] = e;
    return true;
}
private void ensureCapacityInternal(int minCapacity) {
    if (elementData == EMPTY_ELEMENTDATA) {//如果当前数组还是空数组
        //minCapacity按照 默认初始容量和minCapacity中的的最大值处理
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    //看是否需要扩容处理
    ensureExplicitCapacity(minCapacity);
}
//...

JDK1.8源码

private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;//初始化为空数组
}
public boolean add(E e) {
    //查看当前数组是否够多存一个元素
    ensureCapacityInternal(size + 1);  // Increments modCount!!

    //存入新元素到[size]位置,然后size自增1
    elementData[size++] = e;
    return true;
}
private void ensureCapacityInternal(int minCapacity) {
    //如果当前数组还是空数组
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        //那么minCapacity取DEFAULT_CAPACITY与minCapacity的最大值
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    //查看是否需要扩容
    ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
    modCount++;//修改次数加1

    // 如果需要的最小容量  比  当前数组的长度  大,即当前数组不够存,就扩容
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;//当前数组容量
    int newCapacity = oldCapacity + (oldCapacity >> 1);//新数组容量是旧数组容量的1.5倍
    //看旧数组的1.5倍是否够
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    //看旧数组的1.5倍是否超过最大数组限制
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);

    //复制一个新数组
    elementData = Arrays.copyOf(elementData, newCapacity);
}

public boolean remove(Object o) {
    //先找到o在当前ArrayList的数组中的下标
    //分o是否为空两种情况讨论
    if (o == null) {
        for (int index = 0; index < size; index++)
            if (elementData[index] == null) {//null值用==比较
                fastRemove(index);
                return true;
            }
    } else {
        for (int index = 0; index < size; index++)
            if (o.equals(elementData[index])) {//非null值用equals比较
                fastRemove(index);
                return true;
            }
    }
    return false;
}
private void fastRemove(int index) {
    modCount++;//修改次数加1
    //需要移动的元素个数
    int numMoved = size - index - 1;

    //如果需要移动元素,就用System.arraycopy移动元素
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);

    //将elementData[size-1]位置置空,让GC回收空间,元素个数减少
    elementData[--size] = null; // clear to let GC do its work
}

public E remove(int index) {
    rangeCheck(index);//检验index是否合法

    modCount++;//修改次数加1

    //取出[index]位置的元素,[index]位置的元素就是要被删除的元素,用于最后返回被删除的元素
    E oldValue = elementData(index);

    //需要移动的元素个数
    int numMoved = size - index - 1;

    //如果需要移动元素,就用System.arraycopy移动元素
    if (numMoved > 0)
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    //将elementData[size-1]位置置空,让GC回收空间,元素个数减少
    elementData[--size] = null; // clear to let GC do its work

    return oldValue;
}

public E set(int index, E element) {
    rangeCheck(index);//检验index是否合法

    //取出[index]位置的元素,[index]位置的元素就是要被替换的元素,用于最后返回被替换的元素
    E oldValue = elementData(index);
    //用element替换[index]位置的元素
    elementData[index] = element;
    return oldValue;
}
public E get(int index) {
    rangeCheck(index);//检验index是否合法

    return elementData(index);//返回[index]位置的元素
}

public int indexOf(Object o) {
    //分为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;
}
public int lastIndexOf(Object o) {
    //分为o是否为空两种情况
    if (o == null) {
        //从后往前找
        for (int i = size-1; i >= 0; i--)
            if (elementData[i]==null)
                return i;
    } else {
        for (int i = size-1; i >= 0; i--)
            if (o.equals(elementData[i]))
                return i;
    }
    return -1;
}

LinkedList源码

int size = 0;
Node<E> first;//记录第一个结点的位置
Node<E> last;//记录最后一个结点的位置

private static class Node<E> {
    E item;//元素数据
    Node<E> next;//下一个结点
    Node<E> prev;//前一个结点

    Node(Node<E> prev, E element, Node<E> next) {
        this.item = element;
        this.next = next;
        this.prev = prev;
    }
}

public boolean add(E e) {
    linkLast(e);//默认把新元素链接到链表尾部
    return true;
}
void linkLast(E e) {
    final Node<E> l = last;//用l 记录原来的最后一个结点

    //创建新结点
    final Node<E> newNode = new Node<>(l, e, null);
    //现在的新结点是最后一个结点了
    last = newNode;

    //如果l==null,说明原来的链表是空的
    if (l == null)
        //那么新结点同时也是第一个结点
        first = newNode;
    else
        //否则把新结点链接到原来的最后一个结点的next中
        l.next = newNode;
    //元素个数增加
    size++;
    //修改次数增加
    modCount++;
}
public boolean remove(Object o) {
    //分o是否为空两种情况
    if (o == null) {
        //找到o对应的结点x
        for (Node<E> x = first; x != null; x = x.next) {
            if (x.item == null) {
                unlink(x);//删除x结点
                return true;
            }
        }
    } else {
        //找到o对应的结点x
        for (Node<E> x = first; x != null; x = x.next) {
            if (o.equals(x.item)) {
                unlink(x);//删除x结点
                return true;
            }
        }
    }
    return false;
}
E unlink(Node<E> x) {//x是要被删除的结点
    // assert x != null;
    final E element = x.item;//被删除结点的数据
    final Node<E> next = x.next;//被删除结点的下一个结点
    final Node<E> prev = x.prev;//被删除结点的上一个结点

    //如果被删除结点的前面没有结点,说明被删除结点是第一个结点
    if (prev == null) {
        //那么被删除结点的下一个结点变为第一个结点
        first = next;
    } else {//被删除结点不是第一个结点
        //被删除结点的上一个结点的next指向被删除结点的下一个结点
        prev.next = next;
        //断开被删除结点与上一个结点的链接
        x.prev = null;//使得GC回收
    }

    //如果被删除结点的后面没有结点,说明被删除结点是最后一个结点
    if (next == null) {
        //那么被删除结点的上一个结点变为最后一个结点
        last = prev;
    } else {//被删除结点不是最后一个结点
        //被删除结点的下一个结点的prev执行被删除结点的上一个结点
        next.prev = prev;
        //断开被删除结点与下一个结点的连接
        x.next = null;//使得GC回收
    }
    //把被删除结点的数据也置空,使得GC回收
    x.item = null;
    //元素个数减少
    size--;
    //修改次数增加
    modCount++;
    //返回被删除结点的数据
    return element;
}

面试题

ArrayList是否会自动缩容

既然ArrayList的自动扩容一般是发生在add()addAll()方法中,那么当ArrayList调用remove()方法时是否会自动缩容呢?

ArrayList.remove()源码分析

remove()方法有两个

  1. 入参为数组下标返回被删除的元素
  2. 入参为数组元素返回数组下标

PS:本文研究的JDK源码为Oracle JDK 1.8

这里对第1个方法的源码进行分析

public E remove(int index) {
    // 校验传入的下标是否大于或等于当前ArrayList的长度size
    // 如果超过直接抛出IndexOutOfBoundsException
    rangeCheck(index);

    // 修改次数自增
    // modCount是用于快速失败机制的,这里不展开
    modCount++;
    // 获取需要删除的元素的值
    E oldValue = elementData(index);

    // 计算数组从需要删除的元素后需要移动的元素个数
    int numMoved = size - index - 1;
    if (numMoved > 0)
        // 使用System.arraycopy()来进行数组元素左移
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    // 多余的元素标记为null便于gc回收,同时size自减
    elementData[--size] = null;
    // 返回被删除的元素值
    return oldValue;
}

PS:System.arraycopy()并不会影响源数组和目标数组的长度。

ArrayList.remove()源码图解

image-20220306080142947

从上面可以看出ArrayList在执行remove()方法时,内置的Object数组实际上并没有减少长度,只是通过数组部分元素左移、最后一个元素置为null同时size自减来实现删除操作。而且入参为数组元素返回数组下标的remove()removeAll()removeRange()也是类似的做法。

ArrayList.clear()源码分析

除了remove()以外,ArrayList还有一个clear()方法用来对数组进行清空。那它又是怎么工作的呢?实际上简单到我不敢相信

public void clear() {
    // 修改次数自增
    modCount++;

    // 遍历数组并置为null便于gc回收
    for (int i = 0; i < size; i++)
        elementData[i] = null;
    // 直接给size赋值为0
    size = 0;
}

结论

从上面两节源码的分析可以得知,实际上ArrayList是不提供自动缩容机制的。其实这样设计也无可厚非,毕竟很多时候都无法判断是否需要进行缩容操作。

没有自动缩容,ArrayList可以进行缩容吗

当然是可以,那就是trimToSize()方法。这个方法会将ArrayList内置的数组缩容到当前的size,源码也特别简单,就是给elementData一个对应size长度的数组。源码如下

public void trimToSize() {
    // 修改次数自增
    modCount++;
    // 判断当前是否需要缩容
    if (size < elementData.length) {
        // 如果size为0,直接给elementData赋值内置的空数组
        // 不为0则创建一个size长度的新数组
        elementData = (size == 0)
            ? EMPTY_ELEMENTDATA
            : Arrays.copyOf(elementData, size);
    }

总结

  1. ArrayListremove只是通过数组部分元素左移、最后一个元素置为null同时size自减来实现删除操作。实际上并未对elementData进行缩容。
  2. 可以使用trimToSize()方法对elementData进行缩容。

posted on 2022-03-07 21:32  freyhe  阅读(34)  评论(0编辑  收藏  举报