第一章第一篇、深入ArrayList源码分析

ArrayList简介

  1. 概念

    ArrayList是一个容量能够动态增长的动态数组。和java的数组相比,它的容量能够动态增长。它继承了AbstractList,实现了List、RandomAccess、Cloneable、java.io.Serializable

  2. 继承关系
    首先,我们先查看下ArrayList在java集合体中所在的位置:
    在这里插入图片描述
    在这里我们可以看到ArrayList的根部是Collection,下面我们看分析下ArrayList的继承关系在这里插入图片描述
    上图清晰的描述到,实现四个接口继承一个抽象类:

  • ArrayList继承AbstractList;实现List。它是一个数组队列,提供了新增、修改、删除和遍历等功能。
  • ArrayList实现了RandomAccess,即提供了随机访问功能。
  • ArrayList实现了Cloneable,即覆盖了函数clone(),能够被克隆。
  • ArrayList实现了java.io.Serializable,这表明ArrayList可以被序列化,能通过序列化去传输。
  • 和Vector不同,ArrayList操作的是线程不安全的,建议在单线程的情况下使用,如果是多线程的情况下建议使用Vector和CopyOnWriteArrayList
  • 从ArrayList源码分析

1.ArrayList属性
ArrayList属性主要有当前数组长度size、存放数组对象elementData数组和代表ArrayList修改次数从AbstractList继承的modCount属性。

// 序列化id
private static final long serialVersionUID = 8683452581122892189L;
// 默认初始化容量大小
private static final int DEFAULT_CAPACITY = 10;
// 用于空实例时共享空数组实例
private static final Object[] EMPTY_ELEMENTDATA = {};
// 一个空对象,如果使用默认构造函数创建,则默认对象内容默认是该值
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
// 当前数据对象存放地方
transient Object[] elementData; // non-private to simplify nested class access
// 当前数组的长度
private int size;

2.ArrayList的构造方法
ArrayList构造方法有三种,分别是默认不传参、初始化容量的空数组和包含元素的数组。

  • 无参构造函数,初始容量为10的空数组,如下:
// 第一种,调用ArrayList(10),其中初始化的一个长度为10的object数组
public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }
  • 初始化大小的构造函数,如下:
public ArrayList(int initialCapacity) {
    // 如果初始化大小小于0会报异常,否则新建初始化为object的数组
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }
  • 带collection对象的构造方法,
    1)先将集合转换成数组,然后赋值给elementData;
    2)将数组长度赋值给size并判断是否为0,
    如下:
public ArrayList(Collection<? extends E> c) {
        elementData = c.toArray();
        if ((size = elementData.length) != 0) {
            // c.toArray might (incorrectly) not return Object[] (see 6260652)
            if (elementData.getClass() != Object[].class)
                elementData = Arrays.copyOf(elementData, size, Object[].class);
        } else {
            // replace with empty array.
            this.elementData = EMPTY_ELEMENTDATA;
        }
    }

1.0、这边谈一下ArrayList(Collection<? extends E> c)是深拷贝还是浅拷贝,测试代码如下:

/** 测试ArrayList(Collection<? extends E> c)是浅拷贝还是深拷贝 **/
		List<BeanDemo> beanDemos = new ArrayList<>();
        BeanDemo beanDemo = new BeanDemo();
        beanDemo.setName("test1");
        beanDemos.add(beanDemo);
        List<BeanDemo> beanDemoList = new ArrayList<>(beanDemos);
        beanDemo.setName("test2");
        System.out.println(beanDemoList.get(0).getName());
        //结果输出test2

1.2、深入研究一下源码,为什么ArrayList(Collection<? extends E> c)构造函数是浅拷贝,其中调用了Arrays.copyof()方法,传参是原始数组和拷贝数组的长度,copyof()通过获取一个原始数组的副本,被截断或用null填充以返回指定的长度。源码如下,到这一步还看不出是到底是浅拷贝还是深拷贝:

public static <T,U> T[] copyOf(U[] original, int newLength, Class<? extends T[]> newType) {
        @SuppressWarnings("unchecked")
        T[] copy = ((Object)newType == (Object)Object[].class)
            ? (T[]) new Object[newLength]
            : (T[]) Array.newInstance(newType.getComponentType(), newLength);
        System.arraycopy(original, 0, copy, 0,
                         Math.min(original.length, newLength));
        return copy;
    }

