ArrayList简介
ArrayList 是一个数组队列,相当于 动态数组。与Java中的数组相比,它的容量能动态增长。
和Vector不同,ArrayList中的操作不是线程安全的!所以,建议在单线程中才使用ArrayList,而在多线程中可以选择Vector或者CopyOnWriteArrayList。
ArrayList属性
ArrayList属性主要就是当前数组长度size,以及存放数组的对象elementData数组,除此之外还有一个经常用到的属性就是从AbstractList继承过来的modCount属性,代表ArrayList集合的修改次数。
private static final int DEFAULT_CAPACITY = 10; //容量
private static final Object[] EMPTY_ELEMENTDATA = {}; // 一个空对象
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};// 一个空对象,默认构造函数创建
transient Object[] elementData; //数组
private int size; //长度
ArrayList构造函数
无参构造
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA; //空对象
}
注意:此时我们创建的ArrayList对象中的elementData中的长度是1,size是0,当进行第一次add的时候,elementData将会变成默认的长度:10.
含参构造
public ArrayList(int initialCapacity) {
if (initialCapacity > 0) {
this.elementData = new Object[initialCapacity]; //new一个新数组
} else if (initialCapacity == 0) {
this.elementData = EMPTY_ELEMENTDATA; //空对象
} else {
throw new IllegalArgumentException("Illegal Capacity: "+ //非法参数
initialCapacity);
}
}
插入数据:add()方法
注意size时元素个数,elementData.length是当前数组容量
public boolean add(E e) {
modCount++;
add(e, elementData, size);
return true;
}
private void add(E e, Object[] elementData, int s) {
if (s == elementData.length)
elementData = grow();
elementData[s] = e;
size = s + 1;
}
在末尾添加元素:首先要判断是否需要扩容,传入s为当前元素个数size,若s == elementData.length则需要调用grow()扩容
public void add(int index, E element) {
rangeCheckForAdd(index); //检查边界
modCount++; //修改次数加一
final int s;
Object[] elementData;
if ((s = size) == (elementData = this.elementData).length)
elementData = grow(); //当前元素个数size == 当前数组容量length 时,grow扩容
System.arraycopy(elementData, index,
elementData, index + 1,
s - index); //index后的元素后移
elementData[index] = element; //插入
size = s + 1;
}
在指定位置添加元素:首先rangeCheckForAdd(index)判断索引是否超出数组下标,之后检测是否需要扩容**(当前元素个数size == 当前数组容量length 时,grow扩容)**
System.arraycopy()把指定位置的以后的元素向后移一位,将index位置赋新值
补充System.arraycopy()方法:
将指定源数组(前)中的数组从指定位置复制到目标数组(后)的指定位置。
扩容方法grow()
private Object[] grow() {
return grow(size + 1); //扩容需要的最小容量为size+1
}
private Object[] grow(int minCapacity) {
int oldCapacity = elementData.length;
if (oldCapacity > 0 || elementData != DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {//非初始化的扩容
int newCapacity = ArraysSupport.newLength(oldCapacity,
minCapacity - oldCapacity, /* minimum growth */
oldCapacity >> 1 /* preferred growth */);//新数组长度
return elementData = Arrays.copyOf(elementData, newCapacity);
//复制旧数组,赋值给新数组,长度为newCapcity(不足的长度用0填充)
} else { //初始化的扩容
return elementData = new Object[Math.max(DEFAULT_CAPACITY, minCapacity)];
}
}
扩容步骤:
使用Arrays.copyOf(elementData, newCapacity), 复制旧数组到新数组,长度为newCapcity(不足的长度用0填充)
minCapacity - oldCapacity是最小需要增长的容量,
oldCapacity >> 1这是期待增长的容量,原容量的0.5倍
**public static final int SOFT_MAX_ARRAY_LENGTH = Integer.MAX_VALUE - 8; **
(设置一个阈值,保留8的长度,防止一些异常)
源码中定义MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;上面的注释也写明白了。
一些vm可能会在数组中保留一些header信息,分配更大的长度可能会导致OutOfMemoryError异常。
这里这样做的原因是为了尽可能的避免因为vm使用了数据保存header的信息而导致分配更大的长度产生OutOfMemoryError异常。但是并不一定超出这个长度一定会异常。这只是为了尽可能的去避免。但是假使当一个vm使用了数组保存一些header,并且这些header使用的长度大于8时那么当数组扩容到2^31-1再减去header的信息长度时依旧会发生OutOfMemoryError异常。
所以Arraylist的最大长度为2147483647即2^31-1
决定扩容大小的函数:
public static int newLength(int oldLength, int minGrowth, int prefGrowth) {
// preconditions not checked because of inlining
// assert oldLength >= 0
// assert minGrowth > 0
int prefLength = oldLength + Math.max(minGrowth, prefGrowth); // might overflow
if (0 < prefLength && prefLength <= SOFT_MAX_ARRAY_LENGTH) { //大于0小于阈值
return prefLength; //范围符合要求,增长1.5倍
} else {
// put code cold in a separate method
return hugeLength(oldLength, minGrowth); //prefLength大于阈值时
}
}
private static int hugeLength(int oldLength, int minGrowth) {
int minLength = oldLength + minGrowth;
if (minLength < 0) { // overflow,溢出了,最大长度2^31-1
throw new OutOfMemoryError(
"Required array length " + oldLength + " + " + minGrowth + " is too large");
} else if (minLength <= SOFT_MAX_ARRAY_LENGTH) { //小于阈值,分配阈值大小
return SOFT_MAX_ARRAY_LENGTH;
} else {
return minLength; //否则分配minLength最小增长大小
}
}
综上,扩容大小:
- 调用add方法,数组容量不足,需要扩容。
1)使用无参构造创建的ArrayList,并且是第一次添加元素,扩容后的新容量为10
2)在添加元素时容量不足,扩容后的新容量为原先容量的1.5倍(除过初始容量为1的ArrayList第一次扩容,它第一次扩容后的容量为2)。
3)原先容量的1.5倍prefLength大于阈值SOFT_MAX_ARRAY_LENGTH,并且最小扩容量minLength小于阈值,则新容量为阈值SOFT_MAX_ARRAY_LENGTH;
若最小扩容量也大于阈值,那新容量为最小扩容量minLength
ArrayList类常用方法
import java.util.ArrayList;
- ArrayList() 构造一个初始容量为十的空列表。
| Modifier and Type | Method and Description |
|---|---|
boolean | add(E e) 将指定的元素追加到此列表的末尾。 |
boolean | contains(Object o) 如果此列表包含指定的元素,则返回 true 。 |
E | get(int index) 返回此列表中指定位置的元素。 |
int | indexOf(Object o) 返回此列表中指定元素的第一次出现的索引,如果此列表不包含元素,则返回-1。 |
E | remove(int index) 删除该列表中指定位置的元素 |
E | set(int index, E element) 用指定的元素替换此列表中指定位置的元素。 |
void | sort(Comparator<? super E> c) 使用提供的 Comparator对此列表进行排序以比较元素。(默认为升序) Comparator.reverseOrder() 参数降序排序 Comparator.naturalOrder() 参数升序排序 |
| List subList(int fromIndex, int toIndex) | 返回此列表中指定的 fromIndex (包括)和 toIndex之间的独占视图。 |
ArrayList是Java中的动态数组,其容量可随元素增加而自动扩展。与线程安全的Vector不同,ArrayList操作不是线程安全的,适合单线程使用。在添加元素时,若容量不足,会进行扩容,扩容策略通常是原容量的1.5倍,但最大容量受Integer.MAX_VALUE限制。此外,ArrayList提供了如add、contains、get、remove等常用方法。
960





