ArrayList的扩容机制

ArrayList的扩容机制:

当向ArrayList中添加元素的时候,ArrayList的存储容量如果满足新元素的容量要求,则直接存储;ArrayList的存储容量如果不满足新元素的容量要求,ArrayList会增强自身的存储能力,以达到存储新元素的要求。

因为不同的JDK版本的扩容机制可能有差异,下面以JDK1.8为例说明
一、构造方法

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

    /**
     * Constructs an empty list with an initial capacity of ten.
     * ArrayList():并不是在初始化ArrayList的时候就设置初始容量为空,而是在添加第一个元素时,将初始容量设置为10(DEFAULT_CAPACITY)。
     * 在jdk1.6中,无参构造方法的初始容量为10
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

二、主要方法
1. 参数说明

    //默认的初始容量
    private static final int DEFAULT_CAPACITY = 10;

    /**
     * 当使用构造方法ArrayList(int initialCapacity) 时,若initialCapacity=0,则将ArrayList初始化为EMPTY_ELEMENTDATA
     * 或使用ArrayList(Collection<? extends E> c) ,collection为空时
     */
    private static final Object[] EMPTY_ELEMENTDATA = {};

    /**
     * 当使用构造方法ArrayList() 时,将ArrayList初始化为空
     */
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    /**
     * elementData是存储ArrayList元素的数组。
     * ArrayList的容量也就是数组elementData的长度。
     * 添加第一个元素时,任何elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA(即数组为空,elementData = {})的情况,ArrayList的容量都将扩展为DEFAULT_CAPACITY。
     */
    transient Object[] elementData; // non-private to simplify nested class access

    //The size of the ArrayList (the number of elements it contains).
    private int size;

2. add(E e)方法

    public boolean add(E e) {
        //判断是否可以容纳e,若能,则直接添加在末尾;若不能,则进行扩容,然后再把e添加在末尾
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        //将e添加到数组末尾
        elementData[size++] = e;
        return true;
    }

每次在add()一个元素时,arraylist都需要对这个list的容量进行一个判断。通过ensureCapacityInternal()方法确保当前ArrayList维护的数组具有存储新元素的能力,经过处理之后将元素存储在数组elementData的尾部
3. ensureCapacityInternal方法

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

        ensureExplicitCapacity(minCapacity);
    }

ensureCapacityInternal判断ArrayList默认的元素存储数据是否为空,为空则设置最小要求的存储能力为必要存储的元素和默认存储元素个数的两个数据之间的最大值,然后调用ensureExplicitCapacity方法实现这种最低要求的存储能力
4. ensureExplicitCapacity方法

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

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

若ArrayList已有的存储能力满足最低存储要求,则返回add直接添加元素;如果最低要求的存储能力>ArrayList已有的存储能力,这就表示ArrayList的存储能力不足,因此需要调用 grow();方法进行扩容
5. grow()方法

    private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        int newCapacity = oldCapacity + (oldCapacity >> 1); //设置新的存储能力为原来的1.5倍
        if (newCapacity - minCapacity < 0) //扩容之后仍小于最低存储要求minCapacity
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0) //private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

当ArrayList扩容的时候,首先会设置新的存储能力为原来的1.5倍,如果扩容之后仍小于必要存储要求minCapacity,则取值为minCapacity。
若新的存储能力大于MAX_ARRAY_SIZE,则取值为Integer.MAX_VALUE
在grow方法中,确定ArrayList扩容后的新存储能力后,调用的Arrays.copyof()方法进行对原数组的复制,再通过调用System.arraycopy()方法(native修饰)进行复制,达到扩容的目的
6. hugeCapacity方法

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

从hugeCapacity方法看出,ArrayList最大的存储能力:存储元素的个数为整型的范围。
例:当ArrayList中的当前容量已经为Integer.MAX_VALUE,仍向ArrayList中添加元素时,抛出异常

public class ArrayOrLinked2{

    public static void main(String[] args) {
        // TODO Auto-generated method stub
        ArrayList<Integer> arrayList = new ArrayList<Integer>();

        arrayList.add(1);
        for(int i = 0; i < Integer.MAX_VALUE; i++) //超过ArrayList的最大容量
            arrayList.add(i);
    }
}

ArrayList扩容的例子:
ArrayList相当于在没指定initialCapacity时就是会使用延迟分配对象数组空间,当第一次插入元素时才分配10(默认)个对象空间。

public class ArrayListTest2 {

    public static void main(String[] args) {

        ArrayList<Integer> arrayList = new ArrayList<Integer>();

        arrayList.add(1);
        for(int i = 0; i < 19; i++)
            arrayList.add(i);
    }
}

假如有20个数据需要添加,那么会分别在第一次的时候,将ArrayList的容量变为10 (如下图一);之后扩容会按照1.5倍增长。也就是当添加第11个数据的时候,Arraylist继续扩容变为10*1.5=15(如下图二);当添加第16个数据时,继续扩容变为15 * 1.5 =22个。

总结
在JDK1.8中,如果通过无参构造的话,初始数组容量为0,当真正对数组进行添加时(即添加第一个元素时),才真正分配容量,默认分配容量为10;当容量不足时(容量为size,添加第size+1个元素时),先判断按照1.5倍(位运算)的比例扩容能否满足最低容量要求,若能,则以1.5倍扩容,否则以最低容量要求进行扩容。