1.3、copyof()方法中主要使用System.arraycopy()方法来实现列表拷贝,再往下查看System.arraycopy(),发现这个是一个原生native的函数(native方法不是用java实现的),我只能仔细阅读一下这个原生方法的注释,参见文章最下方,注释内容比较长,但第一段注释直接告诉了我们arraycopy()方法拷贝的是数组的引用地址,所以它属于浅拷贝:A subsequence of array components are copied from the source array referenced by src to the destination array referenced by dest.

public static native void arraycopy(Object src,  int  srcPos,
                                        Object dest, int destPos,
                                        int length);

2.0、为何要判断elementData.getClass() != Object[].class,当我们查看c.toArray()源码的时候肯定认为返回的就是Object[]数组,为什么还需要判断呢?在查阅了各种资料之后才知道Arrays含有一个asList方法,Arrays.asList
体现的是适配器模式,只是转换接口,后台的数据仍是数组。

用代码来证实下:

		List<String> llll = Arrays.asList("zhen", "xiang");
		System.out.println(llll.toArray().getClass());
		//返回结果:class [Ljava.lang.String;

到这ArrayList集合的三种构造方法就讲完了。下一篇会继续讲解它的其他方法~~~
借鉴资料:
1.源码分析:
https://blog.csdn.net/augfun/article/details/82323164
https://baijiahao.baidu.com/sid=1637926321175819771&wfr=spider&for=pc
2.判断toArray()方法是否返回Object[]:
https://blog.csdn.net/weixin_43390562/article/details/101236699
3.判断ArrayList(Collection<? extends E> c)是深拷贝还是浅拷贝:
https://blog.csdn.net/king0406/article/details/103752855

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是源码分析ArrayList集合通过空参构造方法创建对象,并第一次添加元素的过程: 1. 首先,通过空参构造方法创建ArrayList对象,代码如下: ``` ArrayList<Object> arrayList = new ArrayList<>(); ``` 2. 在ArrayList类中,空参构造方法的实现如下: ``` public ArrayList() { // 默认初始容量为10 this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; } ``` 其中,DEFAULTCAPACITY_EMPTY_ELEMENTDATA是一个空数组,定义如下: ``` private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {}; ``` 3. 接着,当第一次向ArrayList添加元素时,会调用ensureCapacityInternal()方法来确保容量足够存放新的元素。在这个过程中,如果当前容量不足,会自动进行扩容操作。代码如下: ``` arrayList.add("hello"); ``` 4. 在add()方法中,会先调用ensureCapacityInternal()方法进行容量检查和扩容操作,代码如下: ``` private void ensureCapacityInternal(int minCapacity) { if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) { minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); } ensureExplicitCapacity(minCapacity); } private void ensureExplicitCapacity(int minCapacity) { modCount++; // 当前容量小于所需最小容量时,进行扩容 if (minCapacity - elementData.length > 0) grow(minCapacity); } private void grow(int minCapacity) { // 计算新容量,为原容量的1.5倍 int oldCapacity = elementData.length; int newCapacity = oldCapacity + (oldCapacity >> 1); if (newCapacity - minCapacity < 0) newCapacity = minCapacity; // 如果新容量超过了最大容量,则将新容量设置为Integer.MAX_VALUE if (newCapacity - MAX_ARRAY_SIZE > 0) newCapacity = hugeCapacity(minCapacity); // 进行数组复制,扩容 elementData = Arrays.copyOf(elementData, newCapacity); } ``` 5. 在ensureCapacityInternal()方法中,会先判断当前容量是否为默认初始容量,如果是,则将所需最小容量和默认容量进行比较,取较大值作为新容量。如果当前容量已经大于默认初始容量,则直接调用ensureExplicitCapacity()方法进行容量检查和扩容操作。 6. 在ensureExplicitCapacity()方法中,会先对modCount进行自增操作,然后判断当前容量是否小于所需最小容量,如果小于,则调用grow()方法进行扩容操作。 7. 在grow()方法中,会先计算出新容量,然后判断新容量是否超过了最大容量,如果超过,则将新容量设置为Integer.MAX_VALUE。最后,调用Arrays.copyOf()方法进行数组复制,完成扩容操作。 综上所述,ArrayList集合通过空参构造方法创建对象,并第一次添加元素的过程,主要就是进行容量检查和扩容操作。如果当前容量不足,就需要进行扩容操作,扩容后再添加元素。这个过程中,ArrayList会自动进行容量的调整,保证存储的元素数量不会超过当前容量。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值