ArrayList源码分析
自定义List集合接口
package com.mayikt.ext;
/**
* @Description: 自定义List集合
* @Author: ChenYi
* @Date: 2020/06/09 20:43
**/
public interface MayiktList<E> {
/**
* 集合的大小
*
* @return
*/
int size();
/**
* 往集合中添加元素
*
* @param e
* @return
*/
boolean add(E e);
/**
* 通过索引获取集合的元素
*
* @param index
* @return
*/
E get(int index);
}
自定义ArrayList类
package com.mayikt.ext.impl;
import com.mayikt.ext.MayiktList;
import java.util.Arrays;
/**
* @Description: 自定义ArrayList
* @Author: ChenYi
* @Date: 2020/06/09 20:46
**/
public class MayiktArrayList<E> implements MayiktList<E> {
/**
* 初始化空的数组
*/
private static final Object[] DEFAULTCAPACITY_EMPTY_ELEMENTDATA = {};
/**
* 数组的容量,transient代表该变量不能被序列化
*/
transient Object[] elementData;
/**
* 集合的大小
*/
private int size;
/**
* ArrayList默认的初始容量
*/
private static final int DEFAULT_CAPACITY = 10;
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
protected transient int modCount = 0;
public MayiktArrayList() {
this.elementData = DEFAULTCAPACITY_EMPTY_ELEMENTDATA;
}
@Override
public int size() {
return this.size;
}
@Override
public boolean add(E e) {
//初始化集合的大小
ensureCapacityInternal(size + 1);
//赋值
elementData[size++] = e;
return true;
}
@Override
public E get(int index) {
return (E) elementData[index];
}
@Override
public E remove(int index) {
//检测数组是否越界
rangeCheck(index);
//保证线程安全性
modCount++;
E oldValue = get(index);
int numMoved = size - index - 1;
//检测如果是删除最后一位的话直接把最后一位置空
if (numMoved > 0) {
System.arraycopy(elementData, index + 1, elementData, index,
numMoved);
}
//最后索引位置置空
elementData[--size] = null;
return oldValue;
}
private void ensureCapacityInternal(int minCapacity) {
if (elementData == DEFAULTCAPACITY_EMPTY_ELEMENTDATA) {
//没有指定集合容量的大小并且是第一次添加元素的时候进来
//minCapacity=0+1 DEFAULT_CAPACITY=10
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity);
}
//第一次初始化的集合的大小为10
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
//保证线程的安全性,如果同时写入的话就可能报错
modCount++;
//判断数组是否需要扩容
if (minCapacity - elementData.length > 0) {
grow(minCapacity);
}
}
/**
* 判断数组是否越界
*
* @param index
*/
private void rangeCheck(int index) {
if (index >= size) {
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
}
private String outOfBoundsMsg(int index) {
return "Index: " + index + ", Size: " + size;
}
private void grow(int minCapacity) {
// 第一次elementData为空数组 0
int oldCapacity = elementData.length;
/**
* >>位运算 向右移动相当于/2 左移相当于*2
* 0+0/2=0
*/
int newCapacity = oldCapacity + (oldCapacity >> 1);
// 0-10
if (newCapacity - minCapacity < 0) {
//第一次扩容的时候才进来 10
newCapacity = minCapacity;
}
//集合的最大容量是2 的21次方
if (newCapacity - MAX_ARRAY_SIZE > 0) {
newCapacity = hugeCapacity(minCapacity);
}
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) {
throw new OutOfMemoryError();
}
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
}
总结:
- 使用无参构建函数new出ArrayList集合的时候,数组的容量是0,elementData是一个空的数组
- 第一次添加的时候才会初始化数组的大小,会调用grow方法进行扩容,初始化的大小为10
- 每次扩容的时候,数组的长度增加0.5倍
- 数组最大的长度是Interger的最大值
- 集合删除元素之后,数组是不会缩容的,对应的位置置为null
- 由于ArrayList是线程不安全的,在高并发的情况下会出现Fail-Fast,所以里面用了一个modCount来保证线程的安全。
CopyOnWriteArrayList源码分析
自定义的CopyOnWriteArrayList类
package com.mayikt.ext.impl;
import com.mayikt.ext.MayiktList;
import java.util.Arrays;
import java.util.concurrent.locks.ReentrantLock;
/**
* @Description: 自定义CopyOnWriteArrayList
* @Author: ChenYi
* @Date: 2020/06/11 07:46
**/
public class MayiktCopyOnWriteArrayList<E> implements MayiktList<E> {
final transient ReentrantLock lock = new ReentrantLock();
private transient volatile Object[] array;
@Override
public int size() {
return 0;
}
@Override
public boolean add(E e) {
final ReentrantLock lock = this.lock;
//加lock能够保证线程的安全
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
//每次添加都要把之前的数组复制到新的数组中
Object[] newElements = Arrays.copyOf(elements, len + 1);
newElements[len] = e;
setArray(newElements);
return true;
} finally {
lock.unlock();
}
}
final Object[] getArray() {
return array;
}
public MayiktCopyOnWriteArrayList() {
setArray(new Object[0]);
}
final void setArray(Object[] a) {
array = a;
}
@Override
public E get(int index) {
return get(getArray(), index);
}
private E get(Object[] a, int index) {
return (E) a[index];
}
@Override
public E remove(int index) {
final ReentrantLock lock = this.lock;
lock.lock();
try {
Object[] elements = getArray();
int len = elements.length;
E oldValue = get(elements, index);
int numMoved = len - index - 1;
if (numMoved == 0) {
setArray(Arrays.copyOf(elements, len - 1));
} else {
Object[] newElements = new Object[len - 1];
System.arraycopy(elements, 0, newElements, 0, index);
System.arraycopy(elements, index + 1, newElements, index,
numMoved);
setArray(newElements);
}
return oldValue;
} finally {
lock.unlock();
}
}
}
总结:
- 使用无参构建对象的时候,默认的数组也是空的
- 每次添加元素的时候都会加Lock锁,原来的数组长度加1,创建出新的数组,然后把之前的值放进去,没有存在扩容的操作。
- 读的时候是没有加Lock锁的,所以读取的时候效率比较快。
Vector源码解析
自定义的Vector类
package com.mayikt.ext.impl;
import com.mayikt.ext.MayiktList;
import java.util.Arrays;
/**
* @Description: 自定义Vector集合
* @Author: ChenYi
* @Date: 2020/06/11 08:19
**/
public class MayiktVector<E> implements MayiktList<E> {
protected int capacityIncrement;
protected Object[] elementData;
protected transient int modCount = 0;
protected int elementCount;
private static final int MAX_ARRAY_SIZE = Integer.MAX_VALUE - 8;
public MayiktVector() {
this(10);
}
public MayiktVector(int initialCapacity) {
this(initialCapacity, 0);
}
public MayiktVector(int initialCapacity, int capacityIncrement) {
super();
if (initialCapacity < 0) {
throw new IllegalArgumentException("Illegal Capacity: " +
initialCapacity);
}
this.elementData = new Object[initialCapacity];
this.capacityIncrement = capacityIncrement;
}
@Override
public int size() {
return 0;
}
@Override
public synchronized boolean add(E e) {
modCount++;
ensureCapacityHelper(elementCount + 1);
elementData[elementCount++] = e;
return true;
}
private void ensureCapacityHelper(int minCapacity) {
// overflow-conscious code
if (minCapacity - elementData.length > 0) {
grow(minCapacity);
}
}
private void grow(int minCapacity) {
// overflow-conscious code
int oldCapacity = elementData.length;
int newCapacity = oldCapacity + ((capacityIncrement > 0) ?
capacityIncrement : oldCapacity);
if (newCapacity - minCapacity < 0) {
newCapacity = minCapacity;
}
if (newCapacity - MAX_ARRAY_SIZE > 0) {
newCapacity = hugeCapacity(minCapacity);
}
elementData = Arrays.copyOf(elementData, newCapacity);
}
private static int hugeCapacity(int minCapacity) {
if (minCapacity < 0) {
throw new OutOfMemoryError();
}
return (minCapacity > MAX_ARRAY_SIZE) ?
Integer.MAX_VALUE :
MAX_ARRAY_SIZE;
}
@Override
public synchronized E get(int index) {
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index);
}
return elementData(index);
}
E elementData(int index) {
return (E) elementData[index];
}
@Override
public synchronized E remove(int index) {
modCount++;
if (index >= elementCount) {
throw new ArrayIndexOutOfBoundsException(index);
}
E oldValue = elementData(index);
int numMoved = elementCount - index - 1;
if (numMoved > 0) {
System.arraycopy(elementData, index + 1, elementData, index,
numMoved);
}
elementData[--elementCount] = null;
return oldValue;
}
}
总结:
- 使用无参构建创建对象的时候默认创建容量为10的数组
- 存在扩容的原理,跟ArrayList一样,只是线程是安全的,在修改或者读取数据的时候都加了synchronized关键字,效率很低,capacityIncrement没有指定的时候,扩容默认是扩大一倍的
LinkeList源码分析
自定义的LinkList类
package com.mayikt.ext.impl;
import com.mayikt.ext.MayiktList;
import java.util.Objects;
/**
* @Description:自定义LinkList集合
* @Author: ChenYi
* @Date: 2020/06/20 09:11
**/
public class MayiktLinkList<E> implements MayiktList<E> {
transient int size = 0;
transient MayiktLinkList.Node<E> first;
transient MayiktLinkList.Node<E> last;
protected transient int modCount = 0;
public MayiktLinkList() {
}
@Override
public int size() {
return size;
}
@Override
public boolean add(E e) {
linkLast(e);
return true;
}
private void linkLast(E e) {
final MayiktLinkList.Node<E> l = last;
final MayiktLinkList.Node<E> newNode = new MayiktLinkList.Node<>(l, e, null);
last = newNode;
if (l == null) {
//链表的第一个元素
first = newNode;
} else {
l.next = newNode;
}
size++;
modCount++;
}
@Override
public E get(int index) throws Exception {
checkElementIndex(index);
return (E) node(index).item;
}
private MayiktLinkList.Node node(int index) {
//采用折半查找
MayiktLinkList.Node x;
if (index <= size >> 1) {
x = first;
for (int i = 0; i < index; i++) {
x = x.next;
}
} else {
x = last;
for (int i = size - 1; i > index; i--) {
x = x.prev;
}
}
return x;
}
private void checkElementIndex(int index) throws Exception {
if (isElementIndex(index)) {
return;
}
throw new Exception("数组越界!!!");
}
private boolean isElementIndex(int index) {
return index >= 0 && index < size;
}
@Override
public E remove(int index) throws Exception {
checkElementIndex(index);
return unlink(node(index));
}
private E unlink(Node current) {
//要删除节点的上节点
Object item = current.item;
Node prev = current.prev;
//要删除节点的下节点
Node next = current.next;
//说明删除的是第一个元素
if (Objects.isNull(prev)) {
first = next;
} else {
prev.next = next;
//设置为空给垃圾回收
current.prev = null;
}
//说明删除最后一个元素
if (Objects.isNull(next)) {
last = prev;
} else {
next.prev = prev;
//设置为空给gc回收
current.next = null;
}
//设置为空
current.item = null;
size--;
modCount++;
return (E) item;
}
private static class Node<E> {
E item;
MayiktLinkList.Node<E> next;
MayiktLinkList.Node<E> prev;
Node(MayiktLinkList.Node<E> prev, E element, MayiktLinkList.Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
}
总结:
- 底层是采用双链表的数据结构来存储数据的,底层采用了静态内部类Node节点存放节点元素,包含指向上个节点的指针prev,当前的数据item,下个节点的指针next,链表是不需要扩容的,因为长度是可以不断变化的,数组的数据结构才需要扩容
- add原理是一直在链表之后添加
- get的原理是采用折半查询,查询效率比较低,时间复杂度是O(n/2)
- remove的原理是改变上下节点的指针
参考:来自蚂蚁课堂