执行add(E e)方法时,先判断ArrayList当前容量是否满足size+1的容量;
在判断是否满足size+1的容量时,先判断ArrayList是否为空,若为空,则先初始化ArrayList初始容量为10,再判断初始容量是否满足最低容量要求;若不为空,则直接判断当前容量是否满足最低容量要求;
若满足最低容量要求,则直接添加;若不满足,则先扩容,再添加。

ArrayList的最大容量为Integer.MAX_VALUE
参考:
https://blog.csdn.net/yang1464657625/article/details/59109133
https://blog.csdn.net/u010176014/article/details/52073339

原文地址:https://www.cnblogs.com/zeroingToOne/p/9522814.html

时间: 2024-10-23 18:58:39

ArrayList的扩容机制的相关文章

【数组】- ArrayList自动扩容机制

不同的JDK版本的扩容机制可能有差异 实验环境:JDK1.8 扩容机制: 当向ArrayList中添加元素的时候,ArrayList如果要满足新元素的存储超过ArrayList存储新元素前的存储能力,ArrayList会增强自身的存储能力,已达到存储新元素的要求 ArrayList:本质通过内部维护的数组对象进行数据存储 ①:分析ArrayList的add(E)方法 public boolean add(E e) { ensureCapacityInternal(size + 1); // In

ArrayList源码解析(二)自动扩容机制与add操作

目录 1.ArrayList的自动扩容机制 2.add操作 正文 本篇主要分析ArrayList的自动扩容机制,add和remove的相关方法. 作为一个list,add和remove操作自然是必须的. 前面说过,ArrayList底层是使用Object数组实现的.数组的特性是大小固定,这个特性导致的后果之一就是,当ArrayList中成员个数超过capacity后,就需要重新分配一个大的数组,并将原来的成员拷贝到新的数组之中. add操作前都需要保证capacity足够,因此扩容机制和add放

【面试必备】透过源码角度一步一步带你分析 ArrayList 扩容机制

一 先从 ArrayList 的构造函数说起ArrayList有三种方式来初始化,构造方法源码如下:/** 默认初始容量大小*/private static final int DEFAULT_CAPACITY = 10; private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; /*默认构造函数,使用初始容量10构造一个空列表(无参数构造)*/public ArrayList() {this.elementDat

ArrayList和Vector的扩容机制

转自:http://man-yutao.iteye.com/blog/1317446 ArrayList和Vector都是继承了相同的父类和实现了相同的接口.如下 Java代码   public class Vector<E> extends AbstractList<E> implements List<E>, RandomAccess, Cloneable, java.io.Serializable {} public class ArrayList<E>

Java常见集合的默认大小及扩容机制

在面试后台开发的过程中,集合是面试的热话题,不仅要知道各集合的区别用法,还要知道集合的扩容机制,今天我们就来谈下ArrayList 和 HashMap的默认大小以及扩容机制. 在 Java 7 中,查看源码可以知道:ArrayList 的默认大小是 10 个元素,HashMap 的默认大小是16个元素(必须是2的幂,为什么呢???下文有解释).这就是 Java 7 中 ArrayList 和 HashMap  类 的代码片段: // from ArrayList.java JDK 1.7 pri

重磅消息!AppCan扩容机制上线,扩大空间随心所欲!

亲爱的AppCan开发者: AppCan在线打包空间扩容机制已经正式上线啦! 开发的项目越来越多,可用的空间却越来越小,怎么办?[扩容空间]让您不再为空间"斤斤计较"! VS 扩容规则: 1.请关注AppCan官方微信号,并在申请原因中注明您的微信号. AppCan微信二维码 2.有至少一个上线的AppCan应用,可申请50M空间,经官方人员审核后即可扩容. 3.任何一个应用安装量超过1000次可申请一次200M空间. 4.3个月内不可重复申请,且3个月后需凭借其他符合上述要求的应用才

Arraylist动态扩容详解

ArrayList 概述 ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长. ArrayList不是线程安全的,只能用在单线程环境下. 实现了Serializable接口,因此它支持序列化,能够通过序列化传输: 实现了RandomAccess接口,支持快速随机访问,实际上就是通过下标序号进行快速访问: 实现了Cloneable接口,能被克隆. 动态扩容 一 初始化 首先有三种方式来初始化: public ArrayList(); 默认的构造器,将会以默认的大小来初始化内部的数

HashMap原理(二) 扩容机制及存取原理

我们在上一个章节<HashMap原理(一) 概念和底层架构>中讲解了HashMap的存储数据结构以及常用的概念及变量,包括capacity容量,threshold变量和loadFactor变量等.本章主要讲解HashMap的扩容机制及存取原理. 先回顾一下基本概念: table变量:HashMap的底层数据结构,是Node类的实体数组,用于保存key-value对: capacity:并不是一个成员变量,但却是一个必须要知道的概念,表示容量: size变量:表示已存储的HashMap的key-

HashMap的扩容机制

复习散列数据结构之余重新研究了一下Java中的HashMap:本文主要针对:1.HashMap的初始化:2.HashMap的插入:3:HashMap的扩容这三个方面进行总结 1.HashMap的初始化 首先我们来看看代码: 1 public HashMap(int initialCapacity, float loadFactor) { 2 if (initialCapacity < 0) 3 throw new IllegalArgumentException("Illegal init