ArrayList底层实现
一、ArrayList实现原理
1.1、ArrayList 父类和实现接口
1.2、add方法实现
编写java代码
List<String> list = new ArrayList<>();
list.add("java");
点击add(),进入查看
进入实现类ArrayList的add()
private int size; //
ensureCapacityInternal(size + 1); //ensureCapacityInternal(1);
进入ensureCapacityInternal()
int minCapacity = 1;
transient Object[] elementData;
calculateCapacity(elementData, minCapacity)
进入calculateCapacity()
minCapacity = 1
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {
};
private static final int DEFAULT_CAPACITY = 10;
判断相等
执行Math.max(DEFAULT_CAPACITY, minCapacity);
return Max.max(10,1)
进入ensureExplicitCapacity()
ensureExplicitCapacity() //ensureExplicitCapacity(10)
int minCapacity = 10
AbstractList抽象类中定义了
protected transient int modCount = 0;
modCount++; //modCount = 1
进入判断
结果为true,执行grow(minCapacity);
grow(minCapacity); // grow(10);
进入grow(minCapacity)
grow(minCapacity); // grow(10);
int minCapacity = 10
int oldCapacity = 0
int newCapacity = 0
判断结果为true,执行
if (newCapacity - minCapacity < 0)
newCapacity = minCapacity; //newCapacity = 10
elementData = 10
elementData[size++] = e; //elementData[0] = e; 给数组设置值
size = 1;
return true;
1.3、使用反射测试初始化数组容量
List<String> list = new ArrayList<>();
list.add("java");
Class<? extends List> aClass = list.getClass();
Field elementData = aClass.getDeclaredField("elementData");
// 设置权限
elementData.setAccessible(true);
Object[] arr = (Object[]) elementData.get(list);
System.out.println("容量:" + arr.length);
System.out.println("长度:" + list.size());
结果
1.4、ArrayList 扩容机制
List<String> list = new ArrayList<>();
for (int i = 0; i < 11; i++) {
list.add("java");
}
//i = 11时
size = 10
ensureCapacityInternal(11)
进入ensureCapacityInternal()
elementData = 10
minCapacity = 11
calculateCapacity(elementData, minCapacity) // calculateCapacity(10, 11)
进入calculateCapacity()
判断结果为true,执行
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
return Math.max(DEFAULT_CAPACITY, minCapacity);
}
返回11
进入ensureExplicitCapacity()
ensureExplicitCapacity() //ensureExplicitCapacity(11)
minCapacity = 11;
modCount = 11
判断结果为true,执行
if (minCapacity - elementData.length > 0)
grow(minCapacity);
进入grow()
grow() //grow(11)
minCapacity = 11
oldCapacity = 10
newCapacity = 10 + 10 >> 1 //15
elementData = 15
然后下一步
elementData[size++] = e; //给赋值elementData[10]
return true;
1.5、使用反射测试扩容容量
List<String> list = new ArrayList<>();
for (int i = 0; i < 11; i++) {
list.add("java");
}
Class<? extends List> aClass = list.getClass();
Field elementData = aClass.getDeclaredField("elementData");
// 设置权限
elementData.setAccessible(true);
Object[] arr = (Object[]) elementData.get(list);
System.out.println("容量:" + arr.length);
System.out.println("长度:" + list.size());
1.6、测试数组定义时容量
List<String> list = new ArrayList<>();
Class<? extends List> aClass = list.getClass();
Field elementData = aClass.getDeclaredField("elementData");
// 设置权限
elementData.setAccessible(true);
Object[] arr = (Object[]) elementData.get(list);
System.out.println("容量:" + arr.length);
System.out.println("长度:" + list.size());
结果
1.7、remove
remove
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
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
}
System.arraycopy(elementData, index+1, elementData, index,
numMoved);
System.arraycopy(原数组, 从元数据起始位置开始, 目标数组, 目标数组的起始位置,
要copy数组的长度);
二、LinkedList实现原理
底层基于链表结构
数组与链表结构区别
- 数组:保证元素有序性,可以基于下标查询,时间复杂度:O(1)
- 链表:单向链表和双向链表,可以基于下标查询,时间复杂度:O(n)
- 数组适合于:基于下标查询arrays[1]增删有可能会对我们数组实现移动,效率非常低。
- 链表适合于:增删,只需要该引用指针关系
HashMap和LinkedList对比 - HashMap底层单链表,不能保证key有序性
- LinkedList底层双向链表,保证key有序性
LInkedList为什么需要用一个变量记录下头结点
- 后期遍历数据知道从哪里开始
2.1、ArrayList 父类和实现接口
## 2.2、add方法实现
public boolean add(E e) {
linkLast(e);
return true;
}
进入linkLast(e)方法
void linkLast(E e) {
final Node<E> l = last;
final Node<E> newNode = new Node<>(l, e, null);
last = newNode;
if (l == null)
first = newNode;
else
l.next = newNode;
size++;
modCount++;
}
第一次添加
last保存的是最后一个节点
final Node l = last;
创建一个新节点 ,上个节点指向l
final Node newNode = new Node<>(l, e, null);
如果最后一个节点为null,说明是第一次添加
first = newNode
把他设为第一个节点
transient int size = 0;
size++; size=1
protected transient int modCount = 0;
modCount++; modCount=1
第二次添加
final Node newNode = new Node<>(l, e, null);
l指向的是第一次添加
if判断执行else操作,将上次添加的的节点的尾指针当前节点
size++; size = 2
modCount++; modCount = 2
2.3、get方法实现
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
进入checkElementIndex(index)方法
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
判断索引是否越界
进入isElementIndex(index)
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
判断索引是否大于或等于0并且小于size
如果为false,抛出索引越界异常
如果为true,进入node(index),这里使用折半算法,时间复杂度O(logN),找到节点,返回节点的item属性
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
2.3、remove方法实现
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
首先进入checkElementIndex(index),判断索引是否越界,越界抛出索引越界异常
然后进入node(index)方法,使用折半算法,查找到节点
Node<E> node(int index) {
// assert isElementIndex(index);
if (index < (size >> 1)) {
Node<E> x = first;
for (int i = 0; i < index; i++)
x = x.next;
return x;
} else {
Node<E> x = last;
for (int i = size - 1; i > index; i--)
x = x.prev;
return x;
}
}
再进入unlink()方法
unlink(Node<E> x) {
// assert x != null;
final E element = x.item;
final Node<E> next = x.next;
final Node<E> prev = x.prev;
//头节点指向的节点为null,将first改为尾指针指向的节点,否则指向尾指针指向的节点
//如果尾指针指向的节点为null,说明当前节点是尾节点,将头指针指向的节点赋值给last,否则将头指针指向的节点和尾指针指向当前指针尾节点指向的节点
//将item设为null,size减1,modCount加1;
//返回当前节点
if (prev == null) {
first = next;
} else {
prev.next = next;
x.prev = null;
}
if (next == null) {
last = prev;
} else {
next.prev = prev;
x.next = null;
}
x.item = null;
size--;
modCount++;
return element;
}
十、总结
ArrayList 的内部实现,其实是用一个对象数组进行存放具体的值,然后通过Arrays.copy()扩容,进行数组的动态增长。
- 定义时容量为0
- 初始化容量为10
- 扩容为原来容量的1.5倍,(当前容量 + 当前容量 >> 1)
- 存放元素有序
- 线程不安全
- 基于数组实现 类型Object类型
时间复杂度 o(1)/o(n)/o(log n )
- o(1) 只需要通过查询一次就能找到元素 get(index) 基于下标查询
- o(n) 需要从头查询到尾部 例如根据元素值查询 链表
- o(log n) 二叉树 红黑树
数组:时间复杂度o(1) 基于下标查询
链表:时间复杂度o(n)
ArrayList和Vector的区别
- 相同点
都是基于数组实现,默认初始容量为10 - 不同点
ArrayList线和不安全,Vector线程是安全的
扩容ArrayList是1.5倍,Vector是2倍