ArrayList和LinkedList源码分析:
package easyExcel;
import java.io.FileNotFoundException;
import java.util.ArrayList;
import java.util.LinkedList;
public class App
{
public static void main( String[] args ) throws FileNotFoundException {
//从构造函数可以看出 ArrayList 底层是由数组构成的
//LinkedList 源码分析
/**
无参构造方法
public ArrayList() {
super();
this.elementData = EMPTY_ELEMENTDATA; //初始化
}
Object[] elementData //保存数组元素
Object[] EMPTY_ELEMENTDATA = {};
*/
ArrayList list = new ArrayList<>();
//添加数据
/**
public boolean add(E e) {
ensureCapacityInternal(size + 1); // 判断是否扩容 以及容器的初始化大小
elementData[size++] = e; //存储元素
return true;
}
private static final int DEFAULT_CAPACITY = 10;//容器默认大小
private void ensureCapacityInternal(int minCapacity) {
if (elementData == EMPTY_ELEMENTDATA) { //如果数组为空数组
minCapacity = Math.max(DEFAULT_CAPACITY, minCapacity); //取最大值 设置数组容量
}
ensureExplicitCapacity(minCapacity);
}
private void ensureExplicitCapacity(int minCapacity) {
modCount++;
if (minCapacity - elementData.length > 0) //判断是否扩容 若为空数组 满足 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) //新的容器容量 小于 minCapacity容量 ----对应的数组的初始化
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); //实际扩容操作
}
*/
list.add("1");
//根据索引获取数据
/**
public E get(int index) {
rangeCheck(index); //索引校验
return elementData(index);
}
E elementData(int index) {
return (E) elementData[index]; //根据数组的下标获取数据
}
*/
list.get(1);
// 在中间插入元素
/**
public void add(int index, E element) {
rangeCheckForAdd(index); //校验索引
ensureCapacityInternal(size + 1); // 判断是否需要扩容 见上
arraycopy(Object src, int srcPos,Object dest, int destPos,int length);
@param src the source array. 源数组
@param srcPos starting position in the source array. 开始的位置
@param dest the destination array. 目标数组
@param destPos starting position in the destination data. 在目标数组数组什么位置开始复制
@param length the number of array elements to be copied. 复制的长度
System.arraycopy(elementData, index, elementData, index + 1,
size - index); // 次操作 把index位置空出来 导致index开始所有的数据都要向后移动一位
elementData[index] = element; //将值填充进去
size++; //数组大小加1
}
*/
list.add(1,"2");
//根据索引移除
/**
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);//将index后面的元素逐个向前移动一位
elementData[--size] = null; // clear to let GC do its work
return oldValue;
}
*/
list.remove(1);
//根据元素移除
/**
public boolean remove(Object o) {
if (o == null) { //o为空
for (int index = 0; index < size; index++)
if (elementData[index] == null) { //找到所有为null的元素
fastRemove(index); //删除元素
return true;
}
} else { // o不为null
for (int index = 0; index < size; index++) //遍历所有的元素找出
if (o.equals(elementData[index])) { //找出 元素等于o的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 and size-1
}
*/
list.remove("3");
//是否包含
/**
public boolean contains(Object o) {
return indexOf(o) >= 0;
}
public int indexOf(Object o) {
if (o == null) { //o为null
for (int i = 0; i < size; i++) //循环
if (elementData[i]==null) //判断有一个为null 怎返回
return i;
} else { //o不为null
for (int i = 0; i < size; i++) //循环
if (o.equals(elementData[i])) // 找出与o相等的元素 然后返回
return i;
}
return -1;
}
*/
list.contains("3");
//LinkedList 源码分析
LinkedList linkedList = new LinkedList();
//添加数据
/**
private static class Node<E> {
E item;
Node<E> next;
Node<E> prev;
Node(Node<E> prev, E element, Node<E> next) {
this.item = element;
this.next = next;
this.prev = prev;
}
}
在LinkedList内部中
transient int size = 0; 元素个数
transient Node<E> first; 首节点
transient Node<E> last; 尾节点
*/
//在linkedList 内部有一个内部类 Node 节点
//节点保存的是当前元素,以及前一个节点和后一个节点
/**
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++;
}
添加节点的时候
① Links e as last element 将元素作为尾节点添加 将该元素封装成一个节点 并指向上一个节点
②再判断尾节点是否为null 为null 将该元素设置为首节点
③否则将该节点作为原尾节点的下一节点 形成一个双向链表
*/
linkedList.add(1);
//按索引获取数据
/**
Node<E> node(int 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;
}
}
①二分法查找方 index < (size >> 1)
②循环遍历 找到对应索引的元素节点
*/
linkedList.get(1);
//按索引添加数据
/**
向链表中间插入元素
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
①判断插入的位置 是否在最后位置插入
②就添加到将该元素放到尾节点
③否则 在中间插入
linkBefore(element, node(index)); node(index)获取该索引下的节点
void linkBefore(E e, Node<E> succ) {
final Node<E> pred = succ.prev; 索引前置节点 本身成为插入元素的后置节点
final Node<E> newNode = new Node<>(pred, e, succ);
succ.prev = newNode;
if (pred == null)
first = newNode;
else
pred.next = newNode;
size++;
modCount++;
}
封装节点设置该元素的前置节点和后置节点
并且
将对应索引节点的前一个节点 设置成这个新节点
对应索引的前一个节点的下一个节点 设置成这个新节点
*/
linkedList.add(1,99);
//按索引删除数据
/**
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
E unlink(Node<E> x) {
// assert x != null;
final E element = x.item; //索引元素
final Node<E> next = x.next; //后一个节点 后置节点
final Node<E> prev = x.prev; //前一个节点 前置节点
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;
}
*/
linkedList.remove(1);
// 判断元素是否存在
/**
public int indexOf(Object o) {
int index = 0;
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) { //链表遍历
if (x.item == null) //找出元素为null的
return index;
index++;
}
} else {
for (Node<E> x = first; x != null; x = x.next) { //链表遍历
if (o.equals(x.item)) //找出相等的元素
return index;
index++;
}
}
return -1;
}
*/
linkedList.contains(1);
}
}
总结:
ArrayList:底层是由动态数组组成。
LinkedList:底层是双向列表构成,每一个节点(头尾节点除外)都有指向前一个节点,和下一个节点的引用。
add方法:
ArrayList:若是无参构造方法,会先初始化一个空的数组,添加的时候初始换数组大小默认数组长度为10。
LinkedList:是构建一个Node节点,作为尾节点添加到链表中去(若尾节点为null,则头尾节点为同一个节点)会建立该节点的前节点的引用,以及原尾节点下一个节点的引用关系。
and(int index)中间添加元素
ArrayList:先判断是否扩容,然后将index后面的元素整体后移一位后,然后将值复制的index位置
LinkedList:现根据索引找到该索引的节点A,获取他前一个节点B,构建新的元素C,C节点的前一个节点指向B,B的下一个节点指向C,C的下一个节点指向B,B的前一个节点指向C
remove方法 根据索引删除
ArrayList:会根据数组下表找到该节点Node,然后做赋值操作,将index后面的元素向前移动一个位置。
LinkedList:先通过二分法找到该节点(Node(index)方法),然后找到该节点的前一个节点A和后一个节点B,将A节点的下一个节点的引用指向B,B的前一个节点的引用指向A完成删除。
get方法 索引
ArrayList:根据数组下标直接获取
LinkedList:现根据二分法(size>>1)判断index所在的位置,然后遍历(0到index或size-1到index)找到对应的节点
contain方法
ArrayList:遍历数组,判断是否有元素等于指定的元素
LinkedList:遍历链表 找到是由有节点元素等于指定的元素
由上可知:,末尾删除元素ArrayList直接删除即可,LinkedList需要分配索引;尾节点添加元素(不需要扩容),ArrayList会直接存储元素,LinkedList需要分配索引;中间删除元素时LinkedList只需重新分配引用即可,ArrayList(不需要扩容,需要的话会扩容)索引后面的元素都要向前面移动一位;随机获取元素的时候ArrayList根据下标直接获取,LinkedList使用二分法遍历获取;中间添加元素LinkedList只需要重新分配引用即可,ArrayList 则要从index位置向后移动一位给插入的元素;尾节点插入元素ArrayList较快,LinkedList需要建立引用关系;ArrayList由于采用的是数组会浪费一定的空间(数组不一定存储满)