Java基础-ArrayList源码浅析

以Java8为版本分析

1.认识ArrayList的关键变量

在ArrayList中,关键变量有两个。

//存储对象的数组
transient Object[] elementData;
//数组长度
private int size;
  • 之所以用transient关键词修饰,就是为了防止其被序列化。

当然,还有下面的一些变量

//默认容量
private static final int DEFAULT_CAPACITY = 10;
//空对象数组
private static final Object[] EMPTY_ELEMENTDATA = {};
//空对象数组,在初始化的时候有用。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
  • ArrayList随机返回快的原因就是因为他将元素存在数组里面,线性存储最快的就是数组了吧。

2.先看初始化

有三种初始化的方法。

  • ArrayList()
  • ArrayList(Collection

3.add操作

向ArrayList中添加数据的操作有四中,分别为:

  • add(E e)
  • add(int index, E element)
  • addAll(Collection

3.1 add(E e)

其对应的源代码如下:

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

非常简单的一段代码,其流程如下:

  • 判断容量及做扩容操作
  • 想对象添加到size的位置,并将size+1(利用好++的代码很优雅~)
  • 返回结果

那么,关键性的代码就在于ensureCapacityInterna操作了。我们看看它做了什么。

    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }
  • 简单来说,就是再size+1 和 初始化大小10之间取大值(再一次被优雅的代码折服)

继续跟踪

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

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
  • modCount是什么呢?从注释上来看,是记录这个list被修改的次数的
  • 当前需要的容量大于现有容量的时候。就grow进行扩容操作

我们接下来看扩容操作的逻辑。

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

解释如下:

  • 获取当前容量
  • 新容量为旧容量 + (旧容量右移一位)
  • 若新容量小于需要的容量,则将新容量设置需要的容量
  • 如果新容量大于最大容量了,如果大于MAX_ARRAY_SIZE的话,设置Integer.MAXVALUE,否则为MAX_ARRAY_SIZE
  • 生成一个新的对象数组,并将值复制过去,最后赋值给elementData。

采用这样扩容方法的好处是什么呢?我们想想,党我们一直往里塞数据的时候,容量会增长的越来越快(ArrayList以为我们贪得无厌了~),同样,这样的坏处就是,党我们添加到最后的时候,我们可能申请了很大的空间,但是只往里加多了一个数据,这样就造成了浪费。不过,这都是小事~~~

3.2 addAll(int index, Collection

    public boolean addAll(int index, Collection c) {
        rangeCheckForAdd(index);

        Object[] a = c.toArray();
        int numNew = a.length;
        ensureCapacityInternal(size + numNew);  // Increments modCount

        int numMoved = size - index;
        if (numMoved > 0)
            System.arraycopy(elementData, index, elementData, index + numNew,
                             numMoved);

        System.arraycopy(a, 0, elementData, index, numNew);
        size += numNew;
        return numNew != 0;
    }
  • 将集合转化为对象数组
  • 判断容量及扩容
  • 如果需要移动 将elementData 从index的位置移动到index+numNew的位置
  • 将a中的元素复制到elementData空开的位置

由于那两个方法是native方法,我们在这里就不说了。

4. get操作

    public E get(int index) {
        rangeCheck(index);

        return elementData(index);
    }
  • rangeCheck做检查,如果index大于size就抛出异常。

5.set操作

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

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

6.总结

在知道ArrayList中使用的数据结构以及如何添加数据操作之后,明显对ArrayList理解了很多。

你可能感兴趣的:(Java)