浅谈arraylist的底层扩容的原理(代码片段)

clover-forever clover-forever     2022-11-28     714

关键词:

ArrayList扩容机制的源码详解

一:ArrayList的构造函数:

ArrayList的构造函数源码有三种:

先来看看ArrayList底层定义的一些变量的含义:

/**  Default initial capacity
  *  默认的容量大小
  */
private static final int DEFAULT_CAPACITY = 10;

/**  Shared empty array instance used for empty instances.
  *  定义的空的数组
  */
private static final Object[] EMPTY_ELEMENTDATA = ;

 /**
   * 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


 /**
   * The size of the ArrayList (the number of elements it contains).
   * 这个list集合的长度
   * @serial
   */
    private int size;
 /**
  * Constructs an empty list with an initial capacity of ten
  * 空的构造函数
  */
    public ArrayList() 
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    
    
    
 /**
   * Constructs an empty list with the specified initial capacity.
   *
   * @param  initialCapacity  the initial capacity of the list
   * @throws IllegalArgumentException if the specified initial capacity
   *         is negative
   * 根据用户传入的容量大小构造一个list集合,长度可以大于等于0,但是如果为负数会抛出异常
   */
    public ArrayList(int initialCapacity) 
        // 如果初始容量大于0
        if (initialCapacity > 0) 
            // 创建一个大小为initialCapacity的数组
            this.elementData = new Object[initialCapacity];
         else if (initialCapacity == 0) 
            // 创建一个空数组
            this.elementData = EMPTY_ELEMENTDATA;
         else 
           // 如果为负数,直接抛出异常
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        
    
    
    
 /**
   * Constructs a list containing the elements of the specified
   * collection, in the order they are returned by the collection‘s
   * iterator.
   *
   * @param c the collection whose elements are to be placed into this list
   * @throws NullPointerException if the specified collection is null
   * 构造包含指定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()方法

1:add()方法的实现

 // 将指定的元素加到列表的末尾
 public boolean add(E e) 
        // 添加元素之前,先调用ensureCapacityInternal方法
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        // 这里看到的ArrayList添加元素的实质相当于为数组赋值
        elementData[size++] = e;
        return true;
    

2:ensureCapacityInternal方法的实现

// 得到最小的扩容量
private void ensureCapacityInternal(int minCapacity) 
        // 当一开始是默认空的列表
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) 
            // 获取默认的容量和传入参数的最大值
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        

        ensureExplicitCapacity(minCapacity);
    

例如:当add进第一个元素时候,minCapacity为1,此时去二者的最大值

3:比较ensureExplicitCapacity

// 判断是否需要扩容
private void ensureExplicitCapacity(int minCapacity) 
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            // 调用grow方法进行扩容
            grow(minCapacity);
    

我们来将上面的内容梳理一下,会get到几个点:

*当我们add进第一个元素到ArrayList时, elementData.length 为0 (因为还是一个空的 list) ,但是此时执行了ensureCapacityInternal()方法,通过默认的比较,此时会得到minCapacity为10。此时minCapacity - elementData.length > 0满足,所以会进入grow(minCapacity)方法.

*当add第二个元素时, minCapacity 为2,此时e lementData.length(容量)在添加第一个元素后扩容成 10 了。此时,minCapacity - elementData.length > 0 不成立,所以不会进入 (执行)grow(minCapacity) 方法。

*同理添加第3,4,5,6.....一直到11个元素都不会grow,当到第11个元素的时候,minCapacity(11)比10大,会进行grow操作

4:grow()方法(扩容核心)

// 需要分配的数组大小
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);
        // 如果新容量小于添加的集合的容量,则把该容量替换
        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:
        // 将原数组copy到新的数组中
        elementData = Arrays.copyOf(elementData, newCapacity);
    
    
    /** 如果新容量大于 MAX_ARRAY_SIZE,进入(执行) `hugeCapacity()` 方法来比较 minCapacity 和           * MAX_ARRAY_SIZE,如果minCapacity大于最大容量,则新容量则为`Integer.MAX_VALUE`,否则,           * 新容量大小则为 MAX_ARRAY_SIZE 即为 `Integer.MAX_VALUE - 8`。
      */
    private static int hugeCapacity(int minCapacity) 
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    

补充:System.arrayCopy()和Arrays.copyOf()方法

联系: 看两者源代码可以发现 copyOf() 内部实际调用了 System.arraycopy() 方法

区别: arraycopy() 需要目标数组,将原数组拷贝到你自己定义的数组里或者原数组,而且可以选择拷贝的起点和长度以及放入新数组中的位置 copyOf() 是系统自动在内部新建一个数组,并返回该数组。

参考原文:

https://www.cnblogs.com/baichunyu/p/12965241.html

arraylist的底层数组扩容你会吗?(代码片段)

前言:想必大家都知道ArrayList的底层使用数组来实现的。今天我们就写个简易版的来实现这一功能。一、思考需要哪些步骤实现一个数组的动态扩容第一步需要一个数组进行初始化使用第二步还需要一个数组作为一个中转使用第... 查看详情

arraylist底层原理

ArrayList底层采用数组实现,访问特别快,它可以根据索引下标快速找到元素。但添加插入删除等写操作效率低,因为涉及到内存数据复制转移。ArrayList对象初始化时,无参数构造器默认容量为10,当空间不足时会扩容,扩容后的... 查看详情

arraylist底层扩容机制(代码片段)

