热门标签 | HotTags
当前位置:  开发笔记 > 运维 > 正文

在java中ArrayList集合底层的扩容原理

这篇文章主要介绍了在java中ArrayList集合底层的扩容原理,文中有非常详细的代码示例,对正在学习java的小伙伴们有一定的帮助,需要的朋友可以参考下

第一章 前言概述

第01节 概述

底层说明

ArrayList是List的实现类,它的底层是用Object数组存储,线程不安全

后期应用

适合用于频繁的查询工作,因为底层是数组,可以快速通过数组下标进行查找

第02节 区别

区别方向 ArrayList集合 LinkedList集合
线程安全 不安全 不安全
底层原理 Object类型数组 双向链表
随机访问 支持(实现 RandomAccess接口) 不支持
内存占用 ArrayList 浪费空间, 底层是数组,末尾预留一部分容量空间 LinkedList占用空间比ArrayList多,存在头尾地址值占用空间

小结

ArrayList 集合的特点:

1. 线程不安全

2. 底层数据结构是数组(查询快,增删慢,支持快速随机访问)

3. 内存占用会存在部分浪费,末尾会预留一部分容量空间

第二章 核心代码

第01节 成员变量

代码

public class ArrayList extends AbstractList implements List, RandomAccess, Cloneable, java.io.Serializable{ 

	/**
	 * 默认初始容量大小, 默认初始化容量为10
	 */
	private static final int DEFAULT_CAPACITY = 10;

	/**
	 * 空数组。当集合内容置空的时候,底层使用空数组标记
	 */
	private static final Object[] EMPTY_ELEMENTDATA = {};

	/**
	* 用于无参数构造方法,创建对象的时候,使用这个数组定义。
	* 相比上面的数组 EMPTY_ELEMENTDATA 可以进行区分,知道在添加元素的过程当中,容量增加多少
	*/
	private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

	/**
	 * 保存ArrayList数据的数组,这个数组会不断的改变,前面没有被 final 修饰表示地址值会发生的变化
	 */
	transient Object[] elementData; // non-private to simplify nested class access

	/**
	 * ArrayList 所包含的元素个数,也就是在 ArrayList 集合底层,通过 size()方法,获取到的元素个数
	 */
	private int size; 
}

补充

1. ArrayList 集合底层存在6个成员变量
	还有一个 private static final long serialVersiOnUID= 8683452581122892189L;  
	序列化使用, 目前针对于当前的操作过程当中, 暂时不会使用得到。

2. ArrayList 集合当中核心的两个成员变量
	A. 底层维护数组  		transient Object[] elementData;
	B. 存储的元素个数		private int size; 

第02节 构造方法

代码

public class ArrayList extends AbstractList implements List, RandomAccess, Cloneable, java.io.Serializable{ 

    /**
     * 构造一个初始长度为0的空数组。
     */
    public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

    /**
     * 在构造方法当中,传递一个参数集合c,将集合 c 转换成为新的列表
	 * elementData 当中的数据,就是新集合存放的数据
     * c.toArray 就是将原始集合的数据取出
	 * 如果取出的集合长度不为零的情况下,则复制 参数集合c 到 elementData 当中
	 * 如果取出的集合长度为零的情况下,则赋值为空数组  EMPTY_ELEMENTDATA 
     */
    public ArrayList(Collection<&#63; extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) { 
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else { 
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }
	
	 /**
     * 指定参数的长度大小
	 * 如果初始化的长度大于0,则返回新的数组
	 * 如果初始化的长度等于0,则返回默认的空数组作为集合 this.elementData = EMPTY_ELEMENTDATA;
	 * 如果初始化的长度小于0,则出现非法参数异常
     */
    public ArrayList(int initialCapacity) {
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+ initialCapacity);
        }
    }
}

补充(一) 无参构造创建对象

在这里插入图片描述

补充(二)带参构造创建对象,带有int类型参数

在这里插入图片描述

补充(三)带参构造创建对象,带有 集合类型参数

在这里插入图片描述

第三章 扩容操作

第01节 扩容代码

核心方法介绍

来自于 ArrayList 集合当中的方法:
	1. public boolean add(E e){ ... }
	2. private void add(E e, Object[] elementData, int s){ .... }
	3. private Object[] grow()
	4. private Object[] grow(int minCapacity)

