ArrayList源码分析

ArrayList源码分析

前言

  1. 利用空参创建的集合,在底层创建一个默认长度为0的数组
  2. 添加第一个元素时,底层会创建以一个新的长度为10的数组
  3. 存满时,会扩容1.5倍数
  4. 如果一次添加多个元素,1.5倍还放不下,则新创建数组的长度以实际为准,数组长度是存入元素还要+10

今天源码深度分析:

源码剖析第一二点

ArrayList<String> list=new ArrayList<>();
list.add("aaa");

首先调用

public boolean add(E e)

方法,进入源码

public boolean add(E e) {
    modCount++;
    add(e, elementData, size);
    return true;
}

可以看到,

第一步modCount=1

protected transient int modCount = 0;

第二步,调用了add的重载方法

private void add(E e, Object[] elementData, int s) {
    if (s == elementData.length)
        elementData = grow();
    elementData[s] = e;
    size = s + 1;
}

然后重载方法中传递了三个参数,

第一个是“aaa”,理解为:要添加的元素

第二个是一个Object类型的数组elememData,可以看到源码发现他是一个空数组"",理解为:集合底层的数组的名字

第三个是size,集合大小size=0,理解为:集合的长度或者当前应存入元素的位置

transient Object[] elementData;

第二.1步,判断s和elementData的长度是否相等

明显是相等的,然后调用了grow()方法

private Object[] grow() {
    return grow(size + 1);
}

第二.2步:返回值是一个object类型的数组,grow(1)

第三步调用grow()的有参构造,传的值是1,minCapacity=1

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)];
    }
}

第三.1步

int oldCapacity = elementData.length;

用oldCapacity记录老容量,等于oldCapacity=0;

走入判断,明显都不满足,这个判断意思就是oldCapacity是不是大于零,并且做了一个非空判断,其实只需要看第一个不满足,整个判断为false,但是其实这两个都没满足,于是走了else语句

return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];

最后创建了一个对象使用Math.max获取最大值,源码中发现DEFAULT_CAPACITY=10,minCapacity等于传进来的1

private static final int DEFAULT_CAPACITY = 10;

在这里我们就发现了实际上就是创建了一个10的数组给了elementData并且做了一个返回

这里走完了我们就回到了add的重载方法

private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}

把elementData[0]="aaa";

size=1

直到这里add方法的第一次添加才算走完,这里就充分验证了

  1. 利用空参创建的集合,在底层创建一个默认长度为0的数组
  2. 添加第一个元素时,底层会创建以一个新的长度为10的数组

没有添加数据的时候,默认长度是空数组,就是长度为0

添加第一个就会创建以一个新的长度为10的数组

扩容机制

  • 接下来我们验证第三点,为什么存满时候会扩容1.5倍

这一次,我们假设现在正在添加第11个元素,假如第11个元素内容是"qqq",并且这里已经存满了10个元素

还是走进

第一步

public boolean add(E e) {
    modCount++;
    add(e, elementData, size);
    return true;
}

这是后modCount=10

还是继续调用

第二步

private void add(E e, Object[] elementData, int s) {
    if (s == elementData.length)
        elementData = grow();
    elementData[s] = e;
    size = s + 1;
}

第一个是“qqq”,理解为:要添加的元素

第二个是一个Object类型的数组elememData[10]

第三个是size,集合大小size=10,理解为:集合的长度或者当前应存入元素的位置

第二.1步

判断s和数组长度是否相等,明显是相等的

调用grow()

private Object[] grow() {
    return grow(size + 1);
}

第二.2步

继续调用grow有参构造并且作返回

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)];
    }
}

oldCapacity,老数组容量就是10,oldCapacity=10

第二.3步

接下来判断,明显第一个(老数组长度是否大于0)和第二个(非空判断)都满足,走进if语句

定义一个newCapacity,代表新容量

调用了ArraysSupport.newLength方法()传递了三个参数

第一个参数:oldCapacity,老数组的容量,返回值是10

第二个参数:minCapacity - oldCapacity,minCapacity=11减去oldCapacity=10,即返回值是1

第三个参数: oldCapacity >> 1 ,oldCapacity>>1,在二进制中进行右移,也就是把oldCapacity除于2,返回值是5

int newCapacity = ArraysSupport.newLength(oldCapacity,
        minCapacity - oldCapacity, /* minimum growth */
        oldCapacity >> 1           /* preferred growth */);
return elementData = Arrays.copyOf(elementData, newCapacity);
public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
    // preconditions not checked because of inlining
    // assert oldLength >= 0
    // assert minGrowth > 0

    int prefLength = oldLength + Math.max(minGrowth, prefGrowth); // might overflow
    if (0 < prefLength && prefLength <= SOFT_MAX_ARRAY_LENGTH) {
        return prefLength;
    } else {
        // put code cold in a separate method
        return hugeLength(oldLength, minGrowth);
    }
}

第三步

第三.1步

进入ArraysSupport.newLength()方法

int prefLength = oldLength + Math.max(minGrowth, prefGrowth); // might overflow

这里就是prefLength=10+Math.max(1,5)这里的minGrowth, prefGrowth,就是传进来的第二个和第三个参数古

最后等于15

第三.2步

进入判断

if (0 < prefLength && prefLength <= SOFT_MAX_ARRAY_LENGTH) {
    return prefLength;
} else {
    // put code cold in a separate method
    return hugeLength(oldLength, minGrowth);
}
public static final int SOFT_MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8;
@Native public static final int   MAX_VALUE = 0x7fffffff;

也就是Integer最大值减8

直接返回了prefLength=15

回到了grow()方法

新容量就是

newCapacity=15

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)];
    }
}

回到第7行

调用Arrays.copyOf()方法,传递的参数是(elementData[10],15)把原数组elementData10个数据拷贝到新elementData中,后面的数据补null,并且长度这里就已经是15了,并且把新数组作为一个返回

第四步

private void add(E e, Object[] elementData, int s) {
    if (s == elementData.length)
        elementData = grow();
    elementData[s] = e;
    size = s + 1;
}

继续执行第四行代码

把索引10传递进去,然后把"qqq"添加为11个元素

最后size+1

第五步

public boolean add(E e) {
    modCount++;
    add(e, elementData, size);
    return true;
}

回到add方法,把添加成功作为true进行返回

验证第四点

很简单,在上面验证第三点的时候我们翻到

第二.3步,假如我们传递的是第101个数据,里面已经又了100个元素

prefLength=10+Math.max(1,100),然后作为返回,数组长度就是110

这里就完全验证了第四点

posted @ 2022-11-09 20:26  喜欢七岁就很浪  阅读(35)  评论(0编辑  收藏  举报