ArrayList底层原理详解

前言:

  • 这里我用的JDK8

  • 在讲ArrayList之前,先讲简单讲下List

  • List集合代表一个有序集合,集合中每个元素都有其对应的顺序索引,它继承Collection接口,可以定义一个允许重复的有序集合

  • List接口的特点:
    1、有下标
    2、有顺序
    3、能重复

  • 实现List接口的集合有:

    • ArrayList、LinkedList、Vector、Stack
  • 在这篇文章,只详细讲ArrayList底层

ArrayList

  • 实现了List接口,也是最常用的一种集合
  • 特点:底层数据结构是数组,查询快,增删除慢,线程不安全,效率高
  • 优点:操作读取操作效率高,底层是基于数组实现的,可以为null值,可以允许重复元素,有序,异步
  • 缺点:由于它是底层是动态数组实现的,不适合频繁的对元素的进行插入和删除操作,因为每次插入和删除都需 要移动数组中的元素,操作慢且复杂。

底层原理

我们以下的代码进行讲解,且从debug模式进入底层

import java.util.ArrayList;

public class ArrayListDemo {
    public static void main(String[] args) {
        ArrayList<Integer> arryayList = new ArrayList<Integer>();
        arryayList.add(1);
        arryayList.add(2);
        arryayList.add(3);
        arryayList.add(4);
        arryayList.add(5);
        arryayList.add(6);
        arryayList.add(7);
        arryayList.add(8);
        arryayList.add(9);
        arryayList.add(10);
        arryayList.add(11);
    }
}

我们在main方法里面的全部代码都打上断点
在这里插入图片描述进入debug模式,按F7,再按F8,再按F7,就可以跳过类加载器,进入ArrayList类中

  • ArrayList中的构造方法

 public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
  1. elementData是一个Object空数组,
  2. DEFAULTCAPACITY_EMPTY_ELEMENTDATA:是一个空常量数组
1.transient Object[] elementData;
#
2.private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};

第一个结论
Object[] elementData:就是ArrayList存储数据的地方,且是空数组

ArrayList存储数据的地方其实是一个Object数组,并且一开始是{}(空数组)
     public ArrayList() {
         this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

接着我们将debug进入add底层,首先会进入包装方法,这个方法与底层无关,跳过,所以我们按F8跳出,再按F7进入
在这里插入图片描述跳到这里add方法

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

size刚开始是等于0 : size=0,

  • 我们再进入ensureCapacityInternal方法
    这里的minCapacity就是size+1
    minCapacity = 1
private void ensureCapacityInternal(int minCapacity) {//minCapacity=1
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
    
  • 进入calculateCapacity(elementData, minCapacity)
    elementData:就是Object数组,这里是空数组
    minCapacity = 1
private static int calculateCapacity(Object[] elementData, int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            return Math.max(DEFAULT_CAPACITY, minCapacity);
        }
        return minCapacity;
    }

在ArrayList的构造方法中就是
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
所以两者是相等的,会执行

//DEFAULT_CAPACITY=10
//minCapacity = 1
return Math.max(DEFAULT_CAPACITY, minCapacity);//返回DEFAULT_CAPACITY=10

而DEFAULT_CAPACITY =10

 private static final int DEFAULT_CAPACITY = 10;

所以执行的代码如下

return Math.max(10,1);//返回10

那么10就会返回到这里

private void ensureCapacityInternal(int minCapacity) {//minCapacity=1
		//相等于ensureExplicitCapacit(10)
        ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
    }
  • 进入ensureCapacityInternal方法中
  1. 此时的minCapacity=10
    在这里插入图片描述
private void ensureExplicitCapacity(int minCapacity) {
        modCount++;//

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }

minCapacity=10,
elementData.length = 0
那么必然进行grow(minCapacity)方法中
grow()就是扩容方法

  • 进入grow()方法中
 private void grow(int minCapacity) {//minCapacity=10
        // overflow-conscious code
        int oldCapacity = elementData.length;//oldCapacity是旧的容量为0
        //newCapacity :新容量
        //oldCapacity >> 1:右移一位,位运算,相当于oldCapacity/2
        //newCapacity=旧容量+旧容量的0.5倍 = 1.5倍,所以ArrayList每次扩容1.5倍
        //但在这里newCapacity=0,因为oldCapacity=0
        int newCapacity = oldCapacity + (oldCapacity >> 1);
        if (newCapacity - minCapacity < 0)
            newCapacity = minCapacity;
        if (newCapacity - MAX_ARRAY_SIZE > 0)
            newCapacity = hugeCapacity(minCapacity);
        // minCapacity is usually close to size, so this is a win:
        elementData = Arrays.copyOf(elementData, newCapacity);
    }
  1. int oldCapacity = elementData.length;//oldCapacity是旧的容量为0

  2. newCapacity :新容量

  3. oldCapacity >> 1:右移一位,位运算,相当于oldCapacity / 2

  4. **newCapacity=旧容量+旧容量的0.5倍 = 1.5倍,所以ArrayList每次扩容1.5倍**

  5. int newCapacity = oldCapacity + (oldCapacity >> 1);
    newCapacity = 0 +(0 >> 1)=0
    符合if (newCapacity - minCapacity < 0)判断语句
    而minCapacity = 10,那么得到newCapacity = minCapacity = 10;
    第二次总结
    ArrayList每次扩容原来的1.5倍
    在这里插入图片描述

  • 接着进入copyOf方法,
//original=elementData:就是旧数组
//newLength = newCapacity = 10 //新数组
 public static <T> T[] copyOf(T[] original, int newLength) {
        return (T[]) copyOf(original, newLength, original.getClass());
    }
  • 里面还有个copyOf,接着进入

在这里插入图片描述最重要的数组复制方法

//将原来的数组,从0开始,复制到copy这个数组中,也是从0开始,复制原来数组的长度
System.arraycopy(original, 0, copy, 0,Math.min(original.length, newLength));

将原来的数组,从0开始,复制到copy这个数组中,也是从0开始,复制原来数组的长度
其实就是把原来数组的数据复制到新数组中

第三次总结:
在grow()方法中的Arrays.copyOf()才是做真正的扩容

Arrays.copyOf(elementData,newCapacity(minCapacity))
   这个方法在做数组的扩容,底层其实就是System.arrayCopy()

然后返回copy这个新数组,赋值给旧数组,这样就完成扩容

 elementData = Arrays.copyOf(elementData, newCapacity);//elementData  = copy

完成扩容后回来原来的add方法

public boolean add(E e) {
        ensureCapacityInternal(size + 1);  // 完成扩容
        //elementData:就是完成扩容后的数组,size++,
        //elementData[1] = e,完成添加操作
        elementData[size++] = e;
        return true;
    }

出处:https://blog.csdn.net/xiannbi
本文版权归作者所有,欢迎转载,但未经作者同意必须保留此段声明,且在文章页面明显位置给出原文连接,否则保留追究法律责任的权利。

发布了3 篇原创文章 · 获赞 0 · 访问量 94

猜你喜欢

转载自blog.csdn.net/xiannbi/article/details/104551552