JDK14 中ArrayList的动态扩容简单介绍

JDK14 中ArrayList的动态扩容简单介绍

一、简介

1.1集合

  • 集合与数组的区别:

    数组集合
    长度固定可变
    存储内容同一类型的元素,可以存储基本数据类型值。存储的都是对象,而且对象的类型可以不一致。在开发中一般当对象多的时候,使用集合进行存储。
  • 集合按照其存储结构可以分为两大类

    • 单列集合 java.util.Collection
    • 双列集合 java.util.Map

1.2Colleciton接口

  • Collection是所有单列集合的父接口,集合是java中提供的一种容器,可以用来存储多个数据。

  • 它有两个重要的子接口,分别是 java.util.List 和 java.util.Set 。

  • List 的特点是元素有序、元素可重复。List 接口的主要实现类有 java.util.ArrayList 和 java.util.LinkedList

  • Set 的特点是元素无序,而且不可重复。Set 接口的主要实现类有 java.util.HashSet 和 java.util.TreeSet。

  • 常用功能(接口的所有抽象方法都需要被子类实现)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zaISST2v-1627564150953)(图片/image-20210729164729243.png)]

1.3List接口

  • 特点:

    1. 它是一个元素存取有序的集合。例如,存元素的顺序是11、22、33。那么集合中,元素的存储就是按照11、22、33的顺序完成的)。
    2. 它是一个带有索引的集合,通过索引就可以精确的操作集合中的元素(与数组的索引是一个道理)
    3. 集合中可以有重复的元素,通过元素的equals方法,来比较是否为重复的元素。
  • 常用方法(接口的所有抽象方法都需要被子类实现)

    [外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-x7GfRpDG-1627564150955)(图片/image-20210729164925062.png)]

二、ArrayList

2.1概述

ArrayList 是一个可以实现动态扩容的Object数组

public class ArrayList<E> extends AbstractList<E>
        implements List<E>, RandomAccess, Cloneable, java.io.Serializable

它继承了AbstractList这个抽象类,实现了List, RandomAccess, Cloneable, java.io.Serializable四个接口。

  • ArrayList 继承了AbstractList,实现了List。它是一个数组队列,提供了相关的添加、删除、修改、遍历等功能。
  • ArrayList *实现了RandmoAccess接口,即提供了随机访问功能。*RandmoAccess是java中用来被List实现,为List提供快速访问功能的。
  • 在ArrayList中,我们即可以通过元素的序号快速获取元素对象;这就是快速随机访问。
  • ArrayList 实现了Cloneable接口,即覆盖了函数clone(),能被克隆。
  • ArrayList 实现java.io.Serializable接口,这意味着ArrayList支持序列化,能通过序列化去传输。

和Vector不同,ArrayList中的操作不是线程安全的

所以,建议在单线程中才使用ArrayList,而在多线程中可以选择Vector或者CopyOnWriteArrayList。

2.2构造器

// 默认构造函数
ArrayList()

// capacity是ArrayList的默认容量大小。
ArrayList(int capacity)

// 创建一个包含collection的ArrayList
ArrayList(Collection<? extends E> collection)

三、ArrayList的动态扩容简单介绍

Demo

package com.test0729.demo;

import java.util.ArrayList;

public class ArrayListDemo {

    public static void main(String[] args) {
        // AarryList: 动态数组实现,查找快,删除增加慢
        ArrayList<Integer> data = new ArrayList<>();
        // 无参构造函数:创建一个长度为0的空数组
        data.add(100);
        // add:只return true
        // 第一次add,需要扩容,默认长度为10,所以数组的长度变为10
    }
}

无参构造器

   /**
     * Constructs an empty list with an initial capacity of ten.
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
  • 注释写的是创建一个初始容量为10的空数组,但是DEFAULTCAPACITY_EMPTY_ELEMENTDATA是一个长度为0的空数组。
    /**
     * The array buffer into which the elements of the ArrayList are stored.
     * The capacity of the ArrayList is the length of this array buffer. Any
     * empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
     * will be expanded to DEFAULT_CAPACITY when the first element is added.
     */
    transient Object[] elementData; // non-private to simplify nested class access

    /**
     * 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 = {};

   /**
     * Shared empty array instance used for empty instances.
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};
  • 注释解释了这样是为了,与EMPTY_ELEMENTDATA区分,在第一次add的时候才会扩容。后面我们会知道第一次扩容的默认大小为10,所以api中才会写"创建一个初始容量为10的空数组"。

有参构造器

add

/**
 * Appends the specified element to the end of this list.
 *
 * @param e element to be appended to this list
 * @return {@code true} (as specified by {@link Collection#add})
 */
public boolean add(E e) {
    modCount++;
    add(e, elementData, size);
    return true;
}
  • 方法中首先将变量modCount自增1;

  • modCount是用来标记当前arraylist集合操作变化的次数,在fail-fast机制中会有用到这个变量。

protected transient int modCount = 0;
  • 然后调用重载的另一个add方法;
/**
 * This helper method split out from add(E) to keep method
 * bytecode size under 35 (the -XX:MaxInlineSize default value),
 * which helps when add(E) is called in a C1-compiled loop.
 */
