ArrayList的创建与扩容机制详解

ArrayList的创建与扩容

一.创建ArrayList

在这里插入图片描述

1.元素的存储

粗浅讲一下 ArrayList 底部还是用数组存储具体元素的
还使用了一个计数器 size存储当前数组存放最后一个元素的下标加1 也就是当前元素的个数;
在这里插入图片描述

2.无参的构造方式:
		//无参构造
		ArrayList<String> arrayList=new ArrayList();
		System.out.println(arrayList.size());   //输出arraylist里面的内容长度                为0
		System.out.println(getArrayListLength(arrayList));//输出arrayList的当前容量   为0
		//下面这个静态方法 getArrayListLength(arrayList) 会贴上去 大家可以复制一下自己去测试下看看

默认容量:0
过程是:将ArrayList里面用来存储内容的Object 数组elementData 赋值一个静态常量
在这里插入图片描述
该静态常量也是Object []即Object类型的数组 只不过内容为空容量为空
在这里插入图片描述
前面两个静态常量大家先记住,等后面分析源码的时候会用到;

3.传入指定容量参数的构造方式
		ArrayList<String> arrayList1=new ArrayList(8);
		System.out.println(arrayList1.size());  //输出arraylist里面的内容长度                为0
		System.out.println(getArrayListLength(arrayList1));//输出arrayList的当前容量   为8
		arrayList1.addAll(Arrays.asList("你好1","你好2","你好3","你好4","你好5","你好6","你好7","你好8"));
		//Arrays.asList 是调用一个Arrays里的静态方法快速创建了一个List类型的容器(外部声明为List 其实底部是继承自AbstracArraylist抽象类的
		//Arrays里面的一个内部类)   作用是接受一个字符串的动态参数创建出一个List对象 然后作为参数让arrayList1添加进去
4.传入集合参数的构造方式
		ArrayList<String> arrayList2=new ArrayList<String>(arrayList1);
		System.out.println(arrayList2.size());  //输出arraylist里面的内容长度                为0
		System.out.println(getArrayListLength(arrayList2));//输出arrayList的当前容量   为8

输出:
在这里插入图片描述

二.如何查看一个ArrayList的容量与内容长度?

查看内容长度:

调用arrayList.size()方法:
在这里插入图片描述
该方法会返回ArrayList里的size属性

		System.out.println(arrayList.size());  

查看当前容量

大家可以复制粘贴一下一下代码 自己也可以去测试和使用

	public static int getArrayListLength(ArrayList list) throws Exception{
	    //获取Class对象
	    Class c = Class.forName("java.util.ArrayList");
	    //映射Class对象c所表示类(即Arraylist)的属性
	    Field field = c.getDeclaredField("elementData");    //这里要import java.lang.reflect.Field;
	    //设置访问状态表示为true
	    field.setAccessible(true);
	    //返回指定对象上此 Field 表示的字段的值
	    Object[] object = (Object[])field.get(list);
	    return object.length;
	}

该方法是利用反射机制输出当前ArrayList的容量 会输出当前底层Object数组的大小 也就是容量 具体实现可以不必在意,只要注意怎么使用
1.注意导入包:

import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Arrays;

2.注意在main方法体外部加上抛出异常的声明

public static void main(String[] args) throws Exception {
}

3.之后就可以直接用方法名调用了

System.out.println(getArrayListLength(arrayList1));

三.创建后的扩容

1.无参构造生成对象容量的首次扩充

在这里插入图片描述
使用add()添加一个字符串之后容量直接变成了10 这是为什么?去看看源码:
在这里插入图片描述
接着再往里看底部:
在这里插入图片描述
我们这时候需要的最小容量是0+1 也就是1,而如果你是使用无参构造,他会返回一个 你需要的最小容量1 与 一个常量值DEFAULT_CAPACITY(值为10)的最大值.
所以此时容量newCapacity 为10
回到上一层
在这里插入图片描述
grow(minCapacity);是具体实现扩容的底层代码
在这里插入图片描述
Arrays.copyOf(elementData, newCapacity)该方法再底部新建了一个容量为newCapacity的新数组 并讲elementData数组里面的所有元素复制了进去 ,最后再将创建的新数组引用值赋到当前ArrayList的Object [] elementData下,这样就完成了一次扩容
这是中间代码块的作用与含义:(看不懂可以先忽略)
在这里插入图片描述
回头再看一下结果:
在这里插入图片描述
所以我们得出的结论是:
无参构造的情况下,在使用add添加一个元素的时候,首次扩充就会扩充到容量为10的程度;(下一次扩充 就是要add第11个元素的时候)

2.给定指定容量构造方式生成对象的首次扩充

		ArrayList<String> arrayList1=new ArrayList(0);//如果指定了容量8的话  首次添加不会扩充
													 //其中有两个步骤体现不出来 这里我传入参数 设置其为0
													//注意 我设置其为0,和无参构造是不同的
													//不同点就体现在其首次扩充的时候
		System.out.println(arrayList1.size());
		System.out.println(getArrayListLength(arrayList1));
		arrayList1.add("你好");
		System.out.println("添加一个");
		System.out.println(arrayList1.size());              //1
		System.out.println(getArrayListLength(arrayList1)); //1  注意1: 这就是区别 如果是无参构造 第一次扩充就会扩充10个;
		arrayList1.add("你好2");
		System.out.println("添加第二个");
		System.out.println(arrayList1.size());				//2
		System.out.println(getArrayListLength(arrayList1)); //2
		arrayList1.add("你好3");
		System.out.println("添加第三个");			
		System.out.println(arrayList1.size());				//3
		System.out.println(getArrayListLength(arrayList1)); //3
		arrayList1.add("你好4");
		System.out.println("添加第四个");
		System.out.println(arrayList1.size());				//4
		System.out.println(getArrayListLength(arrayList1)); //4
		arrayList1.add("你好5");
		System.out.println("添加第5个");
		System.out.println(arrayList1.size());				//5
		System.out.println(getArrayListLength(arrayList1)); //6    注意2: 前面发生的扩充都是 添加一个扩充一个
		

输出结果:
在这里插入图片描述
底层原因:
在这里插入图片描述
需要多少容量:当前容量 + 传进来几个元素(现在都使用add 一次只加1个)
第一次添加 需要0+1 minCapacity,而当前容量为0
0*1.5=0;0<1 进入if代码块里面 最终容量为1
第二次添加 需要1+1 minCapacity,而当前容量为1
1+1/2(int取整)=1;1<2 进入if代码块里面 最终容量为2
第三次添加 需要2+1 minCapacity,而当前容量为2
2+2/2(int取整)=3;3不小于3 不进入if代码块里面直接return 最终容量为3
第四次添加 需要3+1 minCapacity,而当前容量为3
3+3/2(int取整)=4;4不小于4 不进入if代码块里面直接return 最终容量为4
第五次添加 需要4+1 minCapacity,而当前容量为4
4+4/2(int取整)=6;6不小于5 不进入if代码块里面直接return 最终容量为6
这就是这几次运算的过程:
什么时候按1.5倍扩充来 什么时候按自己最小容量需求来
在这里插入图片描述

3.使用addALL一次添加多个元素时的首次扩充情况

4.后续多次扩容情况

1. 扩充1.5倍满足了需求

就按照1.5倍来

2. 扩充1.5倍没有满足需求

就按照具体需要多少扩充多少

  • 42
    点赞
  • 53
    收藏
    觉得还不错? 一键收藏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值