集合框架源码
ArrayList
介绍
ArrayList是Collection接口中List接口的实现类,是一种有序可重复的单列集合,集合底层采用数组进行数据存储。
ArrayList是Java集合框架中的一个重要类,它实现了List接口,用于存储元素的有序集合。以下是ArrayList的优缺点分析:
优点
- 动态长度:ArrayList的长度是动态的,可以根据需要自动增长或缩减。当元素数量超出当前容量时,ArrayList会自动进行扩容,以适应更多的元素。这一特性使得ArrayList在存储元素时更加灵活,无需像静态数组那样在初始化时确定大小。
- 基于数组实现:ArrayList内部是基于数组实现的,这意味着可以通过索引快速访问元素,时间复杂度为O(1)。这种基于数组的实现方式使得ArrayList在随机访问方面具有较高的效率。
- 允许重复元素和null:ArrayList可以包含重复的元素,同时也可以存储null元素。这一特性使得ArrayList在存储具有重复性和空值的数据时更加灵活。
- 实现了List接口:ArrayList实现了List接口,因此支持List接口的所有操作,如按索引访问、添加、删除和遍历等。这使得ArrayList具有丰富的操作方法,能够满足不同的需求。
- 遍历效率高:由于ArrayList是基于数组的,且支持随机访问,因此在进行遍历操作时具有较高的效率。通过索引可以快速地访问到每个元素,从而提高了遍历的速度。
缺点
- 动态扩展的开销:虽然ArrayList具有动态扩展的特性,但这种扩展操作可能会带来一定的性能开销。当数组达到容量限制时,ArrayList需要重新分配更大的内存块,并将现有元素复制到新的内存位置。这种操作在大型数据集上可能会导致性能下降。
- 内存占用:ArrayList会为每个元素分配内存,这可能会导致内存占用较大,特别是在存储大量数据时。如果内存是一个有限资源,这可能成为一个问题。
- 不适合频繁插入和删除:在ArrayList的开始位置或中间位置插入或删除元素时,需要移动大量的元素来保持顺序,这会导致较高的时间复杂度(O(n))。相比之下,LinkedList在插入和删除操作方面具有较高的效率。
- 缺乏内建排序支持:ArrayList不具备内建的排序功能,因此如果需要对列表中的元素进行排序,需要手动实现或使用额外的排序算法。
- 不支持基本数据类型:ArrayList只能存储对象,而不支持原始数据类型(例如int、char、double等)。因此,对于大量的原始数据类型,需要将它们包装为对象,这可能会导致装箱和拆箱开销。
- 线程安全性:ArrayList不是线程安全的。如果多个线程同时访问和修改ArrayList,可能会导致竞争条件和数据不一致性。虽然可以通过外部同步来解决这个问题,但会增加编程的复杂性和性能开销。
综上所述,ArrayList具有动态长度、基于数组实现、允许重复元素和null、实现了List接口以及遍历效率高等优点;但同时也存在动态扩展开销、内存占用大、不适合频繁插入和删除、缺乏内建排序支持、不支持基本数据类型以及线程安全性等缺点。在选择使用ArrayList时,需要根据具体的应用场景和需求进行权衡。
源码解析
成员变量
// 序列化ID,因为ArrayList实现了Serializable
private static final long serialVersionUID = 8683452581122892189L;
/**
* Default initial capacity.
*/
// 默认初始化数组的长度 10
private static final int DEFAULT_CAPACITY = 10;
/**
* Shared empty array instance used for empty instances.
*/
// 长度为0的数组,为空实例准备,EMPTY_ELEMENTDATA是一个空的数组实例,通常用于表示那些没有显式指定初始容量的ArrayList实例。当通过无参构造器创建ArrayList时,它的内部数组就会指向EMPTY_ELEMENTDATA。这意味着在添加第一个元素之前,ArrayList不占用除了对象头以外的额外空间。
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* Shared empty array instance used for default sized empty instances. We
* distinguish this from EMPTY_ELEMENTDATA to know how much to inflate when
* first element is added.
* 这个数组实例是共享的,用于所有通过无参构造器创建的ArrayList实例。
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* The array buffer into which the elements of the ArrayList are stored.
* The capacity of the ArrayList is the length of this array buffer. Any
* empty ArrayList with elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA
* will be expanded to DEFAULT_CAPACITY when the first element is added. 存储数据的数组
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* The size of the ArrayList (the number of elements it contains).
*
* @serial
* 数组的当度
*/
private int size;
DEFAULTCAPACITY_EMPTY_ELEMENTDATA
与EMPTY_ELEMENTDATA
的区别
- 用途不同:
EMPTY_ELEMENTDATA
也用于表示空的ArrayList
,但它通常用于其他场景,比如当通过有参构造器传入初始容量为0时,或者在某些内部方法中用于表示一个空的集合状态。而DEFAULTCAPACITY_EMPTY_ELEMENTDATA
则专门用于无参构造器创建的ArrayList
实例。 - 扩容行为:由于
EMPTY_ELEMENTDATA
和DEFAULTCAPACITY_EMPTY_ELEMENTDATA
在初始化时都是空的数组,它们在添加第一个元素时都会触发扩容。但关键在于,DEFAULTCAPACITY_EMPTY_ELEMENTDATA
使得ArrayList
能够知道这是通过无参构造器创建的实例,因此在扩容时通常会选择一个默认的初始容量(如10),而不是简单地根据当前容量(0)来计算新容量。
构造方法
/**
* Constructs an empty list with the specified initial capacity.
*
* @param initialCapacity the initial capacity of the list
* @throws IllegalArgumentException if the specified initial capacity
* is negative
* 初始化数组的长度
*/
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
// 如果输入值>0 数组的长度为initalCapacity
this.elementData = new Object[initialCapacity];
} else if (initialCapacity == 0) {
// 如果输入值为0 将EMPTY_ELEMENTDATA的值给到elementData
this.elementData = EMPTY_ELEMENTDATA;
} else {
// 抛出参数不合法的异常
throw new IllegalArgumentException("Illegal Capacity: "+
initialCapacity);
}
}
/**
* Constructs an empty list with an initial capacity of ten.
* 默认构造将DEFAULTCAPACITY_EMPTY_ELEMENTDATA的地址给到elementData
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 通过集合来初始化数组
* Constructs a list containing the elements of the specified
* collection, in the order they are returned by the collection's
* iterator.
*
* @param c the collection whose elements are to be placed into this list
* @throws NullPointerException if the specified collection is null
*/
public ArrayList(Collection<? extends E> c) {
// 将集合转为Object[]
elementData = c.toArray();
// 如果elementData的长度不为0
if ((size = elementData.length) != 0) {
// c.toArray might (incorrectly) not return Object[] (see 6260652)
// 判断是否为Object数组
if (elementData.getClass() != Object[].class)
// 如果不是Object数组,copy一份Object数组赋予elementData
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
常用方法-add
/**
* Appends the specified element to the end of this list.
*
* @param e element to be appended to this list
* @return <tt>true</tt> (as specified by {@link Collection#add})
*/
public boolean add(E e) {
// 检查容量是否满足,并扩容,将modCount++
ensureCapacityInternal(size + 1); // Increments modCount!!
// size+1的位置放入新增元素
elementData[size++] = e;
// 返回true
return true;
}
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
// 扩容
grow(minCapacity);
}
/**
* Increases the capacity to ensure that it can hold at least the
* number of elements specified by the minimum capacity argument.
*
* @param minCapacity the desired minimum capacity
*/
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
// newCapacity为oldCapacity的1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
if (newCapacity - minCapacity < 0)
// 如果1.5倍扩容小于需要的长度,直接为需要的长度
newCapacity = minCapacity;
if (newCapacity - MAX_ARRAY_SIZE > 0)
// 如果数组长度大于Integer.MAX_VALUE - 8,返回Integer.MAX_VALUE
newCapacity = hugeCapacity(minCapacity);
// minCapacity is usually close to size, so this is a win:
// 数组复制
elementData = Arrays.copyOf(elementData, newCapacity);
}
/**
* Inserts the specified element at the specified position in this
* list. Shifts the element currently at that position (if any) and
* any subsequent elements to the right (adds one to their indices).
*
* @param index index at which the specified element is to be inserted
* @param element element to be inserted
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public void add(int index, E element) {
// 检查索引是否在范围之内
rangeCheckForAdd(index);
// 检查容量是否满足,并扩容,将modCount++
ensureCapacityInternal(size + 1); // Increments modCount!!
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
常用方法-remove
/**
* Removes the first occurrence of the specified element from this list,
* if it is present. If the list does not contain the element, it is
* unchanged. More formally, removes the element with the lowest index
* <tt>i</tt> such that
* <tt>(o==null ? get(i)==null : o.equals(get(i)))</tt>
* (if such an element exists). Returns <tt>true</tt> if this list
* contained the specified element (or equivalently, if this list
* changed as a result of the call).
*
* @param o element to be removed from this list, if present
* @return <tt>true</tt> if this list contained the specified element
*/
public boolean remove(Object o) {
if (o == null) {
// 遍历数组,查找数组中值为null的
for (int index = 0; index < size; index++)
if (elementData[index] == null) {
fastRemove(index);
return true;
}
} else {
for (int index = 0; index < size; index++)
// 通过equals判断2个对象是否相同
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)
// 参数1 源数组
// 参数2 源数组的起始位置
// 参数3 目标数组
// 参数4 目标数组的起始位置
// 参数5 复制的源数组中的个数
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
// 将源数组最后一位设置null
elementData[--size] = null; // clear to let GC do its work
}
常用方法-addAll
public boolean addAll(Collection<? extends E> c) {
// 参数转为数组
Object[] a = c.toArray();
// 获取数组的长度
int numNew = a.length;
// 是否需要扩容
// 这里如果需要扩容,扩容到size+numNew的大小
ensureCapacityInternal(size + numNew); // Increments modCount
// 从a数组的0标位开始复制,复制到elementData中,从size开始,复制的个数为a的长度
System.arraycopy(a, 0, elementData, size, numNew);
// size增减numNew
size += numNew;
return numNew != 0;
}
常用方法-removeRange
protected void removeRange(int fromIndex, int toIndex) {
modCount++;
int numMoved = size - toIndex;
// 将源数组中的toIndex移动到elemetnData中
System.arraycopy(elementData, toIndex, elementData, fromIndex,
numMoved);
// clear to let GC do its work
int newSize = size - (toIndex-fromIndex);
// 将fromIndex到toIndex之间的设置为null 用于
for (int i = newSize; i < size; i++) {
elementData[i] = null;
}
size = newSize;
}
常用方法-batchRemove
private boolean batchRemove(Collection<?> c, boolean complement) {
final Object[] elementData = this.elementData;
int r = 0, w = 0;
boolean modified = false;
try {
for (; r < size; r++)
if (c.contains(elementData[r]) == complement)
// 需要删除的数据在elementData中被后面的数据替换
elementData[w++] = elementData[r];
} finally {
// Preserve behavioral compatibility with AbstractCollection,
// even if c.contains() throws.
// r!=size只有在编译的过程中抛出异常才会出现
// r=1 size=2
// w=1
if (r != size) {
System.arraycopy(elementData, r,
elementData, w,
size - r);
w += size - r;
}
if (w != size) {
// clear to let GC do its work
for (int i = w; i < size; i++)
elementData[i] = null;
modCount += size - w;
size = w;
modified = true;
}
}
return modified;
}
eg:
List list1 = 1,2,3,4,5;
List list2 = 3;
this.elementData=1,2,4,5,5
finally前
w=4
删除this.elementData[4]
最终this.elementData=1,2,4,5
moudcount 作用
package com.sofwin.configer;
import java.util.ArrayList;
import java.util.Iterator;
public class ModCountExample {
public static void main(String[] args) {
ArrayList<String> list = new ArrayList<>();
list.add("A");
list.add("B");
list.add("C");
new Thread(()->{
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
if (element.equals("B")) {
// 在迭代过程中尝试修改集合
list.remove(element);
}
}
}).start();
// 输出修改后的集合
for (String element : list) {
System.out.println(element);
}
}
}
1.9.6.jar;D:\mavenrepository\org\javassist\javassist\3.25.0-GA\javassist-3.25.0-GA.jar" com.sofwin.configer.ModCountExample
A
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$Itr.checkForComodification(ArrayList.java:909)
at java.util.ArrayList$Itr.next(ArrayList.java:859)
at com.sofwin.configer.ModCountExample.main(ModCountExample.java:26)
Process finished with exit code 1
由于在迭代过程中修改了集合,将会抛出ConcurrentModificationException异常
这是因为modCount已经改变,而expectedModCount仍然保持迭代开始时的值
建议写法
Iterator<String> iterator = list.iterator();
while (iterator.hasNext()) {
String element = iterator.next();
System.out.println(element);
}