java 新建空数组_Java集合之ArrayList源码解析

本文深入探讨Java中的ArrayList集合类,详细解析ArrayList的内部机制,包括其动态数组的数据结构、初始化、添加与删除元素的源码分析,以及性能特点。通过对ArrayList的add、remove等关键方法的探讨,揭示ArrayList在查询和扩容时的效率差异。
摘要由CSDN通过智能技术生成

前言

集合在Java中是非常重要,不仅在Java项目开发中高频使用,在面试中也经常出现集合相关的问题。本文主要给大家介绍一下ArrayList集合类。

一、ArrayList简介

ArrayList是实现了基于动态数组的数据结构,一个ArrayList集合是存储在一块连续的内存中。如图1.1。

1、定义一个ArrayList集合A,并插入对象(Obj01-Obj05)。

2、A.add(Obj06)会将Obj06添加到集合A的最后面。

3、A.add(3,Obj07)是将Obj07插曲到集合A第三个元素(Obj04)的前面,这时候就需要将Obj04、Obj05、Obj06分别向下移动一个位置供Obj07插入。

4、A.remove(Obj02)将Obj02删除,后面的对面都需要像前移一个位置。

从步骤3和4中可以看出,ArrayList在插入和删除元素的时候,必然导致在该位置后的所有元素需要像后或者向前移动,因此,其效率相对会比较低。但是ArrayList的查询效率很高,因为只要知道ArrayList在内存中的开始位置,通过计算就能准确的定位到某一个元素的位置。

ef91241ffc57572cf9774f1a15f2e10b.png

图1.1

二、ArrayList 源码解析

ArrayList类属性

//ArrayList默认容量为10private static final int DEFAULT_CAPACITY = 10;//空数组,当传入的容量为0的时候使用,通过new ArrayList(0)创建时用的是这个空数组。private static final Object[] EMPTY_ELEMENTDATA = new Object[0];//空数组,这种是通过new ArrayList()创建时用的是这个空数组,与EMPTY_ELEMENTDATA的区别是在添加第一个元素时使用这个空数组的会初始化为DEFAULT_CAPACITY(10)个元素。private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = new Object[0];//存储元素的数组,真正存放元素的地方,使用transient是为了不序列化这个字段。transient Object[] elementData;//ArrayList中真正存储元素的个数private int size;//ArrayList容量的最大值private static final int MAX_ARRAY_SIZE = 2147483639;

构造方法ArrayList(int var1)

ArrayList(int var1)方法是创建一个初始容量为var1的集合,var1是创建集合传入的初始容量,如果大于0就初始化elementData为对应大小,如果等于0就使用EMPTY_ELEMENTDATA空数组,如果小于0抛出异常。如new ArrayList(3),会创建一个初始容量为3的集合,那么初始容量为3是不是这个集合就只能存储3个元素呢?答案是否定的,初始化容量为3的集合是可以存储超过3个元素的,因为ArrayList可自动扩容。

