JAVA——底层源码阅读——集合ArrayList的实现底层源码分析

Posted 叶不修233

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了JAVA——底层源码阅读——集合ArrayList的实现底层源码分析相关的知识,希望对你有一定的参考价值。

JAVA——底层源码阅读——集合ArrayList的实现底层源码分析

  • 当前 jdk版本:jdk1.8.0_74

一、提出问题

集合ArrayList的底层原理是什么,它的增删改查是怎么实现的,为什么它的长度是可变的?

二、查看ArrayList源码

步骤1、新建java文件,把代码敲上去如下

public class Test 

	public static void main(String[] args) 

		ArrayList arrayList = new ArrayList();
		arrayList.add("hello");
		arrayList.add("world");
		arrayList.remove(0);
		arrayList.set(0, "世界");
		arrayList.get(0);
		System.out.println(arrayList);	
	

步骤2、查看源码。

如何查看之前文章中已有详细步骤,不再赘述,以下直接展示具体源码。

1、增add()

2、删remove()

3、改set()

4、查get()

步骤3、阅读和分析源码。

1、增add()
如下所示,add()方法中一共有三行代码:
第一行是调用了ensureCapacityInternal方法;
第二行是往数组elementData的第size++位置下标增加值e;
第三行是返回一个true;

    public boolean add(E e) 
        ensureCapacityInternal(size + 1);  // Increments modCount!!
        elementData[size++] = e;
        return true;
    

由第二行可知,集合ArrayList的底层原理是依赖于数组Array的,接下来从第一行开始分析代码的实现过程。
(1)点击跳转到ensureCapacityInternal()方法中,如图所示:

(2)点击跳转查看if判断条件里的数组elementData和常量DEFAULTCAPACITY_EMPTY_ELEMENTDATA如图所示:
可知,这个条件的意思是:如果数组elementData为空,就执行下面一行代码


(3)点击跳转查看if判断中的执行代码minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);如图所示:
意为,调用Math.max()方法,取常量DEFAULT_CAPACITY和变量minCapacity中的最大值赋给参数minCapacity。
跳转查看常量DEFAULT_CAPACITY的值为10。

(4)回到ensureCapacityInternal()方法,查看if判断之外的这行代码,即,无论有没有进入if判断,这行代码都将被执行。这行代码的意思的,把上面的minCapacity作为入参传递过来,调用 ensureExplicitCapacity(minCapacity)方法。

(5)点击跳转查看 ensureExplicitCapacity(minCapacity)方法,如图所示:
意为:如果传入参数minCapacity>数组elementData的长度,就把minCapacity参数继续传递下去,调用grow(minCapacity)方法

(6)点击跳转查看grow(minCapacity)方法,如图所示:
意为:复制一个elementData数组,新数组长度=旧数组长度*1.5
至此,我们可以发现集合ArrayList长度可变的核心代码实现:基于数组的copyOf方法

(7)总结:
集合ArrayList的add()方法:本质上是依赖于数组Array。
在创建集合时,会定义一个初始长度为10的数组,在10之内的add()方法调用数组的给指定下标元素赋值方法;
当集合元素继续增加,到长度超过10时,会调用数组的copyOf方法对数组进行1.5倍的长度扩容。

2、删remove()
删除方法比较简单,大致与数组操作一致,如图,不再分步骤分析

3、改set()

4、查get()

三、尝试自定义ArrayList

新建一个java文件,任意命名,模仿以上分析的ArrayList实现步骤去重写一个简略版的ArrayList
因为上面已经分析过核心代码的作用,不再重复按点赘述。
整个类的代码及注释如下:

代码

import java.util.Arrays;

/**
 * @description: ArrayList底层源码分析
 * @author: z
 * @date: 2022年4月26日
 */

