ArrayList源码学习
简介
简单实现见文章底部
ArrayList是常用的集合,ArrayList查询效率高,增删效率低
自己实现ArrayList最需要注意的细节:
- 加法优先级高于位运算,故必须要加括号才行
int oneAndHalf = elementData.length + (elementData.length >> 1);
- 删除元素,往前移动index后的元素,移动之后,最后一个元素要置位null,否则会产生内存泄漏,比较容易产生内存泄漏的还有threadlocal
public E remove(int index) {
checkIndex(index);
E t=(E)elementData[index];
System.arraycopy(elementData,index+1,elementData,index,size-index-1);
//设置为空 以免产生内存溢出
elementData[size-1]=null;
size--;
return t;
}
- 查询元素和判断元素相等的时候,需要把null值额外判断一遍
remove(Object o)
indexOf(Object o)
复制数组的重要工具类
# 最常用最重要 特别是原数组与目标数组为同一个数组的时候
System.arraycopy(原数组, 原数组开始索引, 目标数组,目标数组开始索引,
复制长度);
# 拷贝原数组前newLength个元素生成一个新数组
public static T[] Arrays.copyOf(T[] original, int newLength)
# 拷贝original的索引为[form,to)返回一个新数组
public static <T> T[] copyOfRange(T[] original,
int from,
int to)
静态成员
没有特别意义的两个常量: 但是由于引用的地址不同,可以区别懒加载还是初始化容量0
EMPTY_ELEMENTDATA
在new ArrayList(int initialCapacity) 指定长度为0时使用,
在其他任意表示elementData 为空时也会使用DEFAULTCAPACITY_EMPTY_ELEMENTDATA
仅在new ArrayList() 没有指定长度时才会使用
# 默认初始容量 但是会使用懒加载
private static final int DEFAULT_CAPACITY = 10;
# new ArrayList(int initialCapacity) 指定长度为0时
private static final Object[] EMPTY_ELEMENTDATA = {};
# new ArrayList() 没有指定长度时
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
# 最大长度(比较重要)
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
普通成员
注意不是使用泛型数组 而是使用 Object[]数组
# 存储数据的数组
transient Object[] elementData; // non-private to simplify nested class access
# 记录当前存储元素个数
private int size;
构造函数
无参构造
- 默认初始化DEFAULTCAPACITY_EMPTY_ELEMENTDATA={}
public ArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
有参构造
- 如果指定了长度 就立即初始化为当前长度(当要添加大数据量的时候,可以避免重复扩容)
- 如果指定长度为0 则为EMPTY_ELEMENTDATA={}
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);
}
}
grow()扩容函数
只得注意的是,位运算的优先级低于加减法,所以需要括号括起来,否则会变成(oldCapacity+oldCapacity)/2,失去了扩容的效果
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
//扩容到1.5倍
int newCapacity = oldCapacity + (oldCapacity >> 1);
//如果不能扩容到1.5倍 则扩容到当前期望的minCapacity
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);
}
如何保证增删改查
添加元素
- 末尾插入
检查当前数组长度 确保内部容量 如果不足size+1 则grow扩容
public boolean add(E e) {
// 1. 检查当前数组长度 确保内部容量 如果不足size+1 则grow扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
elementData[size++] = e;
return true;
}
// 3. 懒加载的实现部分 如果new ArrayList()就会在这里实现懒加载
private static int calculateCapacity(Object[] elementData, int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
return minCapacity;
}
// 2. calculateCapacity会返回能够承载当前元素的容量(默认值10 或者 minCapacity)
private void ensureCapacityInternal(int minCapacity) {
ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
}
// 4. 如果最小容量大于数组长度 则需要扩容
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
// overflow-conscious code
if (minCapacity - elementData.length > 0)
grow(minCapacity);
}
- 指定位置插入
System.arraycopy(原数组, 原数组开始索引, 目标数组,目标数组开始索引,复制长度);
这里我们可以好好研究一下为什么System.arraycopy(elementData, index, elementData, index + 1,size - index);将从index开始的元素全部往后移了一位。
例如: arr= [4,8,7,4,5,3,null,null,…] ,此时size=6
如果 我们想在index为2 ,即8的后面添加元素1 只需要将数组变成 arr2= [4,8,7,7,4,5,3,null,…] ,然后将arr2[2]=2即可
怎么实现呢?
我们需要 [2,5] 的元素向后移动1格 也就是从**2(index)**开始 拷贝到从 index+1 开始的空间中
并且移动的元素有{7,4,5,3}共 4(size-index) 个元素
public void add(int index, E element) {
rangeCheckForAdd(index);
//检查当前数组长度 确保内部容量 如果不足size+1 则grow扩容
ensureCapacityInternal(size + 1); // Increments modCount!!
//从index开始 所有元素后移一个
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
public void add(int index, E element) {
rangeCheckForAdd(index);
ensureCapacityInternal(size + 1); // Increments modCount!!
//从index开始 所有元素后移一个
System.arraycopy(elementData, index, elementData, index + 1,
size - index);
elementData[index] = element;
size++;
}
删除元素
因为只用移动index后面的元素向前一格 故移动的数量是 int numMoved = size - index - 1;
public E remove(int index) {
rangeCheck(index);
modCount++;
E oldValue = elementData(index);
int numMoved = size - index - 1;
if (numMoved > 0)//将index后面的元素向前移动
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
}
ArrayList简单实现
import java.util.Arrays;
/**
* @Author MinXu
* @Date 2020/12/21 23:16
* QQ 754647431
*/
public class MyList<E> {
//Default capacity
private static final int DEFAULT_CAPACITY = 10;
private static final Object[] EMPTY_ELEMENTDATA = {};
private Object[] elementData;
private int size;
public MyList() {
elementData = EMPTY_ELEMENTDATA;
}
public MyList(int capacity) {
if (capacity > 0)
elementData = new Object[capacity];
else
elementData = EMPTY_ELEMENTDATA;
}
void ensureCapacity(int minCapacity) {
if(elementData==EMPTY_ELEMENTDATA){
elementData=new Object[DEFAULT_CAPACITY];
return;
}
//需要扩容
if (minCapacity >= elementData.length) {
//加法优先级高于位运算
int oneAndHalf = elementData.length + (elementData.length >> 1);
Object[] a;
if (minCapacity > oneAndHalf) {
a = new Object[minCapacity];
} else {
a = new Object[oneAndHalf];
}
System.arraycopy(elementData,0,a,0,size);
elementData=a;
}
}
void checkIndex(int index){
if(index<0)
throw new IndexOutOfBoundsException("索引小于0");
if(index>size-1)
throw new IndexOutOfBoundsException("索引越界");
}
public void add(E e) {
ensureCapacity(size + 1);
elementData[size++]=e;
}
public E remove(int index) {
checkIndex(index);
E t=(E)elementData[index];
System.arraycopy(elementData,index+1,elementData,index,size-index-1);
//设置为空 以免产生内存溢出
elementData[size-1]=null;
size--;
return t;
}
public void add(int index,E e) {
checkIndex(index);
ensureCapacity(size + 1);
System.arraycopy(elementData,index,elementData,index+1,size-index);
elementData[index]=e;
size++;
}
public E get(int index) {
checkIndex(index);
return (E)elementData[index];
}
public int size() {
return this.size;
}
public void print() {
System.out.println(Arrays.toString(elementData));
}
@Override
public String toString() {
if(size==0)
return "[]";
StringBuilder res = new StringBuilder("[");
for(int i=0;i<size-1;i++){
res.append(elementData[i].toString()).append(", ");
}
res.append(elementData[size-1].toString());
res.append("]");
return res.toString();
}
}