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

第一章 前言概述

第01节 概述

底层说明

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

后期应用

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

第02节 区别

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

小结

ArrayList 集合的特点:

1. 线程不安全

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

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

第二章 核心代码

第01节 成员变量

代码

public class ArrayList<E> extends AbstractList<E> implements List<E>, 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<E> extends AbstractList<E> implements List<E>, 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<? 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<E> extends AbstractList<E> implements List<E>, 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集合扩容原理内容请搜索我们以前的文章或继续浏览下面的相关文章希望大家以后多多支持我们!

(0)

相关推荐

  • Java集合框架Collections原理及用法实例

    Collections工具类 Java里关于聚合的工具类,包含有各种有关集合操作的静态多态方法,不能实例化(把构造函数私有化) public class Collections { // Suppresses default constructor, ensuring non-instantiability. private Collections() { } } 和Collection的区别 Collection是接口,提供了对集合对象进行基本操作的通用接口方法,List.Set等多种具体的实

  • 简单的理解java集合中的HashSet和HashTree几个重写方法

    Java中的set是无序的,但是是不可重复的 HashSet底层是哈希表,通过调用hashcode和equals方法实现去重 当我们HashSet里面存的是字符串时,就能默认去重了,因为String已经重写了hashcode和euqals方法 public static void main(String[] args) { HashSet<String> set = new HashSet(); set.add("java"); set.add("c")

  • Java集合功能与用法实例详解

    本文实例讲述了Java集合功能与用法.分享给大家供大家参考,具体如下: 本文内容: 什么是集合 Collection Iterator List set Map Collections工具类 首发日期:2018-05-17 什么是集合: 集合是一种新容器,集合可以存储数量不固定的元素(数组的空间是固定的,你申请多少空间以后都不能改变),而集合可以动态的增加空间(有些是空间不够时新建一个足够大的数组再把原来的元素移到新的数组中). 集合的出现解决的几个问题: 存储数量不等的元素. 定义了数据结构,

  • Java集合 LinkedList的原理及使用详解

    LinkedList和ArrayList一样是集合List的实现类,虽然较之ArrayList,其使用场景并不多,但同样有用到的时候,那么接下来,我们来认识一下它. 一. 定义一个LinkedList public static void main(String[] args) { List<String> stringList = new LinkedList<>(); List<String> tempList = new ArrayList<>();

  • Java集合框架迭代器Iterator实现原理解析

    使用循环遍历集合 普通for循环 for(int i=0;i<10;i++){} 增强for循环 for(String str:list){} 什么是迭代器Iterator Iterator是Java中的一个接口,核心作用就是用来遍历容器的元素,当容器实现了Iterator接口后,可以通过调用Iterator()方法获取一个Iterator对象 为啥是调用容器里面的Iterator方法呢? 因为容器的实现有多种,不同的容器遍历规则不一样,比如:ArrayList.LinkedList.HashS

  • Java集合遍历实现方法及泛型通配

    集合定义 集合,集合是java中提供的一种容器,可以用来存储多个数据. 特点:数组的长度是固定的.集合的长度是可变的.集合中存储的元素必须是引用类型数据' 普通for遍历: //案例一 ArrayList<Person> arr=new ArrayList<Person>(); arr.add(new Person("张三",19)); arr.add(new Person("小红帽",20)); arr.add(new Person(&qu

  • Java集合中的fail-fast(快速失败)机制详解

    简介 我们知道Java中Collection接口下的很多集合都是线程不安全的, 比如 java.util.ArrayList不是线程安全的, 因此如果在使用迭代器的过程中有其他线程修改了list,那么将抛出ConcurrentModificationException,这就是所谓fail-fast策略. 这一策略在源码中的实现是通过 modCount 域,modCount 顾名思义就是修改次数,对ArrayList 内容的修改都将增加这个值,那么在迭代器初始化过程中会将这个值赋给迭代器的 exp

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

    第一章 前言概述 第01节 概述 底层说明 ArrayList是List的实现类,它的底层是用Object数组存储,线程不安全 后期应用 适合用于频繁的查询工作,因为底层是数组,可以快速通过数组下标进行查找 第02节 区别 区别方向 ArrayList集合 LinkedList集合 线程安全 不安全 不安全 底层原理 Object类型数组 双向链表 随机访问 支持(实现 RandomAccess接口) 不支持 内存占用 ArrayList 浪费空间, 底层是数组,末尾预留一部分容量空间 Link

  • Java中ArrayList集合的常用方法大全

    ArrayList集合的创建 非泛型 创建ArrayList集合对象,可以添加任意Object子类元素至集合 //非泛型创建的ArrayList集合对象可以保存任何类型的值 ArrayList list = new ArrayList(); list.add("str");//存入String类型数据 list.add(23);//存入int类型数据 list.add(2.5);//存入double类型数据 list.add('c');//存入char类型数据 泛型 采用泛型创建Arr

  • Java中Arraylist动态扩容方法详解

    前言 本文主要给大家介绍了关于Java中Arraylist动态扩容的相关内容,分享出来供大家参考学习,下面话不多说了,来一起看看详细的介绍吧. ArrayList 概述 ArrayList是基于数组实现的,是一个动态数组,其容量能自动增长.ArrayList不是线程安全的,只能用在单线程环境下.实现了Serializable接口,因此它支持序列化,能够通过序列化传输:实现了RandomAccess接口,支持快速随机访问,实际上就是通过下标序号进行快速访问:实现了Cloneable接口,能被克隆.

  • java中ArrayList和LinkedList的区别详解

    ArrayList和LinkedList都实现了List接口,有以下的不同点: 1.ArrayList是基于索引的数据接口,它的底层是数组.它可以以O(1)时间复杂度对元素进行随机访问.与此对应,LinkedList是以元素列表的形式存储它的数据,每一个元素都和它的前一个和后一个元素链接在一起,在这种情况下,查找某个元素的时间复杂度是O(n). 2.相对于ArrayList,LinkedList的插入,添加,删除操作速度更快,因为当元素被添加到集合任意位置的时候,不需要像数组那样重新计算大小或者

  • Java中ArrayList与顺序表的概念与使用实例

    目录 前言 泛型(Generic) 泛型的引入 泛型的基本概念 包装类(Wrapper Class) 包装类的引入 基本数据类型与包装类的对应关系 ArrayList与顺序表 ArrayList简介 ArrayList使用 ArrayList的构造 ArrayList常见方法 ArrayList的遍历 总结 前言 通过前面的博客我们已经大致了解了关于Java的基本知识,而下面的几篇博客我们着重开始对于数据结构的知识进行学习,这篇博客我们就了解关于顺序表和ArrayList的相关知识,从名字上我们

  • Java中ArrayList和LinkedList区别

    目录 1 前言 2 数据结构的区别 2.1 ArrayList 2.2 LinkedList 2.3 使用场景 3 源码分析 3.1 ArrayList核心源码 3.2 LinkedList核心源码 4 码农来洞见 4.1为什么ArrayList比LinkedList要快 4.2 注意ArrayList不同JDK版本源码 4.3 高并发下如何保证集合数据的同步 4.4 为什么Java的Vector类被认为是过时的或者废弃的 1 前言 许多语言,例如 Perl ,Python 和 Ruby ,都有

  • Java中List集合的深入介绍(超级推荐!)

    目录 1,Java集合介绍 2,List介绍 2.1 ArrayList集合 2.2 LinkedList集合 3,List常用方法 3.1 ArrayList 基本操作 3.2 LinkedList 基本操作 4,ArrayList和LinkedList比较 5,ArrayList源码分析 6,LinkedList源码分析 7,小结 1,Java集合介绍 作为一个程序猿,Java集合类可以说是我们在工作中运用最多.最频繁的类.相比于数组(Array)来说,集合类的长度可变,更加方便开发. Ja

  • Java中ArrayList与顺序表的定义与实现方法

    目录 1.线性表 定义 特征 2.顺序表 定义 实现 打印数组 新增元素 判断是否包含某个元素 查找元素 获取pos位置的元素 更改pos位置的值 删除操作 获取顺序表长度 清空顺序表 3.ArrayList 简介: 使用 一些常见方法 ArrayList的遍历 总结 1.线性表 定义 线性表是最基本.最简单.也是最常用的一种数据结构.线性表(linear list)是数据结构的一种,一个线性表是n个具有相同特性的数据元素的有限序列. 常见的线性表:顺序表.链表.栈.队列... 线性表在逻辑上是

  • Java中ArrayList类的使用方法

    Java中ArrayList类的用法 1.什么是ArrayList ArrayList就是传说中的动态数组,用MSDN中的说法,就是Array的复杂版本,它提供了如下一些好处: 动态的增加和减少元素 实现了ICollection和IList接口 灵活的设置数组的大小 2.如何使用ArrayList 最简单的例子: ArrayList List = new ArrayList(); for( int i=0;i <10;i++ ) //给数组增加10个Int元素 List.Add(i); //..

  • java arrayList遍历的四种方法及Java中ArrayList类的用法

    java arrayList遍历的四种方法及Java中ArrayList类的用法 package com.test; import java.util.ArrayList; import java.util.Iterator; import java.util.List; public class ArrayListDemo { public static void main(String args[]){ List<String> list = new ArrayList<String

随机推荐