...定参数的List集合://使用无参构造创建List集合Listlist=newArrayList();//添加数据for(inti=1;i<=10;i++)list.add(i);//添加数据for(inti=11;i<=15;i++)list.add(i);话不多说直接上源码:1、初始化集合/***初始化一个容量为0的列表*/publicArrayList() 查看详情

arraylist底层源码实现练习(代码片段)

/***Createdbychengbxon2018/5/17.*自己实现一个ArrayList,帮助我们更好的理解ArrayList的底层结构!*一句话概括ArrayList的底层:数组的扩容与数据的拷贝!*/publicclassCbxArrayList//存储集合中的元素privateObject[]elementData;//容器中存储的元素数量... 查看详情

arraylist的add(ee)方法与扩容(代码片段)

ArrayList是Java开发中经常用到的集合类,它是List接口的实现类,具有很高的查询性能,但不是线程安全的。本文主要讲述了ArrayList的add(Ee)方法及该方法中涉及到的容量扩容技术。本文大纲1.ArrayList底层数据结构2.add(Ee)方法流程概... 查看详情

用大白话告诉你arraylist的底层原理(代码片段)

一、ArrayList的数据结构ArrayList的底层数据结构就是一个数组,数组元素的类型为Object类型,对ArrayList的所有操作底层都是基于数组的。二、ArrayList的线程安全性对ArrayList进行添加元素的操作的时候是分两个步骤进行的,即第一步... 查看详情

arraylist原理分析(重点在于扩容)(代码片段)

首先,ArrayList定义只定义类两个私有属性:/***ThearraybufferintowhichtheelementsoftheArrayListarestored.*ThecapacityoftheArrayLististhelengthofthisarraybuffer.*/privatetransientObject[]elementData;/***ThesizeoftheArra 查看详情

arraylist与linkedlist常见的问题(代码片段)

...见的List类有哪些,它们分别有什么区别?答:常见的有ArrayList,Vector,LinkedList等。ArrayList底层是数组组成,Vector是线程安全的ArrayList类。LinkedList底层是由链表组成。ArrayList与Vector的区别,扩容上面ArrayList是扩容1.5倍,而Vector是... 查看详情

浅谈linkedlist(代码片段)

...储,可以无限链接新元素(受限于硬盘存储容量),不存在ArrayList(底层使用数组实现)中的数组扩容问题,具有插入,删除元素快捷、方便的特点,但因为每个节点需要有上一个节点和下一个节点的引用,从而导致了每个结点需要... 查看详情

arraylist扩容机制(代码片段)

ArrayList的底层就是一个数组,依赖其扩容机制(后面会提到)它能够实现容量的动态增长,所以ArrayList就是数据结构中顺序表的一种具体实现。其特点为:查询快,增删慢,线程不安全,效率高。... 查看详情

arraylist扩容机制(基于jdk1.8)(代码片段)

一.ArrayList继承了AbstractList,实现了List接口,底层实现基于数组,因此可以认为是一个可变长度的数组。二.在讲扩容机制之前,我们需要了解一下ArrayList中最主要的几个变量://定义一个空数组以供使用privatestaticfinalObject[]EMPTY_EL... 查看详情

浅谈spring事务底层原理(代码片段)

点击关注公众号,实用技术文章及时了解@EnableTransactionManagement工作原理Spring事务基本执行原理Spring事务详细执行流程Spring事务传播机制Spring事务传播机制分类案例分析情况1情况2情况3情况4Spring事务强制回滚TransactionSynchron... 查看详情

浅谈mqtt底层原理(网络调试助手直连阿里云)(代码片段)

目录第一节本文探讨的内容第二节环境搭建第三节MQTT控制报文格式第四节CONNEC报文第五节订阅和取消订阅第六节接收消息和发布消息第七节网络调试助手直连阿里云极速体验第一节本文探讨的内容        本文讨论MQTT协议底... 查看详情

转arraylist扩容机制(基于jdk1.8)(代码片段)

转载:https://blog.csdn.net/qq_26542493/article/details/88873168一.ArrayList继承了AbstractList,实现了List接口,底层实现基于数组,因此可以认为是一个可变长度的数组。二.在讲扩容机制之前,我们需要了解一下ArrayList中最主要的几个变量://... 查看详情

java常用集合类的底层扩容机制(代码片段)

...于Collection接口(Collection继承于Iterable接口)。ListArrayList先说结论:它的底层使用的是一个Object类型的数组elmentData[];当创建一个ArrayList时,如果没定义初始容量大 查看详情

常见arraylist面试题(代码片段)

文章目录ArrayList数组扩容效率问题底层和源码分析扩容添加流程有参构造如何复制某个ArrayList到另一个ArrayList中去?线程安全问题ArrayList插入或删除元素一定比LinkedList慢么ArrayList适合做队列吗ArrayList的遍历和LinkedList遍历性... 查看详情

常见arraylist面试题(代码片段)

文章目录ArrayList数组扩容效率问题底层和源码分析扩容添加流程有参构造如何复制某个ArrayList到另一个ArrayList中去?线程安全问题ArrayList插入或删除元素一定比LinkedList慢么ArrayList适合做队列吗ArrayList的遍历和LinkedList遍历性... 查看详情

arraylist扩容源码剖析(代码片段)

一、先看ArrayList的简单总结1、ArrayList中维护了一个Object类型的数组elementData.   transientObject[]elementData;(Object类型的数组说明什么类型都可以放)   对象在序列化的时候,transient修饰的属性不会被序列化2、当创建对象时,... 查看详情