private void add(E e, Object[] elementData, int s) {
    if (s == elementData.length)
        elementData = grow();
    elementData[s] = e;
    size = s + 1;
}
  • s即传入的size,如果当前数组的大小已经不够用了,就会调用grow()方法扩容;
  • 添加e至队尾,并将数量加1。

grow

private Object[] grow() {
    return grow(size + 1);
}
  • 调用重载的另一个grow方法,并将当前的数组元素的数量+1传入,即minCapacity;
/**
 * Increases the capacity to ensure that it can hold at least the
 * number of elements specified by the minimum capacity argument.
 *
 * @param minCapacity the desired minimum capacity
 * @throws OutOfMemoryError if minCapacity is less than zero
 */
private Object[] grow(int minCapacity) {
    int oldCapacity = elementData.length;
    if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
        int newCapacity = ArraysSupport.newLength(oldCapacity,
                minCapacity - oldCapacity, /* minimum growth */
                oldCapacity >> 1           /* preferred growth */);
        return elementData = Arrays.copyOf(elementData, newCapacity);
    } else {
        return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
    }
}
  • oldCapacity :数组的长度

  • minCapacity:数组元素数量+1,即最小需求。

    • 如果是add()调用的,根据s == elementData.length可知 minCapacity应该是oldCapacity+1;
    • 如果是其他方法调用,则根据实际情况。
  • 如果数组不是空数组

    扩容的大小为ArraysSupport.newLength()的返回值,实现方法为Arrays.copyOf()。

int newCapacity = ArraysSupport.newLength(oldCapacity,
minCapacity - oldCapacity, /* minimum growth /
oldCapacity >> 1 /
preferred growth */);

​ 注意传入值,oldCapacity是数组的长度,minCapacity - oldCapacity应该是数组元素数量+1-数组长度,oldCapacity >> 1 是数组长度的一半。

  • 否则,数组是空数组,扩容的大小为DEFAULT_CAPACITY和minCapacity中的最大值。

        /**
         * Default initial capacity.
         */
        private static final int DEFAULT_CAPACITY = 10;
    
    • 如果是初始化的空数组,那么第一次扩容后的大小为默认值10

ArraysSupport.newLength

/**
 * Calculates a new array length given an array's current length, a preferred
 * growth value, and a minimum growth value.  If the preferred growth value
 * is less than the minimum growth value, the minimum growth value is used in
 * its place.  If the sum of the current length and the preferred growth
 * value does not exceed {@link #MAX_ARRAY_LENGTH}, that sum is returned.
 * If the sum of the current length and the minimum growth value does not
 * exceed {@code MAX_ARRAY_LENGTH}, then {@code MAX_ARRAY_LENGTH} is returned.
 * If the sum does not overflow an int, then {@code Integer.MAX_VALUE} is
 * returned.  Otherwise, {@code OutOfMemoryError} is thrown.
 *
 * @param oldLength   current length of the array (must be non negative)
 * @param minGrowth   minimum required growth of the array length (must be
 *                    positive)
 * @param prefGrowth  preferred growth of the array length (ignored, if less
 *                    then {@code minGrowth})
 * @return the new length of the array
 * @throws OutOfMemoryError if increasing {@code oldLength} by
 *                    {@code minGrowth} overflows.
 */
public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
    // assert oldLength >= 0
    // assert minGrowth > 0

    int newLength = Math.max(minGrowth, prefGrowth) + oldLength;
    if (newLength - MAX_ARRAY_LENGTH <= 0) {
        return newLength;
    }
    return hugeLength(oldLength, minGrowth);
}
  • oldLength:数组长度

  • minGrowth:数组元素数量+1-数组长度

  • prefGrowth:数组长度/2

  • newLength初始化为Math.max(minGrowth, prefGrowth) + oldLength;

  • 如果newLength小于MAX_ARRAY_LENGTH,那么返回newLenght。

    /**
     * The maximum length of array to allocate (unless necessary).
     * Some VMs reserve some header words in an array.
     * Attempts to allocate larger arrays may result in
     * {@code OutOfMemoryError: Requested array size exceeds VM limit}
     */
    public static final int MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8;
    
    /**
     * A constant holding the maximum value an {@code int} can
     * have, 2<sup>31</sup>-1.
     */
    @Native public static final int   MAX_VALUE = 0x7fffffff;
    
  • 否则,调用hugeLength

hugeLength

private static int hugeLength(int oldLength, int minGrowth) {
    int minLength = oldLength + minGrowth;
    if (minLength < 0) { // overflow
        throw new OutOfMemoryError("Required array length too large");
    }
    if (minLength <= MAX_ARRAY_LENGTH) {
        return MAX_ARRAY_LENGTH;
    }
    return Integer.MAX_VALUE;
}
  • minLength:数组元素数量+1
  • 如果没有溢出,则返回MAX_ARRAY_LENGTH
  • 否则抛出异常OutOfMemoryError(“Required array length too large”);

总结

  • 使用无参构造器创建的ArrayList初始容量为0,第一次调用add()/addAll()方法时才会初始化数组的容量,初始容量为10。
  • 对集合添加若干个元素时,如果当前集合的容量满足需求,不扩容;
  • 如果当前集合容量不满足需求,则扩大为原来的1.5倍;如果扩大1.5倍依然不满足需求,则扩大为满足需求的最小容量。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值