Java基础之ArrayList的扩容机制

我们知道Java中的ArrayList对象底层是基于数组实现的,而数组是有长度限制的,那基于数组实现的ArrayList是否有长度限制呢?我们通过ArrayList的构造方法来剖析

ArrayList提供了3种构造方法以便我们来获取:

  • ArrayList(int initialCapacity) 第一种需要赋值长度进行new
  • ArrayList() 第二种无参构造,不需要赋值数组初始长度
  • ArrayList(Collection<? extends E> c) 第三种入参一个继承了Collection对象转化为ArrayList
//需要赋值ArrayList一个int类型长度值
    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);
        }
    }
    //无参构造
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
	//入参继承了Collection对象转化为ArrayList
    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;
        }
    }

接下来我们从ArrayList的赋值初始长度的构造方法和无参构造方法进行跟踪,来得到我们的答案;

1.ArrayList(int initialCapacity)

/**
* Shared empty array instance used for empty instances.
*/
private static final Object[] EMPTY_ELEMENTDATA = {};

public ArrayList(int initialCapacity) {
		//initialCapacity大于0的情况下,就去new一个Object类型、长度为initialCapacity的数组
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
        //initialCapacity等于0的情况下,则生成已经创造好的Object[];
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
        //initialCapacity小于0的情况下,直接抛异常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

通过上述源码分析,在赋值初始长度的构造方法中,如果initialCapacity符合大于0的情况下的话,在new ArrayList的时候是会创造一个长度为initialCapacity的对象数组,而小于0的情况下则返回已经创造好的Object[] EMPTY_ELEMENTDATA;

2.ArrayList()

 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    public ArrayList() {
    	//当你没有赋值初始长度时,同样是去赋值已经创造好的空数组,没有长度值
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

当ArrayList生成后,我们接着来看他的add()添加方法,找到ArrayList实现的add方法:

 private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

    public boolean add(E e) {
    	//1、add的第一步先去调用ensureCapacityInternal方法传入size+1
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    }

    //3、传入一开始我们new的数组和add后的数组长度
    private static int calculateCapacity(Object[] elementData, int minCapacity) {
    	//如果elementData等于空数组,则对比DEFAULT_CAPACITY和minCapacity大小,返回大的值,DEFAULT_CAPACITY默认为10
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        //否则返回minCapacity
        return minCapacity;
    }

	//2、在ensureCapacityInternal方法内部又去调用calculateCapacity(elementData, minCapacity)方法,传入add数据后的list的size长度
    private void ensureCapacityInternal(int minCapacity) {
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
	//4、拿到minCapacity的值后,对modCount++,比对添加后的list的长度和现在的list的长度来判断是否需要进行扩容;
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // 如果添加list的长度大于list现在的长度,则说明list的长度已经不够了,需要走grow(minCapacity)方法进行扩容
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

经过上面的分析,我们发现在add时,ArrayList会先去判断你是不是一个空数组,如果是的话则会给你赋值数组的,长度值,默认为10,到这一步我们就知道了,不管是哪种构造方法都会在add时,都会进行判断给空数组进行赋值长度,也就是说对于ArrayList来讲,他是需要数组的长度的,同时他还会对比数组原来长度和add后的长度来判断是否需要进行扩容。

接下来我们看grow(minCapacity)扩容方法:

private void grow(int minCapacity) {
        // 拿到数组原来的长度
        int oldCapacity = elementData.length;
        //进行1.5倍计算
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //如果1.5倍的值小于add后的长度值,则把add后的长度值设置成新数组的长度
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        //如果1.5倍的值大于array的最大长度则调用hugeCapacity(minCapacity)方法
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // 调用Arrays.copyOf方法传入旧数组对象和新数组的长度,进行扩容
        elementData = Arrays.copyOf(elementData, newCapacity);
    }

    private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
            //如果add后的长度大于array的最大长度那就返回Integer的最大长度,否则返回array的最大长度,丢弃1.5倍的值,选择array的最大长度来进行扩容
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }

到此,我们对于源码的分析就结束了。

结论:

1.new的时候可以给ArrayList设置数组的长度值,也可以不设置,不设置的情况下,在第一次add时会默认赋值长度为10

2.每一次add时都会去对比add后的长度值和数组原有长度值,判断是否需要走grow(int minCapacity)扩容方法

3.默认ArrayList是以1.5的长度进行扩容,会先去创造一个新的长度的数组,再将原来数组赋值过去,完成扩容操作

4.还会去对比1.5的长度和array的最大长度进行选择用array的最大长度还是选择Integer的最大长度

5.工作时,如果我们能知道ArrayList将要存储多少数据时,最好是new的时候赋值一个初始值,因为去数组扩容肯定是要浪费时间和cpu资源的

到此这篇关于Java基础之ArrayList的扩容机制的文章就介绍到这了,更多相关Java ArrayList的扩容机制内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java使用数组实现ArrayList的动态扩容的方法

    提到数组大家肯定不会陌生,但我们也知道数组有个缺点就是在创建时就确定了长度,之后就不能更改长度.所以Java官方向我们提供了ArrayList这个可变长的容器.其实ArrayList底层也是用数组进行实现的,今天我们就自己使用数组实现ArrayList的功能. 一.整体框架 废话不多说,我们以存放int类型元素为例,看一下ArrayList需要的成员变量和需要实现的方法. public class ArrayList private int size;//用来记录实际存储元素个数 private

  • 区分Java中的ArrayList和LinkedList

    一:ArrayList和LinkedList的大致区别如下: 1.ArrayList是实现了基于动态数组的数据结构,ArrayList实现了长度可变的数组,在内存中分配连续的空间.遍历元素和随机访问元素的效率比较高 2.LinkedList基于链表的数据结构, 插入.删除元素时效率比较高  故:[插入.删除操作频繁时,可使用LinkedList来提高效率]LinkedList提供对头部和尾部元素进行添加和删除操作的方法,插入/删除第一个和最后一个效率比较高: 3:ArrayList和Linked

  • Java中Arraylist动态扩容方法详解

    前言 本文主要给大家介绍了关于Java中Arraylist动态扩容的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. ArrayList 概述 ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长.ArrayList不是线程安全的,只能用在单线程环境下.实现了Serializable接口,因此它支持序列化,能够通过序列化传输:实现了RandomAccess接口,支持快速随机访问,实际上就是通过下标序号进行快速访问:实现了Cloneable接口,能被克隆.

  • Java二维数组与动态数组ArrayList类详解

    Java二维数组 Java 语言中提供的数组是用来存储固定大小的同类型元素. 1.二维数组初始化和声明 数组变量的声明,和创建数组可以用一条语句完成,如下所示: int a[][] = new int[2][3]; int[][] arr = {{1,2,3},{4,5,6},{7,8,9}}; 2.二维数组遍历 //遍历二维数组 public class Traverse_a_two_dimensional_array { public static void main(String[] ar

  • 在java中ArrayList集合底层的扩容原理

    第一章 前言概述 第01节 概述 底层说明 ArrayList是List的实现类,它的底层是用Object数组存储,线程不安全 后期应用 适合用于频繁的查询工作,因为底层是数组,可以快速通过数组下标进行查找 第02节 区别 区别方向 ArrayList集合 LinkedList集合 线程安全 不安全 不安全 底层原理 Object类型数组 双向链表 随机访问 支持(实现 RandomAccess接口) 不支持 内存占用 ArrayList 浪费空间, 底层是数组,末尾预留一部分容量空间 Link

  • 详解Java ArrayList类

    ArrayList 类是一个可以动态修改的数组,与普通数组的区别就是它是没有固定大小的限制,我们可以添加或删除元素. ArrayList 继承了 AbstractList ,并实现了 List 接口. ArrayList 类位于 java.util 包中,使用前需要引入它,语法格式如下: import java.util.ArrayList; // 引入 ArrayList 类 ArrayList<E> objectName =new ArrayList<>(); // 初始化 E

  • Java ArrayList扩容问题实例详解

    本文研究的主要是Java ArrayList扩容问题实例详解的相关内容,具体介绍如下. 首先我们需要知道ArrayList里面的实质的其实是一个Object类型的数组,ArrayList的扩容问题其实就是这个Object类型的数组的扩容问题. transient Object[] elementData; 一.创建时,ArrayList的容量分配 创建一个ArrayList有三种情况 1.默认大小创建(默认为0) ArrayList al = new ArrayList(); 创建完成之后,al

  • java中ArrayList和LinkedList的区别详解

    ArrayList和LinkedList都实现了List接口,有以下的不同点: 1.ArrayList是基于索引的数据接口,它的底层是数组.它可以以O(1)时间复杂度对元素进行随机访问.与此对应,LinkedList是以元素列表的形式存储它的数据,每一个元素都和它的前一个和后一个元素链接在一起,在这种情况下,查找某个元素的时间复杂度是O(n). 2.相对于ArrayList,LinkedList的插入,添加,删除操作速度更快,因为当元素被添加到集合任意位置的时候,不需要像数组那样重新计算大小或者

  • 对Java ArrayList的自动扩容机制示例讲解

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

  • Java基础之ArrayList的扩容机制

    我们知道Java中的ArrayList对象底层是基于数组实现的,而数组是有长度限制的,那基于数组实现的ArrayList是否有长度限制呢?我们通过ArrayList的构造方法来剖析 ArrayList提供了3种构造方法以便我们来获取: ArrayList(int initialCapacity) 第一种需要赋值长度进行new ArrayList() 第二种无参构造,不需要赋值数组初始长度 ArrayList(Collection<? extends E> c) 第三种入参一个继承了Collec

  • 详解ArrayList的扩容机制

    目录 一.ArrayList 了解过吗?它是啥?有啥用? 二.ArrayList 如何指定底层数组大小的 三.数组的大小一旦被规定就无法改变 四.ArrayList 具体是怎么添加数据的 五.ArrayList 又是如何删除数据的呢 六.ArrayList 是线程安全的吗?不安全的表现 七.为什么线程不安全还要用它呢 一.ArrayList 了解过吗?它是啥?有啥用? 众所周知,Java 集合框架拥有两大接口 Collection 和 Map,其中,Collection 麾下三生子 List.S

  • add方法理解ArrayList的扩容机制

    目录 ArrayList的构造方法(前置知识) ArrayList的add方法(理解扩容机制) add 添加元素 得到最小扩容量 判断是否需要扩容 扩容方法 ArrayList的构造方法(前置知识) 可快速过 一些基本成员变量: // 默认初始大小 private static final int DEFAULT_CAPACITY = 10; // 空数组 用于空实例 private static final Object[] EMPTY_ELEMENTDATA = new Object[0];

  • java基础-数组扩容详解

    目录 数组与链表的比较: ArrayList: LinkedList: 总结 数组与链表的比较: 数组通过下标访问的话是O(1) 数组一旦声明 长度就是固定的 数组的数据是物理逻辑均连续的 链表增删要快一些, 数组遍历快一些 长度一定的话, 数组的存储空间比链表要小 ArrayList: ArrayList是List接口的实现类,它是支持根据需要而动态增长的数组:java中标准数组是定长的,在数组被创建之后,它们不能被加长或缩短.这就意味着在创建数组时需要知道数组的所需长度,但有时我们需要动态程

  • Java基础篇之反射机制示例详解

    目录 一.什么是反射: 二.反射的原理: 三.反射的优缺点: 四.反射的用途: 五.反射机制常用的类: 六.反射的基本使用: 1.获得Class:主要有三种方法: 2.判断是否为某个类的示例: 3.创建实例:通过反射来生成对象主要有两种方法: 4.通过反射获取构造方法并使用: 5.获取成员变量并调用: 6.获取成员方法并调用: 7.反射main方法: 8.利用反射创建数值: 9.反射方法的其他使用--通过反射运行配置文件内容: 10.反射方法的其他使用--通过反射越过泛型检查: 总结 一.什么是

  • Java基础之垃圾回收机制详解

    一.GC的作用 进行内存管理 C语言中的内存,申请内存之后需要手动释放:一旦忘记释放,就会发生内存泄漏! 而Java语言中,申请内存后会由GC来释放内存空间,无需手动释放 GC虽然代替了手动释放的操作,但是它也有局限性: 需要消耗更多的资源: 没有手动释放那么及时: STW(Stop The World)会影响程序的执行效率 二.GC主要回收哪些内存 (1)堆:主要回收堆中的内存 (2)方法区:需要回收 (3)栈(包括本地方法栈和JVM虚拟机栈):不需要回收,栈上的内存什么时候释放是明确的(线程

  • Java基础之面向对象机制(多态、继承)底层实现

    一.Java的前世 为什么会产生Java?Java的特点是什么? 从C语言开始讲,C语言是一种结构化语言,模块化编程,便于程序的调试,依靠非常全面的运算符和多样的数据类型,可以轻易完成各种数据结构的构建,通过指针类型更可对内存直接寻址以及对硬件进行直接操作,因此既能够用于开发系统程序,也可用于开发应用软件.其缺点就是封装性弱,程序的安全性上不是很好.C语言的异常处理一般使用setjmp()与longjmp(),在捕获到异常时进行跳转:或者使用abort()和exit()两个函数,强行终止程序的运

  • Java中的ArrayList容量及扩容方式

    目录 查看JDK1.8 ArrayList的源代码 1.默认初始容量为10 2.最大容量为 Integer.MAX_VALUE - 8 3.扩容方式: Java ArrayList() 扩容原理 先看下 ArrayList 的属性以及构造方法,这个比较重要 上看说的是初始化场景,下面看一下其他场景,也是相当简单 结论 查看JDK1.8 ArrayList的源代码 1.默认初始容量为10 /** * Default initial capacity. */ private static final

随机推荐