ArrayList详细介绍

本文详细介绍了ArrayList的内部实现,包括其成员变量、构造函数、添加、删除和获取元素等主要操作。ArrayList基于数组实现,初始容量为10,添加元素时会动态调整容量。删除操作通过System.arraycopy()进行元素重新排列。get操作直接返回数组对应位置的元素,实现快速访问。

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

简介

ArrayList 是 java 集合框架中比较常用的数据结构了,是我们常常能用到的集合。ArrayList继承自 AbstractList,实现了 List 接口。底层基于数组实现容量大小动态变化。允许 null 的存在。同时还实现了 RandomAccess、Cloneable、Serializable 接口,所以ArrayList 是支持快速访问、复制、序列化的。

成员变量

ArrayList 底层是基于数组来实现容量大小动态变化的。

/**
* The size of the ArrayList (the number of elements it contains).
*/
private int size;  // 实际元素个数
transient Object[] elementData; 
注意:上面的 size 是指 elementData 中实际有多少个元素,而 elementData.length 为集合容量,表示最多可以容纳多少个元素。

默认初始容量大小为 10;

/**
* Default initial capacity.
*/
private static final int DEFAULT_CAPACITY = 10;

这个变量是定义在 AbstractList 中的。记录对 List 操作的次数。主要使用是在 Iterator,是防止在迭代的过程中集合被修改。

protected transient int modCount = 0;

ArrayList构造函数

构造函数

无惨构造函数
/**
* Constructs an empty list with an initial capacity of ten.
*/
public ArrayList() {
    this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}

注意:注释是说构造一个容量大小为 10 的空的 list 集合,但构造函数了只是给 elementData 赋值了一个空的数组,其实是在第一次添加元素时容量扩大至 10 的。

构造一个初始容量大小为 initialCapacity 的 ArrayList

public ArrayList(int initialCapacity) {
    if (initialCapacity > 0) {
        this.elementData = new Object[initialCapacity];
    } else if (initialCapacity == 0) {
        this.elementData = EMPTY_ELEMENTDATA;
    } else {
        throw new IllegalArgumentException("Illegal Capacity: "+
                                       initialCapacity);
    }
}

由以上源码可见: 当使用无参构造函数时是把 DEFAULTCAPACITY_EMPTY_ELEMENTDATA 赋值给 elementData。 当 initialCapacity 为零时则是把 EMPTY_ELEMENTDATA 赋值给 elementData。 当 initialCapacity 大于零时初始化一个大小为 initialCapacity 的 object 数组并赋值给 elementData。

使用指定 Collection 来构造 ArrayList 的构造函数

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

将 Collection 转化为数组并赋值给 elementData,把 elementData 中元素的个数赋值给 size。 如果 size 不为零,则判断 elementData 的 class 类型是否为 Object[],不是的话则做一次转换。 如果 size 为零,则把 EMPTY_ELEMENTDATA 赋值给 elementData,相当于new ArrayList(0)。

主要操作方法解析

添加操作

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

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

private void ensureExplicitCapacity(int minCapacity) {
	modCount++;
	// overflow-conscious code
	if (minCapacity - elementData.length > 0)
		grow(minCapacity);
}

由此可见:每次添加元素到集合中时都会先确认下集合容量大小。然后将大小自增1。确保CapacityInternal函数中判断如果elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA就取DEFAULT_CAPACITY和minCapacity的值也就是10。这就是EMPTY_ELEMENTDATA与DEFAULTCAPACITY_EMPTY_ELEMENTDATA的区别所在。同时也验证了上面的说法:使用无惨的构造函数时是在第一次添加元素时初始化容量为10的。确保ExplicitCapacity中对modCount自增1,记录操作次数,然后如果minCapacity大于第一次添加元素时elementData的长度为零。那我们来看看生长函数。

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

很简单明了的一个函数,将会将扩展容至原容量的1.5倍。但是扩容之后也不一定适用,有可能太小,有可能太大。所以才会有以下两个if判断。如果1.5倍太小的话,则将我们所需的容量大小赋值给newCapacity,如果1.5倍太大或者我们需要的容量太大,直接那就拿newCapacity = (minCapacity > MAX_ARRAY_SIZE) ? Integer.MAX_VALUE : MAX_ARRAY_SIZE来扩容。然后将原数组中的数据复制到大小为newCapacity的新数组中,将新副本赋值给elementData。

public void add(int index, E element) {
	rangeCheckForAdd(index);
	ensureCapacityInternal(size + 1);  // Increments modCount!!
	System.arraycopy(elementData, index, elementData, index + 1, size - index);
	elementData[index] = element;
	size++;
}

public boolean addAll(Collection<? extends E> c) {
	Object[] a = c.toArray();
	int numNew = a.length;
	ensureCapacityInternal(size + numNew);  // Increments modCount
	System.arraycopy(a, 0, elementData, size, numNew);
	size += numNew;
	return numNew != 0;
}

public boolean addAll(int index, Collection<? extends E> c) {
	rangeCheckForAdd(index);

	Object[] a = c.toArray();
	int numNew = a.length;
	ensureCapacityInternal(size + numNew);  // Increments modCount

	int numMoved = size - index;
	if (numMoved > 0)
		System.arraycopy(elementData, index, elementData, index + numNew, numMoved);

	System.arraycopy(a, 0, elementData, index, numNew);
	size += numNew;
	return numNew != 0;
}

有以上源码可知,add(int索引,E元素),addAll(Collection <?扩展E> c),addAll(int索引,Collection <?扩展E> c)操作是先对集合容量检查,以确保然后通过System.arraycopy()方法将旧数组元素复制到一个新的数组中去。

删除操作

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 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
}

当我们调用remove(int index)时,首先会检查index是否合法,然后再判断要删除的元素是否位于其最后一个位置。如果index不是最后一个,就再次调用System.arraycopy()方法副本。说白了就是重新索引index + 1开始向后所有的元素都向前挪一个位置。然后将数组的最后一个位置空,大小-1。如果index是最后一个元素那么就直接将数组的最后一个位置空,大小-1就可以。当我们调用remove(Object o)时,会把o分为是否为空来分别处理。方法,删除下标为index的元素。实际上仔细观察fastRemove(int index)方法和remove(int index)方法基本全部相同。

get操作

public E get(int index) {
	rangeCheck(index);
	return elementData(index);
}

由于ArrayList尽可能是基于内置实现的,所以获取元素就相当简单了,直接调用层叠随机访问即可。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值