ArrayList 源码分析

本文详细解析了Java中的ArrayList,包括其内部结构、构造方法、add方法的实现原理,以及扩容机制。同时,分析了两种remove方法的区别。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

ArrayList 分析

本人是基于Java8分析的。其实我认为ArrayList分为两步分析,第一步是分析它的构造方法,它的构造方法其实挺有意思的,一会慢慢讲,第二步就是分析它的Add方法,当然了,还有其他方法也会看看的,但是主要的还是Add方法

ArrayList 底层使用的是动态数组,是连续的内存空间,对于数据的查询比较快。对于插入和删除需要移动内存,所以比较慢。它是线程不安全的。

属性

	// 默认容量
 	private static final int DEFAULT_CAPACITY = 10;
	// 空数组
    private static final Object[] EMPTY_ELEMENTDATA = {};
	// 默认空数组
    private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
	// 存放元素的数组,从这可以发现ArrayList的底层实现就是一个Object数组
    transient Object[] elementData; // non-private to simplify nested class access
	// 获取size,代表的是list中真实存在的元素个数
    private int size;

强调一下,这里的size代表的是list中真正存在的元素的个数,接下来分析源码

一、构造方法

ArrayList 一共有三个构造方法

  1. 第一个构造方法(无参构造)
public ArrayList() {
        this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
    }

上面的这个初始化方法就是我们使用如下方法构造出来的

        List<Integer> integers = new ArrayList<>();
  1. 第二个构造方法(有参构造)
public ArrayList(int initialCapacity) {
		// 判断参数是否大于0,如果大于0,将数组的大小设置为指定大小;
		// 如果等于0,设置一个空数组;
		// 如果小于0,报错。
        if (initialCapacity > 0) {
            this.elementData = new Object[initialCapacity];
        } else if (initialCapacity == 0) {
            this.elementData = EMPTY_ELEMENTDATA;
        } else {
            throw new IllegalArgumentException("Illegal Capacity: "+
                                               initialCapacity);
        }
    }

看样子使用这种方法构造ArrayList比第一种复杂啊,其实他这里也没干啥。。内容比较简单,就不分析了。

使用方式:

List<Integer> integers = new ArrayList<>(10);
  1. 第三个构造方法(有参构造)
public ArrayList(Collection<? extends E> c) {
		// 将List转变为数组;
		// 判断size是否大于0,如果不等于0,直接copy;
		// 否则创建一个空数组
        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;
        }
    }

使用方式:

		List<Integer> integers1 = null;
        List<Integer> integers = new ArrayList<>(integers1);

不知道大家在上面的源码中,有没有看到这样的一句话,
c.toArray might (incorrectly) not return Object[] (see 6260652)
就是这句话,后来查阅之后,发现是这个官方的一个bug,具体我们就不讨论了。
以上就是ArrayList的三个构造方法。

接下来我们继续分析它的add方法

下面这就是ArrayList的add方法,是不是很简单,哈哈。

// 首先他调用了另一个方法
 public boolean add(E e) {
 		// 强调一下,这里的size代表的是list中真正存在的元素的个数
        ensureCapacityInternal(size + 1);  
        elementData[size++] = e;
        return true;
    }

下面这个方法就是add方法中的第一行代码,分析一下:
首先判断用户的数组是不是空的,如果是空的,进行比较,选择大的(注意,我这里说的空是指Object[0])
如果不满足条件,进入下面的方法。

private void ensureCapacityInternal(int minCapacity) {
        if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
            minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
        }

        ensureExplicitCapacity(minCapacity);
    }

方法中的第一行是一个变量,该字段表示list结构上被修改的次数;
接下来,如果添加元素后的大小大于添加元素前的大小,这进行扩容。

private void ensureExplicitCapacity(int minCapacity) {
        modCount++;

        // overflow-conscious code
        if (minCapacity - elementData.length > 0)
            grow(minCapacity);
    }
重点来了,嘻嘻

下面这个方法就是ArrayList进行扩容的核心方法
首先,获取list的元素个数,进行1.5倍扩容,
接下来如果扩容后的大小小于添加元素后的大小,将添加元素后的大小赋值给扩容后的大小,
如果,扩容后的大小大于java 定义数组的最大数,进入hugeCapacity方法,
确定大小后,进行copy,将旧的元素赋值给新的数组,并设置数组容量。
此时,注意:这只是进行了扩容并将旧数据添加到了新的数组里面,别忘了add方法中还有一行代码。那一行才是想list中添加数据,到此分析结束

private void grow(int minCapacity) {
        // overflow-conscious code
        int oldCapacity = elementData.length;
        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);
    }

这个方法就是进行判断,然后一下逻辑,大家自行分析

private static int hugeCapacity(int minCapacity) {
        if (minCapacity < 0) // overflow
            throw new OutOfMemoryError();
        return (minCapacity > MAX_ARRAY_SIZE) ?
            Integer.MAX_VALUE :
            MAX_ARRAY_SIZE;
    }
其他的方法

如果你仔细看过ArrayList源码会发现,他有两个remove方法

第一个方法(根据元素下标删除元素)
public E remove(int index) {
        rangeCheck(index);

        modCount++;
        E 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;
    }
第二个方法(根据元素删除元素)
public boolean remove(Object o) {
        if (o == null) {
            for (int index = 0; index < size; index++)
                if (elementData[index] == null) {
                    fastRemove(index);
                    return true;
                }
        } else {
            for (int index = 0; index < size; index++)
                if (o.equals(elementData[index])) {
                    fastRemove(index);
                    return true;
                }
        }
        return false;
    }
/*
     * Private remove method that skips bounds checking and does not
     * return the value removed.
     */
private void fastRemove(int index) {
        modCount++;
        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
    }

大家都看到这两个方法了吧,其实仔细一看没什么区别,最后还是看官方文档发现,为什么他叫fastRemove,上面的注释的意思大致是:私有删除方法,跳过边界检查并且不返回删除的值。由此看来,是跳过了边界检查。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值