ArrayList
位置: java.util.ArrayList
public class ArrayList
ArrayList是什么?
- ArrayList可以说是一个可变长度的数组,底层基于变长数组实现,所以ArrayList的查询速度是很快的
- ArrayList允许空值和重复元素,当往ArrayList中添加的元素数量大于其底层数组的容量时,就会通过括容机制重新生成一个更大的数组
- ArrayList是线程不安全,在并发环境下,多个线程同时操作ArrayList,会引发不可预知的错误或异常
ArrayList的成员变量
/**
* 序列化
*/
private static final long serialVersionUID = 8683452581122892189L;
/**
* 默认初始容量。 当add第一个元素的时候初始化长度 10
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 用于空实例的共享空数组实例。
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 用于默认大小的空实例的共享空数组实例。我们
* 将其与空的元素数据区分开来,以了解何时
* 添加第一个元素。
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 存储ArrayList元素的数组缓冲区。
* ArrayList的容量是此数组缓冲区的长度。
* 任何 elementData==DEFAULTCAPACITY的空ArrayList
* elementData 将在添加第一个元素时扩展到默认容量。
* 这个数组时真正存储ArrayList集合的元素的数组
*/
transient Object[] elementData; // 非私有以简化嵌套类访问
/**
* ArrayList的大小(它包含的元素数)。
* @serial
*/
private int size;
/**
* 要分配的数组的最大大小。
* Integer.MAX_VALUE = 2147483647
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
ArrayList的构造函数
ArrayList的构造函数有三个
无参构造↓↓↓↓↓↓
构造方法中elementData初始化为空的DEFAULTCAPACITY_EMPTY_ELEMENTDATA,当调用add方法添加第一个元素的时候,才会进行扩容,扩容大小为private static final int DEFAULT_CAPACITY = 10;
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
指定初始容量的构造↓↓↓↓↓↓
这个构造方法是让我们创建ArrayList的时候就指定容量大小initialCapacity
。
此构造函数中判断了三种情况
-
initialCapacity > 0
参数大于0 ,elementData初始化为initialCapacity
大小的数组 -
initialCapacity == 0
参数等于0,elementData初始化为空数组,即private static final Object[] EMPTY_ELEMENTDATA = {};
-
initialCapacity < 0
参数小于0,抛出IllegalArgumentException异常
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);
}
}
指定初始集合的元素的构造↓↓↓↓↓↓
此构造方法的参数是一个Collection集合,将一个Collection集合转为ArrayList集合,如果传入的Collection为null就会报空指针异常
public ArrayList(Collection c) {
elementData = c.toArray();
// 如果 elementData 不等于 0
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;
}
}
以上是ArrayList的成员变量和构造方法,我们使用时可以使用默认的空参构造,也可以指定使用指定长度的构造,这两种我在工作的时候使用较多,第三种构造没怎么使用过,具体的使用看业务需求而定吧!
下面看一下ArrayList的一些操作方法
ArrayList的add方法
ArrayList的add方法是将元素添加到arrayList的末尾。看一下他是如何实现的!
下面的代码是有关add相关的所有代码
-
`ensureCapacityInternal(size + 1); // Increments modCount!
ensureCapacityInternal方法
调用 calculateCapacity 方法判断是不是空数组,如果使用空参构造方法创建,并且第一次添加的时候,那么 minCapacity = size(0) + 1 = 1, 然后比较
Math.max(DEFAULT_CAPACITY, minCapacity);
比较,DEFAULT_CAPACITY = 10,所以DEFAULT_CAPACITY大,所以第一次添加的时候就初始化默认容量为10。在 调用ensureExplicitCapacity方法判断是否需要进行扩容ensureExplicitCapacity方法
这个方法判断是否需要扩容,如果
inCapacity - elementData.length > 0
成立,说明将添加的元素索引已经大于数组的长度了,需要进行扩容,其中grow()方法就是执行扩容操作grow方法
- oldCapacity为旧数组的容量
- newCapacity为新数组的容量,新数组的容量的大小=
oldCapacity + (oldCapacity >> 1);
即新容量为旧容量的1.5倍 - 判断新数组的容量是否小于最小需求容量,如果小于那就将最小数组容量作为数组的新容量
- 判断新容量是否大于
MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
,如果大于,则通过hugeCapacity
方法比较(minCapacity > MAX_ARRAY_SIZE) ?Integer.MAX_VALUE:MAX_ARRAY_SIZE;
- 扩容并拷贝原数组中的元素 Arrays.copyOf(原数组, 新数组容量);
-
elementData[size++] = e;
把传入的参数放到数组中 -
return true;
返回值
/**
* 将指定的元素追加到列表的末尾。
*
* @param e 元素,以附加到此列表中
* @return true (as specified by {@link Collection#add})
*/
public boolean add(E e) {
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
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);
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return 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;
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: minCapacity通常接近大小,所以这是一个胜利:
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的add(index, Element);
这个方法实在指定索引(index)位置添加元素(E)
- 首先调用
rangeCheckForAdd
方法判断指定的索引是否越界,越界就报异常 - 没有越界,调用
ensureCapacityInternal
方法判断是否需要扩容 - 使用
System.arraycopy
方法将index及其后面的元素都往后一一位 - 将新元素(E)插入index位置
/**
* 在列表的指定位置插入指定的元素。
* 将元素当前的位置(如果有的话)和后续的元素向右移动(在它们的索引中添加一个)。
*
* @param index 要插入指定元素的索引
* @param 要插入的元素
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
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++;
}
// 检查给定的索引是否在范围内,不在就抛出异常
private void rangeCheckForAdd(int index) {
if (index > size || index < 0)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}
System.arraycopy(src, srcPos, dest, destPos, length)
作用:将指定源数组中的数组从指定位置复制到目标数组的指定位置。
参数:
- src 原数组,
- srcPos:原数组中的起始位置,
- dest: 目标数组,
- destPos: 目标数组中的起始位置
- length:要复制的原数组元素的数量
ArrayList的addAll(Collection c);
这个方法是将指定集合c,中的所有元素追加到末尾
/**
* 将指定集合中的所有元素追加到列表的末尾
* 按照指定集合的迭代器返回的顺序排列。
* 如果在操作进行时修改了指定的集合,则此操作的行为未定义。
* (这意味着,如果指定的集合是这个列表,并且这个列表是非空的,则此调用的行为是未定义的。)
*
* @param c 集合,其中包含要添加到此列表的元素
* @return true 如果该列表因调用而改变
* @throws NullPointerException 如果指定的集合为空
*/
public boolean addAll(Collection 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;
}
ArrayList的addAll(int index, Collection c)
作用:从指定位置index开始,将指定集合C中的所有元素追加到列表中
从源码中可以看到,如果 size - index > 0 ,就是先进行赋值,
最后c中的所有元素再将指定index以及后面对应长度的值覆盖
public boolean addAll(int index, Collection 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;
}
ArrayList的get(int index)方法
在get方法中,通过index获取元素
- 首先调用
rangeCheck(index)
方法判断index是否合法,不合法就包IndexOutOfBoundsException异常。 - 合法,就调用elementData(index)方法返回index处的元素
/**
* 返回列表中指定位置的元素。
*
* @param index 要返回的元素的索引
* @return 此列表中指定位置的元素
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E get(int index) {
rangeCheck(index);
return elementData(index);
}
rangeCheck方法
/**
* 检查给定索引是否在范围内。
如果不是,则抛出适当的运行时异常。
这个方法不* *检查索引是否为负:
它总是在数组访问之前使用,
如果索引为负则抛出ArrayIndexOutOfBoundsException。
*/
private void rangeCheck(int index) {
if (index >= size)
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
elementData方法
直接通过index获取数组元素,时间复杂度为O(1)。
E elementData(int index) {
return (E) elementData[index];
}
ArrayList的remove方法
E remove(int index)
这个方法按照索引删除。
- 调用rangeCheck(index)方法判断索引index是否合法,不合法,抛异常
- 合法,根据index获取需要删除的元素,这里获取,最后返回(return)。
- size - index -1; 删除了数组中的元素,后面的元素往需要往前移动。
- 判断 size - index -1 是否大于0,大于需要往前移动,等于0,说明是最后一个元素,不需要移动。
- 将原数组的最后一个元素赋值为null。清除,让GC完成它的工作
/**
* 移除此列表中指定位置的元素。
* 将后面的元素向左移动(从它们的索引中减去1)
*
* @param index 要删除的元素的索引
* @return 从列表中删除的元素
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E remove(int index) {
rangeCheck(index);
modCount++; // 记录了ArrayList结构性变化的次数。
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;
}
boolean remove(Object o)
根据元素删除,会删除和参数匹配的第一个元素。
- 如果参数为null,则遍元素中的第一个为null的元素的index,然后调用
fastRemove(index)
根据索引删除, - 如果不为null,遍历元素中第一个参数匹配的元素索引,然后调用
fastRemove(index)
根据索引删除。
/**
* 从列表中删除指定元素的第一个出现项,
* 如果它存在的话。如果列表中不包含该元素,则不会对其进行更改
* . 更正式地说,删除索引最低的元素
* i such that
* (o==null ? get(i)==null : o.equals(get(i)))
* (如果存在这样的元素) Returns true if this list
* 如果这个列表包含指定的元素(或者同样地,如果这个列表由于调用而改变)。
*
* @param o 要从列表中删除的元素(如果存在)
* @return true 如果此列表包含指定元素
*/
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;
}
fastRemove(index)方法
此方法根据索引删除所引处的元素。原理与remove(int index)
方法相同,但是没有返回值
/*
* 跳过边界检查且不返回已删除值的私有移除方法。
*/
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
}
ArrayList的Set(int index, E element)方法
此方法根据index修改index处的值
- 首先判断index是否合法,不合法抛异常
- 合法,则根据index查询元素,最后把index处的元素替换为新元素,return 旧元素
/**
* 将列表中指定位置的元素替换为指定元素。
*
* @param index 要替换的元素的索引
* @param element 元素存储在指定位置
* @return 先前位于指定位置的元素
* @throws IndexOutOfBoundsException {@inheritDoc}
*/
public E set(int index, E element) {
rangeCheck(index);
E oldValue = elementData(index);
elementData[index] = element;
return oldValue;
}
ArrayList的size()方法
此方法直接返回size变量。上述的add的所有方法都会size++; addAll方法会使size+参数的元素长度,上述的删除元素的时候,会--size。
private int size; // ArrayList的成员变量
public int size() {
return size;
}
文章不定时更新