来自于其他类当中的功能
	1. Arrays.copyOf(elementData, newCapacity);  表示来自于 数组工具类 Arrays 当中的 copyOf() 底层使用的是 System.arraycopy() 方法
	2. Math.max(DEFAULT_CAPACITY, minCapacity)   表示来自于 数学工具类 Math 当中的 max() 方法,比较两个数据最大值,取较大者,返回

核心代码解释

public class ArrayList extends AbstractList implements List, RandomAccess, Cloneable, java.io.Serializable{ 

    /**
     * 将指定的元素添加到此集合的末尾位置
     *
     * modCount 是来自于父类的 AbstractList 当中的成员变量
     * add(e, elementData, size) 调用自己下面的重载方法
	 * return true  表示当前的方法,一定可以添加成功,因为List系列的集合添加数据都是允许成功的 true 如果是Set此方法返回false
     */
    public boolean add(E e) {
        modCount++;
        add(e, elementData, size);
        return true;
    }
	
	
	/**
     * 这个方法是私有方法,仅仅自己可以使用。不允许外界访问得到。便于上面的 add() 方法重载调用的
	 * 参数1: 表示需要添加的元素数据 E e
	 * 参数2: 表示成员变量当中, 需要维护管理的底层数组  Object[] elementData
     * 参数3: 表示成员变量当中, size 容器 elementData 当中存放的真实有效的数据个数
	 * 方法里面的逻辑: 当size已经等于了内部容器 elementData 的最大长度,则准备进行扩容的操作,扩容使用 grow() 方法
	 * 无论上面是否进行了扩容的操作,这里都需要将添加的元素赋值到数组当中,也就是 elementData[s] = e;
	 * 并且将当前成员变量的 size 在原始数据的基础上面,增加1,表示添加了新的元素之后,长度变化了,增加了1
     */
    private void add(E e, Object[] elementData, int s) {
        if (s == elementData.length)
            elementData = grow();
        elementData[s] = e;
        size = s + 1;
    }
	
	
	/**
     * 这个方法是私有方法,仅仅自己可以使用。不允许外界访问得到。便于上面的 add() 方法调用的
	 * 方法的内部调用了 ArrayList 当中自己重载的方法 grow(size + 1) 同时传入了参数。
	 * 这里参数的含义,表示的是 集合当中总长度 size + 1 表示在原始size基础上增加1 
	 * 方法的返回值是一个新的数组,也就是扩容之后的数组
     */
	private Object[] grow() {
        return grow(size + 1);
    }


    /**
     * 这个方法是私有方法,仅仅自己可以使用。不允许外界访问得到。便于上面的 grow() 方法调用的
	 * 这里的参数 minCapacity ,就是上面传入的参数 size + 1,也就是说最小容量 minCapacity = size + 1
	 * 方法体当中的执行逻辑:
	 * 		1. 获取到了底层维护数组的长度 int oldCapacity = elementData.length; 这里就是旧容量 oldCapacity
	 *      2. 判断旧容量 oldCapacity 是否小于0,也就是 else 的逻辑,
	 *				如果满足 if 当中的逻辑, 则表示 旧数组当中存在数据,并且 旧数组并不是 默认容量的空数组地址值
	 *					说明: 扩容过的就不会是之前默认 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 的地址值
	 *					在这种情况下,就会得到 1.5被的数组长度整数,传递给 Arrays.copyOf()方法进行扩容,得到新数组返回
	 * 				如果满足 else 当中的逻辑,则返回 DEFAULT_CAPACITY 和 minCapacity 较大值。
	 * 					说明: DEFAULT_CAPACITY 值表示的是成员变量,默认为 10   
	 *					说明: minCapacity 在低于10的时候,表示的会是扩容添加的长度1,2,3..9.10.11..
     */
    private Object[] grow(int minCapacity) {
        int oldCapacity = elementData.length;
        if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            int newCapacity = ArraysSupport.newLength(oldCapacity,
                    minCapacity - oldCapacity, /* minimum growth */
                    oldCapacity >> 1           /* preferred growth */);
            return elementData = Arrays.copyOf(elementData, newCapacity);
        } else {
            return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
        }
    }
}

到此这篇关于在java中ArrayList集合底层的扩容原理的文章就介绍到这了,更多相关ArrayList集合扩容原理内容请搜索以前的文章或继续浏览下面的相关文章希望大家以后多多支持!


