ArrayList初始容量的深入研究

一、内部数组大小与初始容量的关系

1、什么是初始容量?

初始容量指的是ArrayList在创建时分配的内部数组大小,也就是能够存储元素的空间大小。通常情况下我们使用不带参数的构造方法来创建ArrayList,这种情况下,初始容量为10。但是,如果我们有提前知道ArrayList中会存储的元素数量,那么在创建时可以通过传入一个整数值来指定初始容量。这样,初始化时就能够自动设置内部数组的大小。

2、内部数组的实际大小是多少?

除非使用带有参数构造函数来指定初始容量,否则ArrayList的内部数组大小始终为10。但是,这个初始容量只是初始值,实际上ArrayList会自动扩容。

3、什么时候会触发自动扩容?

当我们往ArrayList中添加超出容量的元素时,就会触发ArrayList自动扩容。具体来说,它会把当前的内部数组复制到一个更大的数组中,然后继续添加新元素。


    // 通过构造函数指定初始容量
    ArrayList list = new ArrayList(20); 
    // 内部数组大小是多少?
    System.out.println(list.size()); // 输出 0

二、初始容量与性能的关系

1、为什么初始容量重要?

初始容量实际上在ArrayList的性能中扮演着重要的角色。不同的初始容量会影响到ArrayList的性能表现。如果初始容量太小,ArrayList就会频繁地进行扩容操作,影响效率。而如果初始容量太大,将导致内存浪费。

2、如何确定合适的初始容量?

为了避免频繁扩容,我们需要给出一个合适的初始容量。通常,初始容量应该尽可能地接近预期的元素个数以减少内存浪费。一般来说,我们可以通过创建一个稍大于实际元素个数的ArrayList,然后调整其初始容量来实现优化。

3、通过代码测试ArrayList在不同初始容量下的性能:


    // 不同的初始容量对添加性能的影响
    int size = 1000000;
    long start = System.currentTimeMillis();
    ArrayList list1 = new ArrayList(size);
    for (int i = 0; i < size; i++) {
        list1.add(i);
    }
    long end = System.currentTimeMillis();
    System.out.println("指定初始容量:" + (end - start) + " 毫秒");
    
    start = System.currentTimeMillis();
    ArrayList list2 = new ArrayList();
    for (int i = 0; i < size; i++) {
        list2.add(i);
    }
    end = System.currentTimeMillis();
    System.out.println("未指定初始容量:" + (end - start) + " 毫秒");

三、初始容量调整的实现原理

1、如何调整初始容量?

调整初始容量的核心方法是ensureCapacity(),通过调用这个方法来设置ArrayList的实际容量。如果我们知道ArrayList中需要多少元素,可以在创建之时指定容量。否则,在添加元素时,每当容量不足时,ArrayList就会自动增加一个“增量”来调整容量。默认情况下,“增量”是当前容量的一半。

2、如何自定义“增量”?

我们可以通过设置ArrayList(int initialCapacity)构造方法中的增量参数来自定义“增量”值。如果我们希望增量增加一倍或者减半,那么只需在创建ArrayList对象时传递一个新的增量参数即可。

3、添加元素时自动调整容量的实现原理:


    // 添加元素时自动增加容量的实现原理
    private static final int DEFAULT_CAPACITY = 10;
    private Object[] elementData;
    private int size;
    
    private void ensureCapacity(int capacity) {
        if (capacity > elementData.length) {
            // 计算新长度
            int newCapacity = elementData.length + (elementData.length >> 1);
            // 如果扩增容量还是不够,就直接扩大容量到需要的大小
            if (newCapacity < capacity) {
                newCapacity = capacity;
            }
            // 复制原数组
            elementData = Arrays.copyOf(elementData, newCapacity);
        }
    }
    
    public boolean add(E e) {
        // 添加元素时自动扩容
        ensureCapacity(size + 1);
        elementData[size] = e;
        size++;
        return true;
    }

四、初始容量与内存开销的关系

1、如何减少内存开销?

随着初始容量的增大,ArrayList占用的内存空间也会增多。因此,如果我们想要降低内存开销,就需要尽可能地减少初始容量。

2、如何查看ArrayList占用的内存空间?

我们可以通过在Java应用程序中使用JProfiler等内存分析工具来查看ArrayList实例占用的内存空间。

