数据结构-ArrayList浅析

50 阅读2分钟

ArrayList简述

ArrayList 的底层是数组队列,相当于动态数组。与 Java 中的数组相比,它的容量能动态增长。该篇文章 我们只简单分析add get方法

ArrayList 使用

ArrayList<Object> objects1 = new ArrayList<>(10);
objects1.add(2,new boolean[]);
objects1.get(3);
objects1.remove(2);
objects1.remove(new boolean[])

从remove方法就可以看出 里面保存的是对象

1、new ArrayList<>(10)源码分析

public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
    //这个就能看出底层是数组 初始化数组长度为10 因为我们是Object 数组的默认值是null
    //数组默认值参考 https://blog.csdn.net/duyiwuerluozhixiang/article/details/85797745
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
    //如果初始化化传值是0 那么 初始化的数组长度为0
    //Object[] EMPTY_ELEMENTDATA = {}
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                           initialCapacity);
    }
}

2、add方法源码分析 该方法是android.jar提供的

public void add(int index, E element) {
    //size指的是我们添加的数据的长度 不包含数组默认值数量 比如 {2,3,5,0,0} 如果这个0不是用户传入的 那么size是3
    if (index > size || index < 0)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
    //判断是否要扩容
    ensureCapacityInternal(size + 1);  // Increments modCount!!
    //System.arraycopy和Arrays.copyOf()的区别 可参考 https://www.jianshu.com/p/840976f14950
    //主要是为了插入 选择插入的位置 通过移动的方式 将index后面的数据依次往后移动一个
    System.arraycopy(elementData, index, elementData, index + 1,
                     size - index);
    //插入数据
    elementData[index] = element;
    size++;
}
--------------------------------------------
//ensureCapacityInternal 方法
//此刻minCapacity 是 size+1
private void ensureCapacityInternal(int minCapacity) {
    //如果构造函数initialCapacity 传入的是0 那么elementData是空数组 如果不是0 那就走后面
    if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        //DEFAULT_CAPACITY默认值是10
        minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
    }
    //elementData不是空数组
    ensureExplicitCapacity(minCapacity);
}

private void ensureExplicitCapacity(int minCapacity) {
    modCount++;

    // overflow-conscious code
    //如果真实的数组数据长度大于之前设置数组的数据长度 那么就扩容
    if (minCapacity - elementData.length > 0)
        grow(minCapacity);
}

--------------------------------------
//grow 扩容方法
private void grow(int minCapacity) {
    // overflow-conscious code
    int oldCapacity = elementData.length;
    //通过位运算 使其新数组长度是之前的1.5倍
    //负数在二进制的表示 https://www.jianshu.com/p/6c518e7b4690
    //位运算的左移右移 https://blog.51cto.com/u_13536788/3231257
    int newCapacity = oldCapacity + (oldCapacity >> 1);
    //如果扩容后的数组长度 还是小于真实长度 那么这个地方索性就直接使用真实长度
    if (newCapacity - minCapacity < 0)
        newCapacity = minCapacity;
    //MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8
    if (newCapacity - MAX_ARRAY_SIZE > 0)
        newCapacity = hugeCapacity(minCapacity);
    // minCapacity is usually close to size, so this is a win:
    //这个地方是直接复制一个新数组 长度为newCapacity 并将之前数据的数据copy到新数组来
    elementData = Arrays.copyOf(elementData, newCapacity);
}

private static int hugeCapacity(int minCapacity) {
    if (minCapacity < 0) // overflow
        throw new OutOfMemoryError();
    return (minCapacity > MAX_ARRAY_SIZE) ?
        Integer.MAX_VALUE :
        MAX_ARRAY_SIZE;
}

3、get方法

objects1.get(3);

public E get(int index) {
    //index >= size 虽然数组默认长度可能比较大 如{1,3,4,0,0} 因为这个0 可能是初始默认值
    //size呢 是我们真实通过add添加进去的 所以比较的是index >= size 而不是index >= elementData.length
    if (index >= size)
        throw new IndexOutOfBoundsException(outOfBoundsMsg(index));

    return (E) elementData[index];
}

注意

1、使用默认构造器实例化的ArrayList,在第一次添加元素和第11次添加元素时才会触发扩容。也就是第一次时,和超过数组容量时添加才触发扩容。

2、ArrayList 和 LinkedList` 都是不同步的,也就是不保证线程安全;因为没有用到锁的机制

总结

我们日常使用的都是add get方法 ,该文章也只是针对这个方法的分析。如果想更多的了解ArrayList原理以及它的优缺点。可参考以下文章

ArrayList源码扩容机制分析

面试必备:ArrayList源码解析(JDK8) 强烈推荐!!!