List集合(一)之ArrayList的实现原理

我们知道常用的List集合有ArrayList、LinkedList、Vector,它们都实现了List接口,下面我们分别以构造方法、增删改查等角度分析它们的底层实现,让我们开始吧……

文章目录

      • ArrayList
        • 构造方法
        • 添加元素
        • 修改元素
        • 删除元素
        • 遍历
      • Vector

ArrayList

构造方法

transient Object[] elementData; 
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {
     };
public ArrayList() {
     
	this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

从无参构造方法中可以看出,ArrayList底层就是使用数组进行数据存储的,默认初始化一个容量为0的数组。

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

有参构造,初始化容量initialCapacity,如果>0,则实例化一个initialCapacity大小的数组,如果=0,则实例化一个空数组。

添加元素

    // 默认容量为10
	private static final int DEFAULT_CAPACITY = 10;
	
	// 添加元素,赋值给数组的下一个位置
	public boolean add(E e) {
     
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
    
    // 确定数组的内部容量
    private void ensureCapacityInternal(int minCapacity) {
     
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    
    // 计算容量,如果添加首个元素,那么minCapacity为默认容量大小10
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
     
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
     
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }
    
    // 如果当前数组的容量不满足当前数组所需要的最小容量,进行扩容
    private void ensureExplicitCapacity(int minCapacity) {
     
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
    
    // 数组进行1.5倍扩容
    private void grow(int minCapacity) {
     
        // overflow-conscious code
        int oldCapacity = elementData.length;
        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);
    }
    // 将数组的容量扩容到Integer.MAX_VALUE
    private static int hugeCapacity(int minCapacity) {
     
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

ArrayList添加元素,本质上就是数组添加元素,因为数组添加元素需要初始化大小,所以数组这里给了默认的大小10,每次新增元素之前,都要对数组的大小进行确认,如果数组的大小不满足添加元素所需要的数组最小容量时,则需要对数组进行扩容,扩容的大小,这里运用的右移位运算,右移1位等于除以2的1次幂,所以这里每次是1.5倍扩容。

至于后面为什么还要加上newCapacity < minCapacity的判断呢,那是因为假如oldCapacity无限接近Integer.MAX_VALUE时,再进行1.5倍的扩容会得到负数,(至于为什么会等于负数,因为Integer在JVM中占用4个字节,每个字节8bit,也就是32位,具体大家可以看一下深入浅出之原码、反码、补码)

假如数组扩容后newCapacity大于MAX_ARRAY_SIZE,那么直接将数组的大小设置为Integer.MAX_VALUE。

ArrayList中期望的数组最大容量是Integer.MAX_VALUE - 8,JDK的注释中给了解释:Some VMs reserve some header words in an array. Attempts to allocate larger arrays may result in OutOfMemoryError: Requested array size exceeds VM limit(一些虚拟机在数组中保留了一些头部,尝试分配更大的数组可能会导致OutOfMemoryError:请求的数组大小超过VM限制)。

修改元素

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

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

修改指定位置的元素,返回旧值

删除元素

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

如果删除的元素为null,则删除集合中的所有null值,否则调用遍历出被删除元素所在的位置,调用fastRemove(index)

	private void fastRemove(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
    }

代码很简单,计算数组中被删除元素右侧需要移动的位数,调用System.arraycopy将删除元素后面的值整体向前复制挪一位,再将数组最后一位的值置为null,达到数组删除元素后面的值整体前移的效果。

遍历

  • 迭代器
  • for循环

Vector

数据结构和ArrayList一样,都是数组,实现方法也类似,只不过在方法头上加了synchronized关键字,所以是线程安全的。

你可能感兴趣的:(Java基础-集合,java,arraylist)