3、代码示例:如何比较不同初始容量的内存占用情况:


    // 不同初始容量下内存占用的差异
    Runtime runtime = Runtime.getRuntime();
    long usedMemoryBefore = runtime.totalMemory() - runtime.freeMemory();
    ArrayList list = new ArrayList();
    for (int i = 0; i < 100000; i++) {
        list.add("Element " + i);
    }
    long usedMemoryAfter = runtime.totalMemory() - runtime.freeMemory();
    System.out.println("内存占用:" + (usedMemoryAfter - usedMemoryBefore) + " bytes");
    
    usedMemoryBefore = runtime.totalMemory() - runtime.freeMemory();
    list = new ArrayList(100000);
    for (int i = 0; i < 100000; i++) {
        list.add("Element " + i);
    }
    usedMemoryAfter = runtime.totalMemory() - runtime.freeMemory();
    System.out.println("内存占用:" + (usedMemoryAfter - usedMemoryBefore) + " bytes");

五、总结

本文从各个方面深入阐述了ArrayList的初始容量问题,包括内部数组大小与初始容量的关系、初始容量与性能的关系、初始容量调整的实现原理、初始容量与内存开销的关系。通过深入研究ArrayList的初始容量问题,我们可以更好地使用它,避免无谓的内存浪费,优化程序性能。

原创文章,作者:TNHOT,如若转载,请注明出处:https://www.506064.com/n/329021.html

(0)
打赏 微信扫一扫 微信扫一扫 支付宝扫一扫 支付宝扫一扫
TNHOTTNHOT
上一篇 2025-01-14 18:55
下一篇 2025-01-14 18:55

相关推荐

  • 深入解析Vue3 defineExpose

    Vue 3在开发过程中引入了新的API `defineExpose`。在以前的版本中,我们经常使用 `$attrs` 和` $listeners` 实现父组件与子组件之间的通信,但…

    编程 2025-04-25
  • 深入理解byte转int

    一、字节与比特 在讨论byte转int之前,我们需要了解字节和比特的概念。字节是计算机存储单位的一种,通常表示8个比特(bit),即1字节=8比特。比特是计算机中最小的数据单位,是…

    编程 2025-04-25
  • 深入理解Flutter StreamBuilder

    一、什么是Flutter StreamBuilder? Flutter StreamBuilder是Flutter框架中的一个内置小部件,它可以监测数据流(Stream)中数据的变…

    编程 2025-04-25
  • 深入探讨OpenCV版本

    OpenCV是一个用于计算机视觉应用程序的开源库。它是由英特尔公司创建的,现已由Willow Garage管理。OpenCV旨在提供一个易于使用的计算机视觉和机器学习基础架构,以实…

    编程 2025-04-25
  • 深入了解scala-maven-plugin

    一、简介 Scala-maven-plugin 是一个创造和管理 Scala 项目的maven插件,它可以自动生成基本项目结构、依赖配置、Scala文件等。使用它可以使我们专注于代…

    编程 2025-04-25
  • 深入了解LaTeX的脚注(latexfootnote)

    一、基本介绍 LaTeX作为一种排版软件,具有各种各样的功能,其中脚注(footnote)是一个十分重要的功能之一。在LaTeX中,脚注是用命令latexfootnote来实现的。…

    编程 2025-04-25
  • 深入了解Python包

    一、包的概念 Python中一个程序就是一个模块,而一个模块可以引入另一个模块,这样就形成了包。包就是有多个模块组成的一个大模块,也可以看做是一个文件夹。包可以有效地组织代码和数据…

    编程 2025-04-25
  • 深入探讨冯诺依曼原理

    一、原理概述 冯诺依曼原理,又称“存储程序控制原理”,是指计算机的程序和数据都存储在同一个存储器中,并且通过一个统一的总线来传输数据。这个原理的提出,是计算机科学发展中的重大进展,…

    编程 2025-04-25
  • 深入剖析MapStruct未生成实现类问题

    一、MapStruct简介 MapStruct是一个Java bean映射器,它通过注解和代码生成来在Java bean之间转换成本类代码,实现类型安全,简单而不失灵活。 作为一个…

    编程 2025-04-25
  • 深入理解Python字符串r

    一、r字符串的基本概念 r字符串(raw字符串)是指在Python中,以字母r为前缀的字符串。r字符串中的反斜杠(\)不会被转义,而是被当作普通字符处理,这使得r字符串可以非常方便…

    编程 2025-04-25

发表回复

登录后才能评论