public class YebuxiuArrayList
	
	//声明一个类型为Object的数组
	Object[] elementData;
	
	//定义一个私有的常量,类型为Object[],长度为0的空数组
	private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = ;
	
	//声明一个类型为int的变量size,用于记录数组中元素个数,初始值未赋值则默认为0
	private int size;
	
	//声明一个类型为int的常量,用来记录数组的默认长度,初始值赋值为10
	private static final int DEFAULT_CAPACITY = 10;
	
	/**
	 * 构造方法-ArrayList的创建
	 */
	public YebuxiuArrayList() 
		//给声明的数组赋值,长度为0
		this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
	
	
	/**
	 * add()方法-向ArrayList中插入数据
	 */
	public boolean add(Object obj) 
		//调用这个方法
		ensureCapacityInternal(size + 1);
		//往数组中下标为size的位置插入元素obj,然后给size+1
		elementData[size++] = obj;
		return true;
	
	
	/**
	 * ArrayList的长度初始化,初始化长度默认为10
	 */
	public void ensureCapacityInternal(int minCapacity) 
		//判断:如果数组为空,就给minCapacity赋值为10
		if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) 
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        
		//调用这个方法
        ensureExplicitCapacity(minCapacity);
	
	
	/**
	 * ArrayList的长度发生变化时,调用grow()方法
	 */
	private void ensureExplicitCapacity(int minCapacity) 
            grow(minCapacity);
    
	
	/**
	 * 如果ArrayList变化后的长度小于10,长度就不变
	 * 如果ArrayList变化后的长度大于10,就按1.5倍对ArrayList的长度进行扩容
	 */
	private void grow(int minCapacity) 
		//定义变量记录当前数组长度
        int oldCapacity = elementData.length;
        //定义变量记录新数组长度
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
//        if (newCapacity - MAX_ARRAY_SIZE > 0)//MAX_ARRAY_SIZE=2147483639 [0x7ffffff7]
//            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        //使用数组的copyOf方法,复制一个新数组,名字不变,长度为新数组长度
        elementData = Arrays.copyOf(elementData, newCapacity);
    
	
	/**
	 * 获取对应下标位置的元素
	 */
	public  Object get(int index) 
        return elementData(index);
    
	
	public Object elementData(int index) 
        return  elementData[index];
    
	
	/**
	 * 移除指定下标的元素
	 * 并让该下标之后的元素下标往前进一位
	 */
	public Object remove(int index) 
        rangeCheck(index);
        Object oldValue = elementData(index);

        int numMoved = size - index - 1;
        if (numMoved > 0)
            System.arraycopy(elementData, index+1, elementData, index,
                             numMoved);
        elementData[--size] = null; // clear to let GC do its work
        return oldValue;
	 
	 
	/**
	 * 检查输入下标是否合法,如果下标超出ArrayList的长度
	 * 就抛出定义好的异常
	 */
	 private void rangeCheck(int index) 
	        if (index >= size)
	            throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
	 
	 
	 /**
	  * 定义的异常返回值
	  */
	 private String outOfBoundsMsg(int index) 
	        return "Index: "+index+", Size: "+size;
     
	 
	 /**
	  * 将指定下标位置设置为指定元素(增和改语法一致)
	  */
	 public Object set(int index, Object element) 
	        rangeCheck(index);

	        Object oldValue = elementData(index);
	        elementData[index] = element;
	        return oldValue;
     

	 /**
	  * 重写toString()方法
	  * 直接输出ArrayList,输入里面的元素,而不是内存地址
	  */
	 @Override
	 public String toString() 
		 StringBuilder sb = new StringBuilder();
		 sb.append("[");
		 for(int i = 0;i<elementData.length;i++) 
			 if(elementData[i]!=null) 
			 	sb.append(elementData[i]).append(",");
			 
		 
		 String s = sb.substring(0, sb.length()-1);
		 s = s +"]";
		 return s;
	 
	 

运行结果

测试使用自定义的YebuxiuArrayList创建对象和实现增删改查如图所示,均没有问题

以上是关于JAVA——底层源码阅读——集合ArrayList的实现底层源码分析的主要内容,如果未能解决你的问题,请参考以下文章

JAVA集合之ArrayList源码分析

java集合系列之ArrayList源码分析

浅谈Java集合(底层源码解析)

Java集合之ArrayList源码解析

浅谈Java集合丨底层源码解析

jdk源码阅读笔记之java集合框架(ArrayList)