首页 > 其他分享 >ArrayList动态扩容机制(长度可变原理)

ArrayList动态扩容机制(长度可变原理)

时间:2024-08-23 20:23:27浏览次数:6  
标签:容量 int ArrayList elementData minCapacity 数组 可变 长度

ArrayList底层是数组结构的,数组的默认长度为10。当数组添加满了后,会自动扩容为1.5倍。

原理讲解:

1.用空参构造函数创建ArrayList集合容器。

  • 测试代码:
    • public class ArrayListDemo {
          public static void main(String[] args) {
              //创建ArrayList集合容器
              ArrayList<String> list = new ArrayList<>();
         
          }
      }
      
  • ArrayList部分源码:
    • //成员变量
      //ArrayList底层的数组,如果使用 ArrayList 的默认构造器时(默认构造器就是创建集合容器时不带参数),它的初始容量就是 0
      transient Object[] elementData;  
      //定义一个空数组,空参构造器返回该数组,以及扩容的时候用到
      private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
      
      //空参构造函数
      public ArrayList() {
              this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
      }
      
    • 空参构造函数里把一个空数组赋值给集合底层的数组。

结论:如果只是创建了集合容器,没有进行过添加操作,底层数组的默认长度为0。

2.往集合容器里面添加一个元素

  • 测试代码:
    • public class ArrayListDemo {
          public static void main(String[] args) {
              ArrayList<String> list = new ArrayList<>();
      		
              //添加一个元素“张三”
              list.add("张三");
          }
      }
      
  • ArrayList部分源码:
    • //成员变量
      
      //ArrayList 的默认初始容量,如果没有显式指定容量,则使用这个值
      private static final int DEFAULT_CAPACITY = 10;
      //定义一个空数组,空参构造器返回该数组,以及扩容的时候用到
      private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
      //size 是当前 ArrayList 中的元素数量,初始化为 0
      private int size; 
      //ArrayList底层的数组,如果使用 ArrayList 的默认构造器时(默认构造器就是创建集合容器时不带参数),它的初始容量就是 0
      transient Object[] elementData;  
      // 是一个常量,表示可以创建的最大数组大小
      //Integer.MAX_VALUE 是 2^31 - 1,即最大的正整数。减去 8 是为了留出一些空间,避免在某些情况下(如内存分配)出现溢出或其他问题
      private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
      
      ---------------------------------------------------------------------
      //add方法
          
      public boolean add(E e) {    //将元素 e 添加到 ArrayList 中
          	//这个方法用于确保 ArrayList 有足够的容量来存储新添加的元素
              ensureCapacityInternal(size + 1);  
          	// 会将元素 e 放到 elementData 数组的索引size位置(例如:添加第一个元素时,就是把元素放到0号索引位置),然后再把size的长度加1
              elementData[size++] = e;
              return true; //该方法总是返回true(异常除外),表示元素成功添加到ArrayList中
      }
      
      ---------------------------------------------------------------------
      //ensureCapacityInternal方法,add方法里面调用了该方法
      
      //minCapacity表示所需的最小容量
      private void ensureCapacityInternal(int minCapacity) {  
          	//ensureExplicitCapacity方法负责调整内部数组的实际容量
          
          	//calculateCapacity方法负责计算所需新容量
          	//elementData:当前数组
              ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
      }
      
      ----------------------------------------------------------------------
      //calculateCapacity方法,ensureCapacityInternal方法里调用了该方法
          
      //计算所需新容量
      private static int calculateCapacity(Object[] elementData,int minCapacity) {
          	//检查 elementData 是否为空的默认数组
              if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
                  //如果是,返回默认容量和所需最小容量中的较大值
                  return Math.max(DEFAULT_CAPACITY, minCapacity);
              }
          	//否则,返回所需的最小容量
              return minCapacity;
      }
      
      -----------------------------------------------------------------------
      //ensureExplicitCapacity方法,ensureCapacityInternal方法里调用了该方法
          
      //调整内部数组的实际容量
      private void ensureExplicitCapacity(int minCapacity) {
          	//增加修改计数	
              modCount++;  //modCount是为了确保集合在被迭代时的安全性和一致性
      
              //如果所需容量大于当前数组长度,则调用 grow 方法
          	//即minCapacity如果大于当前数组的长度(elementData.length),则说明当前数组容量不足以容纳所需的最小容量,需要扩容
              if (minCapacity - elementData.length > 0)
                  grow(minCapacity); //调用 grow 方法来扩展数组的容量
      }
      
      -----------------------------------------------------------------------
       //grow方法,ensureExplicitCapacity方法里调用了该方法
          
       //扩展内部数组 elementData 的容量,以确保它能够容纳至少 minCapacity 个元素
      private void grow(int minCapacity) {
              int oldCapacity = elementData.length; // 获取旧容量(即元素的数量)
          
          	//利用位移运算符'>>'计算旧容量的 1.5 倍
          	//oldCapacity >> 1 : oldCapacity 的二进制向右移动一位,从而实现除以2的效果, 相当于oldCapacity/2,
              int newCapacity = oldCapacity + (oldCapacity >> 1); //计算新容量
          
          	//如果计算出的新容量小于所需的最小容量 minCapacity,则将新容量设置为 minCapacity
              if (newCapacity - minCapacity < 0)  //确保新容量满足最小需求:
                  newCapacity = minCapacity;
          
          	//检查新容量(newCapacity)是否超过了最大数组大小 MAX_ARRAY_SIZE
          	//如果超过,则调用 hugeCapacity 方法来处理这种情况,通常是为了避免内存溢出
              if (newCapacity - MAX_ARRAY_SIZE > 0)   //限制最大容量
                  newCapacity = hugeCapacity(minCapacity);
          
              // 使用 Arrays.copyOf 方法创建一个大小为 newCapacity的新数组,并将 elementData 中的所有元素复制到新数组中。elementData 引用被更新为指向这个新数组
              elementData = Arrays.copyOf(elementData, newCapacity);  //复制数组
      }
      
      ----------------------------------------------------------------------
      //hugeCapacity方法,grow方法里调用了该方法
          
      //这个方法用于处理非常大的数组容量
      private static int hugeCapacity(int minCapacity) {
          if (minCapacity < 0)  // 检查最小容量是否为负数
              throw new OutOfMemoryError();   // 如果是,抛出内存不足错误
          //三元表达式
          return (minCapacity > MAX_ARRAY_SIZE) ?  //如果所需容量超过最大数组大小
              Integer.MAX_VALUE :  // 如果是,就返回最大整数值,表示无法满足需求
              MAX_ARRAY_SIZE;      // 否则,返回最大数组大小
      }    
      

