文章目录
基于jdk1.8的ArrayList原码分析
ArrayList的特点:
底层数据结构为数组
元素排列有序、可重复、可以为null
线程不安全
元素查找快,增删慢
扩容大小为:原先数组大小乘以1.5
ArrayList的成员变量
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
private static final long serialVersionUID = 8683452581122892189L;
/**
* 默认初始化数组的大小为10
*/
private static final int DEFAULT_CAPACITY = 10;
/**
* 用于空实例的共享空数组实例
* 当调用int类型和 Collection<? extends E> 类型构造方法时,int为0或者Collection的大小为0时
* 将EMPTY_ELEMENTDATA赋值给elementData
*/
private static final Object[] EMPTY_ELEMENTDATA = {};
/**
* 用于默认大小的空实例的共享空数组实例。
* 调用无参构造函数时将DEFAULTCAPACITY_EMPTY_ELEMENTDATA赋值给elementData
* 将其与EMPTY_ELEMENTDATA区分开来,扩容时区分逻辑,第一次新增元素时,
* 如果elementData==DEFAULTCAPACITY_EMPTY_ELEMENTDATA则扩容为10。具体在扩容部分讲解。
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* ArrAyList底层的数据结构为数组,使用transient修饰时,表示elementData不会被序列化
*/
transient Object[] elementData; // non-private to simplify nested class access
/**
* 数组的大小
*/
private int size;
/**
*最大数组容量
*/
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
}
ArrayList的构造方法
public class ArrayList<E> extends AbstractList<E>
implements List<E>, RandomAccess, Cloneable, java.io.Serializable
{
/**
* int类型参数构造方法,传入初始化的数组大小 initialCapacity
* 如果 initialCapacity大于0时,则new 一个大小为initialCapacity数组对象赋值给elementData
* 如果elementData等于0时则将EMPTY_ELEMENTDATA空数组赋值给elementData,使用共享空数组减少 * 空数组创建,优化性能
* 如果elementData小于0时则抛出异常
*/
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;使用共享空数组减少 空 * 数组创建,优化性能
*/
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
/**
* 将入参集合转化为数组赋值给elementData
* 判断数组的大小是否为空,如果为空则将EMPTY_ELEMENTDATA赋值给elementData
* 如果数组不为空,则判断数组是否为Object类型数组,如果不是则需要重新复制成Object数组
*/
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)//是否转化为Object数组
//非Object数组需要复制后重新赋值给elementData
elementData = Arrays.copyOf(elementData, size, Object[].class);
} else {
// replace with empty array.
this.elementData = EMPTY_ELEMENTDATA;
}
}
}
ArrayList的扩容机制
- ArrayList的扩容发生在向数组添加元素时
- DEFAULTCAPACITY_EMPTY_ELEMENTDATA 空数组扩容时,数组所需容量大小为10
- EMPTY_ELEMENTDATA空数组扩容时,以size+1为基础进行扩容
- 每一次扩容时都需要将原先数组复制到新数组中
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 数组扩容的入口
elementData[size++] = e;
return true;
}
private void ensureCapacityInternal(int minCapacity) {
//calculateCapacity 计算数组所需容量大小
//ensureExplicitCapacity 确认是否需要扩容
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
private static int calculateCapacity(Object[] elementData, int minCapacity) {
//如果通过无参构造器实例化ArrayList对象,在第一次添加元素时,这里会返回数组需要的容量为10,否则返 //回数组需要的容量为minCapacity,即当前数组大小+1
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);//取较大值
}
return minCapacity;
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// 当所需容量大于数组容量时则进行扩容
if (minCapacity - elementData.length > 0)
//扩容方法
grow(minCapacity);
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + (oldCapacity >> 1);//扩容大小为原先容量的1.5倍
if (newCapacity - minCapacity < 0)//比较扩容后的大小和所需容量大小进行比较
newCapacity = minCapacity;//扩容后的大小小于所需容量,则扩容大小为所需容量
if (newCapacity - MAX_ARRAY_SIZE > 0)
newCapacity = hugeCapacity(minCapacity);
// 扩容后需要通过复制将原先的数组赋值到新数组中
elementData = Arrays.copyOf(elementData, newCapacity);
}
ArrayList的三种遍历方式
public class ArrayListTest {
public static void main(String[] args) throws Exception {
ArrayList<Integer> list = new ArrayList(Arrays.asList(1, 2, 3, 4, 5));
//第一种方式 for循环
for (int i = 0; i < list.size(); i++) {
System.out.println(list.get(i));
}
//第二种方式 for each
for (Object o : list) {
System.out.println(o);
}
//迭代器
Iterator it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}
}
}
ArrayList的remove(int index)方法
- 源码分析
//删除指定下标的元素
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; // 将数组最后一位赋值为空
return oldValue;
}
- 场景应用:删除ArrayList中小于4的数据
public class ArrayListTest {
public static void main(String[] args) throws Exception {
//方法一:使用for循环,从后向前进行遍历
// 不能从前向后循环,因为删除非末尾元素时,需要将指定下标后面一位到数组末尾的全部元素向前移动一个单位,此时会有部分元素无法遍历到,
// 使用从前向后的遍历方式得到结果为[2,4,5],元素2没有被移除
ArrayList<Integer> list1 = new ArrayList(Arrays.asList(1, 2, 3, 4, 5));
for (int i = list1.size() - 1; i >= 0; i--) {
//过滤掉小于4的数据
if (list1.get(i) < 4) {
list1.remove(i);
}
}
System.out.println(list1);//[4,5]
//方法二:使用removeIf(Predicate<? super E> filter)方法
ArrayList<Integer> list2 = new ArrayList(Arrays.asList(1, 2, 3, 4, 5));
list2.removeIf((val -> val < 4));
System.out.println(list2);//[4,5]
//方法三:使用while循环
ArrayList<Integer> list3 = new ArrayList(Arrays.asList(1, 2, 3, 4, 5));
int i = 0;
while (i < list3.size()) {
if (list3.get(i) < 4) {
list3.remove(i);
} else {
i++;
}
}
System.out.println(list3);//[4,5]
//方法四:使用迭代器
ArrayList<Integer> list4 = new ArrayList(Arrays.asList(1, 2, 3, 4, 5));
Iterator<Integer> iterator = list4.iterator();
while (iterator.hasNext()) {
if (iterator.next() < 4) {
//这里不能使用 java.util.ArrayList.remove(int)方法,因为ArrayList是非线程安全的会抛出java.util.ConcurrentModificationException异常,在增强for循环中同理,编译就会报错
iterator.remove();
}
}
System.out.println(list4);//[4,5]
//方法五:使用java.util.ArrayList.removeAll方法
ArrayList<Integer> list5 = new ArrayList(Arrays.asList(1, 2, 3, 4, 5));
ArrayList<Integer> removeList = new ArrayList();
for (Integer o : list5) {
if (o < 4) {
removeList.add(o);
}
}
list5.removeAll(removeList);
System.out.println(list5);//[4,5]
}
}
- ArrayList的trimToSize方法
//去除冗余的数组容量
public void trimToSize() {
modCount++;
if (size < elementData.length) {
elementData = (size == 0)
? EMPTY_ELEMENTDATA
: Arrays.copyOf(elementData, size);
}
}