add方法理解ArrayList的扩容机制

目录
  • ArrayList的构造方法(前置知识)
  • ArrayList的add方法(理解扩容机制)
    • add 添加元素
    • 得到最小扩容量
    • 判断是否需要扩容
    • 扩容方法

ArrayList的构造方法(前置知识)

可快速过

一些基本成员变量:

// 默认初始大小
private static final int DEFAULT_CAPACITY = 10;
// 空数组 用于空实例
private static final Object[] EMPTY_ELEMENTDATA = new Object[0];
// 用于默认大小空实例的共享空数组实例。
// 我们把它从EMPTY_ELEMENTDATA数组中区分出来,以知道在添加第一个元素时容量需要增加多少。
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = new Object[0];
// 保存ArrayList数据的数组
transient Object[] elementData;
// 大小
private int size;
// 数组的最大长度
private static final int MAX_ARRAY_SIZE = 2147483639;

构造方法:

   /**
     * 默认初始容量大小
     */
    private static final int DEFAULT_CAPACITY = 10;
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
    /**
     *默认构造函数,使用初始容量10构造一个空列表(无参数构造)
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
    /**
     * 带初始容量参数的构造函数。(用户自己指定容量)
     */
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {//初始容量大于0
            //创建initialCapacity大小的数组
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {//初始容量等于0
            //创建空数组
            this.elementData = EMPTY_ELEMENTDATA;
        } else {//初始容量小于0,抛出异常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
   /**
    *构造包含指定collection元素的列表,这些元素利用该集合的迭代器按顺序返回
    *如果指定的集合为null,throws NullPointerException。
    */
     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的add方法(理解扩容机制)

add 添加元素

    /**
     * 将指定的元素追加到此列表的末尾。
     */
    public boolean add(E e) {
        // 添加元素之前,先试探,容量够不够
        // Increments modCount!! 这里有个failfast机制
        // 简而言之就是并发场景下通过modcount的值判断当前的操作是否是最新的modcount的数据
        ensureCapacityInternal(size + 1);
        //这里看到ArrayList添加元素的实质就相当于为数组赋值
        elementData[size++] = e;
        return true;
    }

得到最小扩容量

我们接下来看看这个“试探容量的方法”ensureCapacityInternal()怎么实现的

我们约定一个值,也就是传参进来的size+1,叫minCapacity(最小需要容量)

   //得到最小扩容量    就是你至少要扩容至这么多,才能容纳下我的数据
    private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
              // 获取默认的容量和传入参数的较大值
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        ensureExplicitCapacity(minCapacity);
    }

当要add进第1个元素时,minCapacity为1,在Math.max()方法比较后,minCapacity为10。

判断是否需要扩容

然后是ensureExplicitCapacity():

  //判断是否需要扩容
    private void ensureExplicitCapacity(int minCapacity) {
        modCount++;
        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            //调用grow方法进行扩容,调用此方法代表已经开始扩容了
            grow(minCapacity);
    }

我们来梳理一下:

  • 当我们要 add 进第 1 个元素到 ArrayList 时,elementData.length为0 (因为还是一个空的 list),因为执行了 ensureCapacityInternal() 方法 ,所以minCapacity此时为 10。此时,minCapacity - elementData.length > 0成立,所以会进入 grow(minCapacity) 方法,此时数组容量变为10
  • 当 add 第 2 个元素时,minCapacity 为 2,此时 elementData.length(容量)在添加第一个元素后扩容成 10 了。此时,minCapacity - elementData.length > 0 不成立,所以不会进入 (执行)grow(minCapacity) 方法。
  • 添加第 3、4···到第10个元素时,依然不会执行 grow()方法,数组容量都为10。
  • 直到添加第 11 个元素,minCapacity(为 11)比 elementData.length(为 10)要大。进入 grow 方法进行扩容。

扩容方法

按照elementData.length(newCapacity)扩容1.5倍,然后看看够不够minCapacity

不够的话,newCapacity就=minCapacity

hugeCapacity()比较一下newCapacity是否超过MAX_ARRAY_SIZE,超过就赋为Integer.MAX_VALUE

然后就进行扩容,Arrays.copyof方法

    /**
     * 要分配的最大数组大小
     */
    private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
    /**
     * ArrayList扩容的核心方法。
     */
    private void grow(int minCapacity) {
        // oldCapacity为旧容量,newCapacity为新容量
        int oldCapacity = elementData.length;
        //将oldCapacity 右移一位,其效果相当于oldCapacity /2,
        //我们知道位运算的速度远远快于整除运算,整句运算式的结果就是将新容量更新为旧容量的1.5倍,
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        //然后检查新容量是否大于最小需要容量,若还是小于最小需要容量,那么就把最小需要容量当作数组的新容量,
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
       // 如果新容量大于 MAX_ARRAY_SIZE,进入(执行) `hugeCapacity()` 方法来比较 minCapacity 和 MAX_ARRAY_SIZE,
       //如果minCapacity大于最大容量
       //则新容量则为`Integer.MAX_VALUE`,否则,新容量大小则为 MAX_ARRAY_SIZE 即为 `Integer.MAX_VALUE - 8`。
        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);
    }

