ArrayList自动扩容(详细篇)

简介: ArrayList自动扩容(详细篇)

                           一.为什么要进行扩容

1.1ArrayLiat底层介绍

ArrayList`是 Java 中的一个常用集合类,其内部实现是基于数组的,可以通过下标来访问和修改其中的元素。在操作 `ArrayList` 时,如果我们向其中添加的元素个数超过了已分配的数组长度,则需要对数组进行扩容。这也是 `ArrayList` 能够自动扩容的原因。

1.2扩容原因:

下面是对 ArrayList 扩容的必要性进行解释:

1. 提高效率

数组在内存中是一段连续的空间,在同一时间内,其所有元素的空间是连续的。在 `ArrayList` 中,当我们需要向其中添加元素时,如果发现当前数组的长度已经达到了其最大值,我们就需要对其进行扩容。扩容的意思是在内存中重新分配一个可以容纳更多元素的连续空间,并将原有数组的元素复制到新的空间中。这样,我们就可以向新的空间中添加元素了。通过扩容,我们避免了在数组中频繁移动元素,提高了程序的效率

2. 避免数组下标越界

当我们访问数组时,如果使用的下标超出了数组的实际长度,则会发生数组下标越界异常。扩容可以避免这种情况发生,因为扩容会重新分配更大的内存空间,并修改数组的长度,使得我们可以在新的空间中添加元素,避免了访问数组时下标越界的问题。

3. 降低内存碎片

扩容可以避免内存碎片的产生。在执行增删操作时,如果数组中存在空洞,即有些位置的元素被删除后,造成数组远小于原来的大小,这时也需要重新分配内存空间。如果这种情况发生频繁,会导致内存空间的碎片化,使得程序性能变差。而数组的扩容操作在内存中分配连续空间,可以避免这种问题的出现,从而降低内存碎片的产生

1.3什么是内存碎片化?

1.3.1定义:

指在程序运行过程中,由于空间的分配和释放不规则,形成了很多不连续、空闲的内存片段,使得原本容易找到连续空间的程序运行变得困难。内存中的空闲区域是不连续的,当需要分配一个大段的内存时,就会因为找不到足够的连续空间而产生问题。这种情况常常发生在反复分配和释放内存的场景中,例如大数据量的操作或多次执行动态数组扩容等。

1.3.2解决方法

内存空间的碎片化会对系统性能产生负面影响,因为程序在分配或释放内存时需要不停地将内存碎片合并或分割,这样会浪费更多的时间和系统资源。在程序的现代化中,应该尽可能地避免或减少内存碎片的出现,从而优化程序性能。

以下是几种减少内存碎片的方法:

  1. 使用内存池技术,固定分配一部分内存块供程序使用。
  2. 动态分配时,采用内存对齐方式,尽量使得分配出来的内存块尺寸是确定的。
  3. 尽量减少对象的分配与回收,从而降低内存碎片化的程度。
  4. 对于数据结构动态增长的场景,采用增量式扩容的策略,避免一次性分配过大的内存空间。

                    二.进行ArrayLiat扩容操作

1.扩容的底层原理:

ArrayList 自动扩容的实现:依赖于 `ensureCapacityInternal()` 和 `grow()` 两个方法。

当添加一个新元素到 ArrayList 时,ArrayList 会先判断当前存储元素的数组容量是否已经达到了最大大小(即 `Integer.MAX_VALUE`),如果已经达到了则不再扩容。否则,如果当前存储元素的数组容量已经满了,那么就需要扩容:

1. `ensureCapacityInternal()` 方法会先计算出新的容量大小原来的容量大小(记为 oldCapacity)加上原来的容量大小的一半(即 oldCapacity >> 1)。

2. 如果新的容量大小小于当前所需的容量(即当前 arrayList 的大小加 1),则新容量大小为当前所需的容量加 1

3. `grow()` 方法会创建一个新的存储元素的数组,其大小就是新的容量大小。然后它会调用 `Arrays.copyOf()` 方法来将旧数组中的元素复制到新数组中。

最后,ArrayList 会将新的数组作为内部存储数组,并更新相关信息。

这样,每次扩容的大小为原数组大小的约1.5倍,这个值是可以设置的,通过 `ensureCapacity()` 方法来调整。如果需要添加很多元素,建议预估需要的大小,调用 `ensureCapacity()` 来避免频繁的扩容操作,提高性能。

2.实现操作

2.1代码截屏:

2.2效果图:

2.3注意事项

1.ArrayList  默认长度为10,当然我们也可以设置长度为其他值

例如:  ArrayList list=new  ArrayList<>(50); 当我们打印集合长度是就为:50

好处:可以减少扩容次数,提高程序在处理,运行时的速度

2.  ArrayList 的增长因子为0.5倍数,扩容倍数为1.5倍数!在进行扩容是数组长度在有小数时会向下取整比如说:在效果图数组长度15时:在添加第16个元素时扩容后的长度为:原来长度*0.5+原来长度(向下取整)=新长度;运用这个公式就可以得到新的扩容数组长度为22

2.3源码:

package liuzhi_list;
import java.lang.reflect.Field;
import java.util.ArrayList;
public class demo1 {
       public static void main(String[] args)   throws  Exception{
         //首先创建一个arrayList  集合
         ArrayList list=new  ArrayList<>(50);
         //通过for循环添加数据  添加100条
         for (int i = 0; i<100; i++) {
        list.add(i);
        //打印输出结果
      System.out.print(i+"\r");
        //发现集合通过for循环增加了100条数据
        System.out.println("集合有多少数据:"+list.size());
        getLength(list);  
      }      
  }         
     /**
      * 此方法通过调用arrayliat底层来操作
      * @param list
      * @throws Exception
      * @throws Exception
      */
    private static void getLength(ArrayList list) throws Exception, Exception {
      //通过调用 获取 ArrayList对象类部类获取底层的elementData字段
      Field f = list.getClass().getDeclaredField("elementData");
      //因为arraylist底层是私有化 必须通过反射 并且打开私有化权限
      f.setAccessible(true);
      //作用是获取 ArrayList 对象的底层数组
      Object[] object = (Object[]) f.get(list);
      //打印数组长度
      System.out.println("当前数组长度: "+object.length);
    }
}
相关文章
|
负载均衡 安全 算法
深入解析线程安全的Hashtable实现
深入解析线程安全的Hashtable实现
|
存储 安全 Java
ConcurrentHashMap底层实现原理
ConcurrentHashMap底层实现原理
827 0
|
Java Spring 容器
springboot的启动流程
springboot的启动流程
|
Java 索引 容器
Java ArrayList扩容的原理
Java 的 `ArrayList` 是基于数组实现的动态集合。初始时,`ArrayList` 底层创建一个空数组 `elementData`,并设置 `size` 为 0。当首次添加元素时,会调用 `grow` 方法将数组扩容至默认容量 10。之后每次添加元素时,如果当前数组已满,则会再次调用 `grow` 方法进行扩容。扩容规则为:首次扩容至 10,后续扩容至原数组长度的 1.5 倍或根据实际需求扩容。例如,当需要一次性添加 100 个元素时,会直接扩容至 110 而不是 15。
650 4
Java ArrayList扩容的原理
|
架构师 Java 开发者
得物面试:Springboot自动装配机制是什么?如何控制一个bean 是否加载,使用什么注解?
在40岁老架构师尼恩的读者交流群中,近期多位读者成功获得了知名互联网企业的面试机会,如得物、阿里、滴滴等。然而,面对“Spring Boot自动装配机制”等核心面试题,部分读者因准备不足而未能顺利通过。为此,尼恩团队将系统化梳理和总结这一主题,帮助大家全面提升技术水平,让面试官“爱到不能自已”。
得物面试:Springboot自动装配机制是什么?如何控制一个bean 是否加载,使用什么注解?
|
存储 Java API
Java数据结构之ArrayList(如果想知道Java中有关ArrayList的知识点,那么只看这一篇就足够了!)
Java数据结构之ArrayList(如果想知道Java中有关ArrayList的知识点,那么只看这一篇就足够了!)
Java数据结构之ArrayList(如果想知道Java中有关ArrayList的知识点,那么只看这一篇就足够了!)
|
存储 安全 Java
synchronized原理详解(通俗易懂超级好)
当系统检查到锁是重量级锁之后,会把等待想要获得锁的线程进行阻塞,被阻塞的线程不会消耗cpu。但是阻塞或者唤醒一个线程时,都需要操作系统来帮忙,这就需要从用户态转换到内核态,而转换状态是需要消耗很多时间的,有可能比用户执行代码的时间还要长。
synchronized原理详解(通俗易懂超级好)
|
存储 算法 索引
HashMap底层数据结构及其增put删remove查get方法的代码实现原理
HashMap 是基于数组 + 链表 + 红黑树实现的高效键值对存储结构。默认初始容量为16,负载因子为0.75。当存储元素超过容量 * 负载因子时,会进行扩容。HashMap 使用哈希算法计算键的索引位置,通过链表或红黑树解决哈希冲突,确保高效存取。插入、获取和删除操作的时间复杂度接近 O(1)。
370 0
|
存储 监控 数据库
什么是聚集索引和非聚集索引?
【8月更文挑战第3天】
9402 6
|
开发框架 Java 开发者
Spring Boot中的自动装配原理
Spring Boot中的自动装配原理
3679 1