结论:

1.添加第一个元素是,底层会创建一个新的长度为10的数组
2.存满时,会扩容1.5倍

小结

  • 初始容量:
    • 如果使用 ArrayList 的默认空参构造器时,它的初始容量就是 0
    • 如果使用 ArrayList 的有参构造器时,它的初始容量就是你传入的参数 initialCapacity的值
  • 动态扩容大小:
    • 添加第一个元素是,底层会创建一个新的长度为10的数组
    • 存满时,会扩容1.5倍

标签:容量,int,ArrayList,elementData,minCapacity,数组,可变,长度
From: https://blog.csdn.net/m0_72057247/article/details/141370446

相关文章

  • 深入理解 Java 中 ArrayList 的底层原理
    在这篇博客中,我们将深入探讨ArrayList的底层实现原理,并通过逐步剖析ArrayList的源码来理解其内部工作机制。我们将重点关注ArrayList的创建、元素添加、扩容机制等关键点。创建ArrayList集合对象ArrayList<String>list=newArrayList<>();使用空参构造器创建ArrayList集合......
  • Python之可变对象及其引用、深拷贝和浅拷贝
    可变对象及其引用深拷贝和浅拷贝可变对象及其引用Python中,变量名关联有值时才存在,如x=5变量名没有关联到特定的类型,类型有关联的对象觉得变量创建后即与特定的Python对象相关联Python维护命名空间,其中改变名与变量关联。这种联系,称为“引用”,也就是变量名引用对象......
  • YOLOv8改进 | 模块融合 | C2f融合可变形自注意力模块【模块缝合】
     秋招面试专栏推荐 :深度学习算法工程师面试问题总结【百面算法工程师】——点击即可跳转......
  • Python数据结构:元组详解(创建、访问、不可变特性)②
    @[toc]Python中的元组(Tuple)是一种重要的数据结构,与列表类似,但元组是不可变的,这意味着一旦创建,就无法修改。元组的不可变性使其在某些场景下比列表更具优势。本文将详细介绍Python元组的创建、访问、不可变特性,并附上一个综合复杂的例子,全面展示元组在实际编程中的应用。一......
  • Java中ArrayList集合—基础详解(知识点+代码示例)
    ArrayList(集合)ArrayList(集合)ArrayList(集合)10.1ArrayList成员方法10.2集合练习10.2.1添加字符串10.2.2添加数字10.2.3添加学生对象并遍历10.2.4集合概述:集合可以直接存储引用数据类型,不能直接存储基本数据类型,如果要存储基本数据类型,需要将基本数据类型变成对......
  • 利用C语言求字符串长度
    在C语言中库函数中已有求字符串长度的函数strlen,我们可以自己编写一个求字符串函数my_strlen求字符串长度注意:strlen函数返回类型是size_t,是无符号整型方法1:创建临时变量#include<stdio.h>intmy_strlen(char*str){   intcount=0;   while(*str!='\0......
  • web前端之根据字符串长度从长到短排序、中文字符串优先、样式循环、禁止冒泡、悬浮、
    MENU前言效果图htmlstyleJavaScript前言1、代码段由HTML、CSS(使用Sass语法)和JavaScript组成,创建一个文本框,用户可以在其中输入内容,并通过点击按钮进行操作。2、代码段的主要功能是允许用户输入一系列以、分隔的项,并根据长度对这些项进行排序(中文字符优先),然后......
  • 不可变字符串string的相关操作
    staticvoidMain(string[]args){//截取字符串stringstr1="ABCDEFGHIJKLMN";stringstr2=str1.Substring(0,4);//从0位开始截取,共截取4位;Console.WriteLine(str2);Console.WriteLin......
  • 浅谈哈希长度扩展攻击
    攻击原理:我们首先需要了解一下MessageAuthenticationcodes(MACs),称为消息验证码,一般用于服务器验证消息的真实性。服务器把密钥和消息连接起来,用摘要算法获取摘要,对于H(secret+data)此类构造的散列函数,在密钥长度****和数据已知的情况下,通常可以使用哈希长度扩展攻击。......
  • c语言计算二叉树的带权路径长度之和(WPL)
    1.WPL:树中全部叶节点的带权路径之和2.代码中所画的树为:3.求上述WPL:WPL=0*1+1*2+1*3+2*4+2*5=234.主要代码为:intwpl(Node*ROOT,inthigh){ intn=0; if(ROOT!=NULL){ n=ROOT->weight*high; n=n+wpl(ROOT->lchild,high+1); n=n+wpl(ROOT->rchild,high+1); } r......