推荐阅读
  • 题库来源:安全生产模拟考试一点通公众号小程序G3锅炉水处理报名考试是安全生产模拟考试一点通生成的,G3锅炉水处理证模拟考试题库是根据G3锅炉水处理最新 ... [详细]
  • 2023年京东Android面试真题解析与经验分享
    本文由一位拥有6年Android开发经验的工程师撰写,详细解析了京东面试中常见的技术问题。涵盖引用传递、Handler机制、ListView优化、多线程控制及ANR处理等核心知识点。 ... [详细]
  • 深入解析 Apache Shiro 安全框架架构
    本文详细介绍了 Apache Shiro,一个强大且灵活的开源安全框架。Shiro 专注于简化身份验证、授权、会话管理和加密等复杂的安全操作,使开发者能够更轻松地保护应用程序。其核心目标是提供易于使用和理解的API,同时确保高度的安全性和灵活性。 ... [详细]
  • Startup 类配置服务和应用的请求管道。Startup类ASP.NETCore应用使用 Startup 类,按照约定命名为 Startup。 Startup 类:可选择性地包括 ... [详细]
  • 网易严选Java开发面试:MySQL索引深度解析
    本文详细记录了网易严选Java开发岗位的面试经验,特别针对MySQL索引相关的技术问题进行了深入探讨。通过本文,读者可以了解面试官常问的索引问题及其背后的原理。 ... [详细]
  • 本文探讨了如何在日常工作中通过优化效率和深入研究核心技术,将技术和知识转化为实际收益。文章结合个人经验,分享了提高工作效率、掌握高价值技能以及选择合适工作环境的方法,帮助读者更好地实现技术变现。 ... [详细]
  • 本文介绍如何在 Unity 的 XML 配置文件中,将参数传递给自定义生命周期管理器的构造函数。我们将详细探讨 CustomLifetimeManager 类的实现及其配置方法。 ... [详细]
  • Ralph的Kubernetes进阶之旅:集群架构与对象解析
    本文深入探讨了Kubernetes集群的架构和核心对象,详细介绍了Pod、Service、Volume等基本组件,以及更高层次的抽象如Deployment、StatefulSet等,帮助读者全面理解Kubernetes的工作原理。 ... [详细]
  • Hadoop入门与核心组件详解
    本文详细介绍了Hadoop的基础知识及其核心组件,包括HDFS、MapReduce和YARN。通过本文,读者可以全面了解Hadoop的生态系统及应用场景。 ... [详细]
  • 本文详细探讨了Java中StringBuffer类在不同情况下的扩容规则,包括空参构造、带初始字符串和指定初始容量的构造方法。通过实例代码和理论分析,帮助读者更好地理解StringBuffer的内部工作原理。 ... [详细]
  • 本文探讨了领域驱动设计(DDD)的核心概念、应用场景及其实现方式,详细介绍了其在企业级软件开发中的优势和挑战。通过对比事务脚本与领域模型,展示了DDD如何提升系统的可维护性和扩展性。 ... [详细]
  • 深入了解 Windows 窗体中的 SplitContainer 控件
    SplitContainer 控件是 Windows 窗体中的一种复合控件,由两个可调整大小的面板和一个可移动的拆分条组成。本文将详细介绍其功能、属性以及如何通过编程方式创建复杂的用户界面。 ... [详细]
  • 实体映射最强工具类:MapStruct真香 ... [详细]
  • 本文探讨了在Linux系统上使用Docker时,通过volume将主机上的HTML5文件挂载到容器内部指定目录时遇到的403错误,并提供了解决方案和详细的操作步骤。 ... [详细]
  • 探讨如何真正掌握Java EE,包括所需技能、工具和实践经验。资深软件教学总监李刚分享了对毕业生简历中常见问题的看法,并提供了详尽的标准。 ... [详细]
author-avatar
海蓝透了我的心
这个家伙很懒,什么也没留下!
PHP1.CN | 中国最专业的PHP中文社区 | DevBox开发工具箱 | json解析格式化 |PHP资讯 | PHP教程 | 数据库技术 | 服务器技术 | 前端开发技术 | PHP框架 | 开发工具 | 在线工具
Copyright © 1998 - 2020 PHP1.CN. All Rights Reserved | 京公网安备 11010802041100号 | 京ICP备19059560号-4 | PHP1.CN 第一PHP社区 版权所有