int newCapacity = oldCapacity + (oldCapacity >> 1),所以 ArrayList 每次扩容之后容量都会变为原来的 1.5 倍左右(oldCapacity 为偶数就是 1.5 倍,否则是 1.5 倍左右)! 奇偶不同,比如 :10+10/2 = 15, 33+33/2=49。如果是奇数的话会丢掉小数

tips:

  • java 中的 length属性是针对数组说的,比如说你声明了一个数组,想知道这个数组的长度则用到了 length 这个属性.
  • java 中的 length() 方法是针对字符串说的,如果想看这个字符串的长度则用到 length() 这个方法.
  • java 中的 size() 方法是针对泛型集合说的,如果想看这个泛型有多少个元素,就调用此方法来查看!

资料:javaguide

以上就是add方法理解ArrayList的扩容机制的详细内容,更多关于add方法ArrayList扩容的资料请关注我们其它相关文章!

(0)

相关推荐

  • Java ArrayList.add 的实现方法

    ArrayList是平时相当常用的List实现, 其中boolean add(E e) 的实现比较直接: /** * Appends the specified element to the end of this list. * * @param e element to be appended to this list * @return <tt>true</tt> (as specified by {@link Collection#add}) */ public boole

  • Java中如何将 int[] 数组转换为 ArrayList(list)

    目录 Java中数组转List的四种方式 第一种方式(未必最佳):使用ArrayList.asList(strArray) 第二种方法(支持增删查改): 第三种方式(通过集合工具类Collections.addAll()方法(最高效)) 第四种方式通过JDK8的Stream流将3总基本类型数组转为List java数组转list误区 一.不能把基本数据类型转化为列表 二.asList方法返回的是数组的一个视图 下面介绍下Java中如何将 int[] 数组转换为 List(ArrayList) 前

  • Java ArrayList add(int index, E element)和set(int index, E element)两个方法的说明

    一般使用List集合,估计都是使用这个ArrayList,一般呢也就是简单遍历数据和存储数据. 很少使用到add(int index, E element)和set(int index, E element)两个方法. 这两个方法,乍一看,就是在指定的位置插入一条数据. 区别: set()是更新,更新指定下标位置的值. add()是添加,区别于一般的add(E e),这个就是有个位置的概念,特殊位置之后的数据,依次往后移动就是了. 然后,看下面代码.来看看陷阱. 就算是,你知道了上面的内容,也不

  • Java实现ArrayList排序的方法详解

    目录 简介 法1:JDK8的stream 法2:Comparator#compare() 法3:Comparable#compareTo() 简介 说明 本文用示例介绍Java的ArrayList排序的方法. List排序方法 主要有三种方法(按推荐度排序): JDK8的stream Comparator#compare() Comparable#compareTo() 法1:JDK8的stream 见:一文详解Java中Stream流的使用 法2:Comparator#compare() 需求

  • C#中ArrayList 类的使用详解

    目录 一:ArrayList 类简单说明 二:ArrayList 类的构造函数 三:ArrayList 类的属性 1:使用举例说明 四:ArrayList 类的常用方法 1: AddRange(ICollection)方法使用举例 2: ArrayList.Clone() 方法使用举例 3:ArrayList.Remove(Object) 使用举例 4:ArrayList.RemoveAt(Int32) 使用举例 5:ArrayList.RemoveAt(Int32) 使用举例 6:ArrayL

  • Java中ArrayList的使用详细介绍

    目录 1.ArrayList类 1.1ArrayList类概述 1.2ArrayList类常用方法 1.2.1构造方法 1.2.2成员方法 1.2.3示例代码 1.3ArrayList存储字符串并遍历 1.3.1案例需求 1.3.2代码实现 1.4ArrayList存储学生对象并遍历 1.4.1案例需求 1.4.2代码实现 1.5ArrayList存储学生对象并遍历升级版 1.5.1案例需求 1.5.2代码实现 总结 1.ArrayList类 1.1ArrayList类概述 在java中,我们会

  • 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的扩容机制

    我们知道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

  • Java 数据结构深入理解ArrayList与顺序表

    目录 一.ArrayList简介 二.ArrayList的使用 1.ArrayList的构造 2.ArrayList的遍历 3.ArrayList的常见操作(方法) 4.ArrayList的扩容机制 三.模拟实现一个顺序表(Object[]) 一.ArrayList简介 在集合框架中,ArrayList是一个普通的类,实现了List接口,具体框架图如下: ArrayList底层是一段连续的空间,并且可以动态扩容,是一个动态类型的顺序表. 二.ArrayList的使用 1.ArrayList的构造

  • 关于ArrayList的动态扩容机制解读

    目录 1. 前言 2. ArrayList 的动态扩容机制 2.1. ArrayList 的主要属性 2.2. ArrayList 的构造器 2.3. ArrayList 的动态扩容 3. 小结 3.1. 初始容量 3.2. 动态扩容大小 3.3. 动态扩容大小测试 1. 前言 对于 ArrayList 的动态扩容机制想必大家都听说过,之前的文章中也谈到过,不过由于时间久远,早已忘却. 所以利用这篇文章做做笔记,加深理解记忆 2. ArrayList 的动态扩容机制 要了解其动态扩容机制就必须先

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

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

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

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

  • 新手入门了解ArrayList扩容机制

    我们下面用最简单的代码创建ArrayList并添加11个元素,并 一 一 讲解底层源码:在说之前,给大家先普及一些小知识: >ArrayList底层是用数组来实现的 >数组一旦创建后,大小就是固定的,如果超出了数组大小后,就会创建一个新的数组 >接下来所谓数组的扩容实质上是重新创建一个大小更大的新数组 @Test public void testArrayList() { //创建一个泛型为String的ArrayList(这里泛型是什么不重要) ArrayList<String&

  • 解析从源码分析常见的基于Array的数据结构动态扩容机制的详解

    本文的写作冲动来源于今晚看到的老赵的一则微博"大家知道System.Collections.Generic.List<T>是一种什么样的数据结构?内部的元素是怎么存放的?还有Dictionary<TKey,TValue>呢?-". 查了一下书,如果参考数据结构和算法里介绍的线性表合哈希表的特点,非常官方的答案就类似:List<T>是一种线性的内存连续分配的存储结构,元素是顺序存放的:它的优点是内存连续分配,相对节省空间,在设定长度范围内增加元素开销很

  • c# HashSet的扩容机制需要注意的

    一:背景 1. 讲故事 自从这个纯内存项目进了大客户之后,搞得我现在对内存和CPU特别敏感,跑一点数据内存几个G的上下,特别没有安全感,总想用windbg抓几个dump看看到底是哪一块导致的,是我的代码还是同事的代码? 很多看过我博客的老朋友总是留言让我出一套windbg的系列或者视频,我也不会呀,没办法,人在江湖飘,迟早得挨上几刀,逼着也得会几个花架子

随机推荐