栏目分类:
子分类:
返回
名师互学网用户登录
快速导航关闭
当前搜索
当前分类
子分类
实用工具
热门搜索
名师互学网 > IT > 软件开发 > 后端开发 > Java

ArrayList 源码解析 - 基于 1.8

Java 更新时间: 发布时间: IT归档 最新发布 模块sitemap 名妆网 法律咨询 聚返吧 英语巴士网 伯小乐 网商动力

ArrayList 源码解析 - 基于 1.8

ArrayList 继承自 AbstractList 类,并且实现了 List,RandomAccess,Cloneable,java.io.Serializable 接口

public class ArrayList extends AbstractList
        implements List, RandomAccess, Cloneable, java.io.Serializable
{}

使用 ArrayList 的空参构造器实例化对象,会初始化一个长度为 0 空数组。由此可以看出,底层使用的是 Object 类型的数组存储数据

private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};


transient Object[] elementData;

public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

从上面代码中可以看出,初始化的时候,数组的长度为0,那么何时扩容数组的长度呢?其实是在第一次 add 的时候。
add 的代码比较多,一步步来分析

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

复杂内容其实是在 ensureCapacityInternal(size + 1); 这个里面,先抛开这个不说。
size 表示当前数组的长度,由于类型是 int,所以默认值为 0。
elementData[size++] = e; 可以拆分为 elementData[size] = e; size = size + 1;
第一次添加数据时,可以表示为:elementData[0] = e; size = 0+1; 即 size = 1
最后返回 true。

但是问题来了,初始化时数组的长度为空,即长度为0,是无法往数组中添加数据的,需要将数组扩容。
扩容的代码就在 ensureCapacityInternal(size + 1) 方法里。
ensureCapacityInternal:翻译过来就是,确保数组内部的容量

private void ensureCapacityInternal(int minCapacity) {
    // 源代码:
    //ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    
    
    int minCapacity = calculateCapacity(elementData, minCapacity);
    ensureExplicitCapacity(minCapacity);
}

// 数组默认容量
private static final int DEFAULT_CAPACITY = 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);
}

首先调用 calculateCapacity 方法计算数组的最小容量。

最小容量什么意思呢?用白话说,就是每次添加数据的时候,要确保当前数组的容量要大于计算出来的最小容量。如果当前数组的容量小于计算出来的最小容量,就意味着数组内没有可以存放新数据的位置了,此时就需要扩容数组的容量。

从计算最小容量的代码中可以看出:

  • 第一次添加数据时,minCapacity = 1

calculateCapacity 方法头上的参数 minCapacity 的实际意义是:每次添加数据的时候,期望的最小容量就是当前数组的容量+1,因为只有当前数组的容量+1 才能新增加一条数据。

  • 第一次添加数据时,elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA 成立,所以返回的最小容量是 DEFAULT_CAPACITY = 10

只有第一次添加数据时,计算出来的最小容量才为 10,其余时候,计算出来的最小容量都是当前数组的容量+1

  • 由此可见,第一次添加数据时,扩容的容量为 10

接下来是 ensureExplicitCapacity 这个方法。
这个方法内部逻辑理解起来比较简单,就是说:如果计算出来的最小容量大于当前数组的容量,就执行扩容的逻辑,反之则不扩容。
真正的核心代码在 grow 里,这个才是扩容的真面目

// 数组的最大长度,2147483639
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;

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

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

oldCapacity:表示当前数组的容量,即当前数组的长度。
newCapacity:表示扩容后数组的容量。

  • >> 1 右移一位,其实就是除以 2 这个操作
    • 以此类推,>> 2 右移两位,表示除以 2 的 2 次方
    • >> 3 右移三位,表示除以 2 的 3 次方
    • >> n 右移 n 位,表示除以 2 的 n 次方
  • oldCapacity + (oldCapacity >> 1) 可以写成 oldCapacity + (oldCapacity / 2) 实际含义是扩容 1.5 倍

如果扩容后数组的容量还比计算出来的最小容量小的话,就把数组容量扩容至计算出来的最小容量。

这种情况只会在第一次添加数据,扩容数组容量时发生。

如果扩容后数组的容量大于数组允许的最大容量,就调用 hugeCapacity 方法(这种情况很少发生)
最后使用 Arrays.copyOf 方法进行数组的扩容。

转载请注明:文章转载自 www.mshxw.com
本文地址:https://www.mshxw.com/it/302115.html
我们一直用心在做
关于我们 文章归档 网站地图 联系我们

版权所有 (c)2021-2022 MSHXW.COM

ICP备案号:晋ICP备2021003244-6号