arraylist原理探究,年轻人不讲武德

This class is a member of the Java Collections Framework.

arraylist是Collections中的一员,底层的存储数据结构是一个Object数组,这就意味着arraylist存放的数据类型只能为引用数据类型、NULL,引用数据类型包括(Integer、String、Boolean、Char、Byte、Float、Double、Long、Short)。而且。数组的优点有查询快、时间复杂度为o(1)。缺点:长度固定无法自动扩容,但是arraylist会自动扩容

transient Object[] elementData;

空参构造函数

给elementData声明了一个默认的空的数组实例,从这里可以看出我们在new一个arraylist的时候,arraylist这时候还是一个空的数组。为了减少内存开销嘛,也合情合理。

/**
     * Shared empty array instance used for default sized empty instances. We
     * distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
     * first element is added.
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
 /**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

带参构造函数

这个其实就是指定arraylist容量大小的,为elementData赋值。其中arraylist中的elementData与size我们需要做一个区分。

  • elementData.length:arraylist容量的大小
  • size:arraylist添加元素的个数、arraylist.size也是指的这个size、也是arraylist是否为empty的标志
	public int size() {
        return size;
    }
    public boolean isEmpty() {
        return size == 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);
        }
    }

接着看另外的带参数的构造函数,这个参数的类型是一个Collection的类型,可以传List(arraylist、linklist、list的儿子…)。类似linklist向arraylist的转换、或者说是arraylist间的拷贝

public ArrayList(Collection<? extends E> c) {
//把参数数组化
        Object[] a = c.toArray();
//入参的长度不为0
        if ((size = a.length) != 0) {
//入参是arraylist类型的直接赋值
            if (c.getClass() == ArrayList.class) {
                elementData = a;
            } else {
//其他类型的转成数组在进行赋值
                elementData = Arrays.copyOf(a, size, Object[].class);
            }
        } else {
//入参长度为0,直接赋值空数组
            // replace with empty array.
            elementData = EMPTY_ELEMENTDATA;
        }
    }

接下来看contains方法,根据元素下标来判断的。如果arraylist包含元素一定有下标且为正数。其余情况为不包含返回负数

public boolean contains(Object o) {
        return indexOf(o) >= 0;
    }
    public int indexOf(Object o) {
        if (o == null) {
        //说明arraylist可以添加null
            for (int i = 0; i < size; i++)
                if (elementData[i]==null)
                    return i;
        } else {
        //elementData中有此值,返回值的下标
            for (int i = 0; i < size; i++)
                if (o.equals(elementData[i]))
                    return i;
        }
        return -1;
    }

接下来看add方法,这个方法算是arraylist的核心方法了,逐层剖析下面几个方法。

  • ensureCapacityInternal 确保内部容量
  • ensureExplicitCapacity 是否需要扩容
  • calculateCapacity:计算容量
javascript boolean add(E e) {
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }
private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }

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

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

calculateCapacity方法:计算容量 这里只是单纯的计算容量分为俩种情况

  1. arraylist 第一次添加元素
  2. arraylist 非第一次添加元素

new ArrayList() 第一次添加元素size == 0、elementData==DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {},此时初始化arraylist,初始化容量为10

new ArrayList(4) 第一次添加元素size == 0、elementData==EMPTY_ELEMENTDATA = {},此时初始化arraylist,初始化容量为1

DEFAULTCAPACITY_EMPTY_ELEMENTDATA != EMPTY_ELEMENTDATA

其他非第一次添加元素直接size++

private static final int DEFAULT_CAPACITY = 10;  
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//如果此时elementData并没有初始化,所以elementData=={}、size==0,此时初始化arraylist容量为DEFAULT_CAPACITY == 10
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
//其他情况下直接返回size+1
        return minCapacity;
    }
public static int max(int a, int b) {
        return (a >= b) ? a : b;
    }

ensureExplicitCapacity方法: 判断是否需要扩容、是否扩容了都记录修改次数

扩容的条件:计算出来的minCapacity最小容量>此时arraylist的容量

 private void ensureExplicitCapacity(int minCapacity) {  
    modCount++;                                                                                
    // overflow-conscious code                          
    if (minCapacity - elementData.length > 0)           
        grow(minCapacity);                              
}                                                       

其中modCount是一个标识符号,记录修改次数的

 protected transient int modCount = 0;

grow方法:初始化容量 无参的arraylis进行add()时候调用grow()是初始化容量
grow方法:扩容 当计算出来的容量>arraylist当前的容量,进行扩容增长elementData数组的长度
扩容规则:

  1. 计算出来的容量<1.5倍旧容量 新容量=1.5倍扩容
  2. 计算出来的容量>1.5倍旧容量 新容量=计算出来的容量
  3. 1.5倍旧容量> MAX_ARRAY_SIZE 新容量=interger.max
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        //新容量为旧容量的1.5倍
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //1.5倍的情况下还是不满足,直接扩大容量至计算出来的那个容量
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //新容量比计算机能承受的最大容量-8还大,最大容量为integer.length
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        //扩容数组且copy给elementData
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

ensureCapacityInternal方法: 容量保证充足可以添加进去元素,开始真正添加元素

到此arraylist添加元素就和普通数组添加元素一样了

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

附页(反射查看arraylist容量大小)

  • 无参arraylist第一次执行add方法的时候容量为10,未执行add方法容量为0
  • 带参arraylist在创建arraylis对象的时候即指定了容量
		ArrayList<Object> list = new ArrayList<>();
        list.add(1);
        Class clazz=Class.forName("java.util.ArrayList");
        Field elementData=clazz.getDeclaredField("elementData");
        elementData.setAccessible(true);
        Object[] arr=(Object[]) elementData.get(list);
        //容量大小
        System.out.println(arr.length);
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

小咸鱼的技术窝

你的鼓励将是我最大的动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值