JDK源码解读-集合(一)-ArrayList

JDK源码解读-集合-ArrayList

如何扩容

private void grow(int minCapacity) {
    // overflow-conscious code
    //获取原集合的长度
    int oldCapacity = elementData.length;
    //扩容到原来的1.5倍
    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);
}

如何在指定位置插入数据

public void add(int index, E element) {
    //先判断传入的下标是否越界
    rangeCheckForAdd(index);
    //判断是否需要扩容
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    //空出index的位置,index后的数据往后移
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    //在index的位置插入指定值
    elementData[index] = element;
    size++;
}

如何删除元素

public E remove(int index) {
    //先判断传入的下标是否越界
    rangeCheck(index);

    //更新修改次数
    modCount++;
    //获取需要删除的对象
    E oldValue = elementData(index);

    //计算需要移动的元素个数, 即index之后元素的个数
    int numMoved = size - index - 1;
    if (numMoved > 0)
        //从元数组的指定位置的下一个位置开始拷贝到元数组的index位置,拷贝numMoved个数据,即把index之后数据向前移动一位
        System.arraycopy(elementData, index+1, elementData, index,
                         numMoved);
    //将最后一位的结果置为null 原来的最后一位的值已经向前移动了,不删除会出现重复
    elementData[--size] = null; // clear to let GC do its work

    return oldValue;
}

怎样求两个集合的交集的

public boolean retainAll(Collection<?> c) {
    Objects.requireNonNull(c);
    //交集的处理方式其实是批量删除不存在的元素对象
    return batchRemove(c, true);
}

private boolean batchRemove(Collection<?> c, boolean complement) {
    //将当前集合的对象赋值给一个新的对象操作。
    final Object[] elementData = this.elementData;
    //双指针法,不需要额外的增加空间;r为读指针,w为写指针
    int r = 0, w = 0;
    boolean modified = false;
    try {
        for (; r < size; r++)
            //如果读指针指向的数据在另一个集合也存在,把当前值更新到原数组中,同时更新写指针
            //由于写指针永远<=读指针, 所以不用担心没读取的数据就被写指针覆盖。该算法也是经常用到的手法
            if (c.contains(elementData[r]) == complement)
                elementData[w++] = elementData[r];
    } finally {
        // Preserve behavioral compatibility with AbstractCollection,
        // even if c.contains() throws.
        //理论上读到的长度和数组的长度应该一致,这里是异常处理。
        if (r != size) {
            //如果不一致;,则把未读的元素都拷贝到写指针之后
            System.arraycopy(elementData, r,
                             elementData, w,
                             size - r);
            w += size - r;
        }
        //如果交集的元素个数不等于原数组的总个数
        if (w != size) {
            // clear to let GC do its work
            //将后面的空对象设置为null,帮助gc回收这些对象;同时更新数组的大小,更新返回值。表示数组已经被修改
            for (int i = w; i < size; i++)
                elementData[i] = null;
            modCount += size - w;
            size = w;
            modified = true;
        }
    }
    return modified;
}

总结

  1. ArrayList每次扩容到原来的1.5倍,删除方法中并没有缩容的操作。如果扩大到1.5被还不满足需求,就扩到需要的大小,前提是不超过最大容量限制。
  2. 删除元素的实现是把指定位置后面的元素移动到index位置,再把最后一位设置为null
  3. 求两个集合的交集,是在原集合中删除另一个集合中不存在的元素。实现方式很巧妙利用的双指针的方式节省了内存空间。

你可能感兴趣的:(JDK集合,JDK原码,ArrayList原码,ArrayList插入数据解析,ArrayList扩容解析)