LinkedList
源码基于1.8.0_112
LinkedList个人使用的不是很多,结构也比较简单。
原理 :LinkedList中使用了双向链表存储元素,链表这里就不在详述了。
成员变量
大致浏览,结合具体方法来了解具体含义
// 第一个节点
transient Node<E> first;
// 最后一个节点
transient Node<E> last;
// 容量
transient int size = 0;
// 修改次数,继承于AbstractList
protected transient int modCount = 0;
构造方法
先阅读默认构造函数,其他构造函数可在阅读完具体操作后再回来阅读
/**
* 默认构造函数
*/
public MyLinkedList() {
}
内部类
再看具体操作前,先看LinkedList的一个内部类
很简单的一个类,item存储对象,next指向下一个对象,prev指向上一个对象
新建Node是必须提供以上三个变量值
代码
/**
* 内部类
*
* @param <E>
*/
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;
}
}
add(E e)
插入过程其实就是Node的引用替换的过程,Node中item存储对象,prev和next相互引用。
图解
代码
/**
* 加入对象
*
* @param e
* @return
*/
public boolean add(E e) {
linkLast(e);
return true;
}
void linkLast(E e) {
// 取得最后一个节点
final Node<E> l = last;
// 构造一个新的Node,prev指向last,next为null
final Node<E> newNode = new Node<>(l, e, null);
// last变为最新添加的Node
last = newNode;
// 第一次添加时原本的last为null,所以第一个Node既是first也是last
if (l == null)
first = newNode;
else
l.next = newNode; // 如果不是第一次添加,则原本last的next指向新的Node
// 容量加1
size++;
modCount++;
}
get(int index)
获取对象,根据下标和容量,确定从前往后遍历,还从后往前遍历。最坏的情况是目标元素刚好在最中间,这样会遍历半个list,比较耗时。
代码
/**
* 获取对象
*
* @param index
* @return
*/
public E get(int index) {
// 判断index是否越界
checkElementIndex(index);
return node(index).item;
}
private void checkElementIndex(int index) {
if (!isElementIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
}
private boolean isElementIndex(int index) {
// 大于等于0,小于size
return index >= 0 && index < size;
}
private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
}
Node<E> node(int index) {
// 判断下标是否小于容量的一半,如果小于容量的一半,则从前往后查找,否则从后往前查找
// 查找过程很简单,即通过prev或者next遍历
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;
}
}
remove(Object o)
通过改变Node的引用来移除对象,图解为一般情况,不包括移除第一个或者最后一个对象
图解
代码
/**
* 移除节点
*
* @param index
* @return
*/
public E remove(int index) {
// 检查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) {
// x刚好是第一个元素,则第一个元素变为x的next
first = next;
} else {
// x的上一个的元素的next指向x的下一个元素,x的下一个元素置为空
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中使用了数组,所以具备链表的特性
- LinkedList一般会与ArrayList对比
- 插入,只需要改变引用,所以效率较高
- 随机访问,需要遍历链表,效率较低
- 删除,需要遍历,改变引用,相对于ArrayList的数组拷贝,效率应该还不错