ArrayList源码学习笔记(1)

背景

之前关注了一个公众号“彤哥读源码”,跟着一起学习了jdk里的很多类的源码,包括集合类、线程类、并发类。真的是很好的公众号,哈哈(因为确实是很好,这里愿意给路过的小伙伴安利一下,纯自己主观推荐,如果对公众号作者有帮助,算是感谢这么好的文章对我的帮助了~)

其中,ArrayList是最简单的一个了,但是读完别人写的东西其实还是不如自己实践来的实在。所以自己再搞点东西。

文章链接(侵删):https://mp.weixin.qq.com/s/a0zq-q8JuSwsLYX7tJ4VxA

 

计划

准备按照一个路径走,边看文章,边看源码,然后自己手动做些调试。顺便写点文章,记录一下学习和调试过程。

之后,自己手动实现一个ArrayList,可以根据情况进行多版本迭代。

 

正文

成员变量很简单,文章中也有介绍。ArrayList的元素存储在Object[] elementData数组中,size是当前保存了多少个元素。

还有两个静态final的空数组EMPTY_ELEMENTDATA和DEFAULTCAPACITY_EMPTY_ELEMENTDATA,看到这个就会产生疑惑,为什么会需要两个静态空数组呢?都是final的,反正不可变,用一个不就可以了?这其实跟构造函数有关系。

构造函数

ArrayList有三个构造方法,ArrayList()、ArrayList(int initialCapacity)、ArrayList(Collection c)。

ArrayList()会默认使用DEFAULTCAPACITY_EMPTY_ELEMENTDATA作为elementData的初始值;

ArrayList(int initialCapacity),如果initialCapacity > 0 就直接用这个值初始化数组长度了,等于0就直接用EMPTY_ELEMENTDATA。

ArrayList(Collection c) 如果c里没有元素,默认也是用EMPTY_ELEMENTDATA。

(上面说的很啰嗦,看代码一目了然)

增长逻辑

此时重点来了,如果用ArrayList()和ArrayList(0)实例化了两个对象,它们在增加第一个变量的时候,elementData会怎么进行扩容?

这里的判断告诉我们它是怎么做的。如果不手动指定初始容量,在第一次就会把容量设置为10。如果手动指定的初始容量是0,那么第一次就会把容量设置为你需要的那个值(调用add方法的时候,其实就是1)。

一开始我产生了额外的疑问:为什么代码写的这么啰嗦?minCapacity不就是1吗,还能大于10?后来看了一下,确实能大于10,看一下addAll(Collection c)这个方法就明白了。如果一下子加入很多元素,也是会调用这个方法的。

那么这个逻辑我们怎么验证一下呢?elementData是default的,自己写代码也没权限获取。这时候就是祭出“反射”大法的时候了,毕竟java里“万物皆可反射”😏

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

public class ArrayListTest {
    public static void main(String[] args) throws NoSuchFieldException, IllegalAccessException {
        // 通过使用ArrayList()和ArrayList(0)来进行对比
        ArrayList<Integer> list = new ArrayList<>(0);
        Field field = list.getClass().getDeclaredField("elementData");
        field.setAccessible(true);
        Object []ele = (Object[]) field.get(list);
        System.out.println(ele.length);

        Field field1 = list.getClass().getDeclaredField("EMPTY_ELEMENTDATA");
        field1.setAccessible(true);
        Object []ele1 = (Object[]) field1.get(null);
        // 这里能发现ele和ele1是同一个对象
        System.out.println(ele);
        System.out.println(ele1);
        System.out.println(ele1 == ele);

        // 逐渐增加元素,会发现容量每次增长一半
        list.add(1);
        list.add(1);
        list.add(1);
        list.add(1);
        list.add(1);
        
        System.out.println(list.size());
        // 这里不能打印ele.length,想想为啥~(一开始就打错了。。。)
        // 即便一开始的容量是大于0的,比如20,当元素增长超过20的时候,也不能用ele了,因为复制产生了新数组
        System.out.println(((Object[]) field.get(list)).length);
    }
}

总结:

(1)如果不指定容量,新加入元素的时候容量最小是10;如果指定了容量为0,新加入元素的时候,容量是加入元素的个数;

(2)之后的容量增长都是每次增长一半。10->15->22->33  或者 0->1->2->3->4->6->9

(3)万物皆可反射~

 

 

 

 

 

 

 

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值