public ArrayList(int var1) { if (var1 > 0) { // 如果传入的初始容量大于0,就新建一个数组存储元素 this.elementData = new Object[var1]; } else { if (var1 != 0) { //如果不大于0且不等于0,则抛出异常(因初始化容量不能小与0) throw new IllegalArgumentException("Illegal Capacity: " + var1); } //如果初始化容量等于0,则使用空数组 EMPTY_ELEMENTDATA this.elementData = EMPTY_ELEMENTDATA; }​ }

构造方法ArrayList()

该构造方法没有传入初始的容量,则默认使用DEFAULTCAPACITY_EMPTY_ELEMENTDATA,这种方法是我们比较常用的。

public ArrayList() { this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;}

构造方法ArrayList(Collection extends E> var1)

该方法是把初始化的时候把传入的集合的元素初始化到ArrayList中,如果传入的集合元素个数为0,则初始化EMPTY_ELEMENTDATA空数组。

public ArrayList(Collection extends E> var1) { //将传入的集合转换成数据,并赋值给elementData this.elementData = var1.toArray(); if ((this.size = this.elementData.length) != 0) { // 检查var1.toArray()返回的是不是Object[]类型,如果不是,重新拷贝成Object[].class类型 if (this.elementData.getClass() != Object[].class) { this.elementData = Arrays.copyOf(this.elementData, this.size, Object[].class); } } else { //如果传入的集合元素个数为0,则初始化EMPTY_ELEMENTDATA空数组。 this.elementData = EMPTY_ELEMENTDATA; }​ }

add(E var1)方法

在集合末尾添加一个新元素。

public boolean add(E var1) { //判断如果新增一个元素,会不会超过ArrayList的容量,若超过,则扩容 this.ensureCapacityInternal(this.size + 1); //将新的元素添加到集合的末尾 this.elementData[this.size++] = var1; return true; }

ensureCapacityInternal(int var1)方法

该方法用来判断var1有没有超过当前ArrayList容量的大小,如果超过了则自动扩容,从这个方法我们可以看到,当使用ArrayList()构造方法的时候,初始化为空数组DEFAULTCAPACITY_EMPTY_ELEMENTDATA,但是当第一次使用Add方法往集合中添加元素的时候,集合会扩容为10。

private void ensureCapacityInternal(int var1) { //如果是空数组,则取var1和10两个值中的最大值 if (this.elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { var1 = Math.max(10, var1); } //将上一步取到的最大值,扩容ArrayList的大小 this.ensureExplicitCapacity(var1); }

ensureExplicitCapacity(int var1)方法

该方法判断var1与集合现在的元素个数的差,如果差值大于0,则表示现有集合的容量已经满足不了新增的元素,就会调用grow方法扩容。

private void ensureExplicitCapacity(int var1) { ++this.modCount; if (var1 - this.elementData.length > 0) { this.grow(var1); } }

grow(int var1)方法

private void grow(int var1) { //获取ArrayList现有的容量 int var2 = this.elementData.length; //将现有容量的值乘1.5倍(扩容以后的值) int var3 = var2 + (var2 >> 1); //判断扩容后的容量是否能满足需要的容量,如果不能则已需要的容量为准 if (var3 - var1 < 0) { var3 = var1; } // 如果新容量已经超过最大容量了,则使用最大容量 if (var3 - 2147483639 > 0) { var3 = hugeCapacity(var1); } // 以新容量拷贝出来一个新数组 this.elementData = Arrays.copyOf(this.elementData, var3); }

add(int n, E var2)方法

该方法是将元素var2插入到集合的第n个位置。

public void add(int var1, E var2) { //检查要插入的位置是否合法(插入的位置不能小于0且不能大于当前集合的容量) this.rangeCheckForAdd(var1); 判断如果新增一个元素,会不会超过ArrayList的容量,若超过,则扩容 this.ensureCapacityInternal(this.size + 1); //将要插入位置的之后的所以元素往后移动一个位置 System.arraycopy(this.elementData, var1, this.elementData, var1 + 1, this.size - var1); //将新的元素插入对应的位置 this.elementData[var1] = var2; ++this.size; }

remove(int var1)方法

删除var1位置的元素,集合中元素的位置是从0开始计算的,所以如果想删除第一个元素需用 remove(0)。

 public E remove(int var1) { //检查要移除的元素位置是否合法(var1不能小于0且不能大于集合容量的最大值) this.rangeCheck(var1); ++this.modCount; //获取要删除的元素 Object var2 = this.elementData(var1); int var3 = this.size - var1 - 1; if (var3 > 0) { //将要删除元素之后的所有元素往前移动一个位置 System.arraycopy(this.elementData, var1 + 1, this.elementData, var1, var3); } //将最后的元素设置为null并将size减1 this.elementData[--this.size] = null; //返回删除的元素 return var2; }

remove(Object var1)

根据元素删除,从该方法的源码中,我们可以看到,如果集合中存在多个var1元素,remove方法只会按顺序找到第一个var1元素并删除,并不会把所有的var1元素都删除。

public boolean remove(Object var1) { int var2; //当var1为null时,循环遍历集合中的元素,找到一个null的元素并删除,返回true if (var1 == null) { for (var2 = 0; var2 < this.size; ++var2) { if (this.elementData[var2] == null) { this.fastRemove(var2); return true; } } } else { //当var1不为null时,循环遍历集合中的元素,找到一个var1的元素并删除,返回true for (var2 = 0; var2 < this.size; ++var2) { if (var1.equals(this.elementData[var2])) { this.fastRemove(var2); return true; } } }​ return false; }

fastRemove(int var1)方法

删除第var1个位置的元素。

private void fastRemove(int var1) { ++this.modCount; //如果var1不是最后一位,则将var1之后的元素依次往前移动一位 int var2 = this.size - var1 - 1; if (var2 > 0) { System.arraycopy(this.elementData, var1 + 1, this.elementData, var1, var2); } //将集合中最后的元素设置为null this.elementData[--this.size] = null; }

System.arraycopy()

System中提供了一个native静态方法arraycopy(),可以使用这个方法来实现数组之间的复制。arraycopy的参数如下:

arraycopy(Object src,int srcPos,Object dest,int destPos,int length);src表示源数组srcPos表示源数组要复制的起始位置desc表示目标数组destPos表示目标数据要复制的其实位置length表示要复制的长度

更多文章请关注微信公众号:IT鸡窝

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值