一:LinkedList源码分析
1.1 成员变量
//长度
transient int size = 0;
//第一个节点
transient Node<E> first;
//最后一个节点
transient Node<E> last;
静态内部类
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;
}
}
1.2 构造方法
//无参构造方法
public LinkedList() {
}
//使用指定 Collection 来构造 ArrayList
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
1.3 add操作
在链表末尾添加元素:
public boolean add(E e) {
linkLast(e);
return true;
}
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++;
}
先把e的前驱节点置为当前链表的最后一个节点,后置节点置为null;然后判断最后一个节点是否为null,如果为null则把e当作第一个节点,否则最后一个节点的next指向e。
在指定位置添加元素:
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, 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;
}
}
void linkBefore(E e, Node<E> succ) {
// assert succ != null;
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++;
}
先找到index位置的节点,把e的前驱节点设置为index节点的前驱节点,把e的后置节点设置为index位置的节点。
1.4 get操作
public E get(int index) {
checkElementIndex(index);
return node(index).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;
}
}
如果index小于链表长度的一半,则从头节点开始往后遍历,否则从尾节点往前遍历。
1.5 remove操作
删除指定位置的元素:
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;
}
index节点的前驱节点prev,prev节点的后置节点设置为index节点的后置节点next,断开index节点的前驱指针;index的后置节点next,next节点的前驱节点设置为index节点的前驱节点prev,断开index节点的后置指针,index节点的值设置为null。
删除元素:
public boolean remove(Object o) {
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
从头结点遍历链表,比较节点的值是否相等,相等的话删除。
1.6 pop、poll、push、peek、offer
unlinkFirst(断开第一个节点) | linkFirst(成为第一个节点) | linkLast(成为最后一个节点) | getFirst(得到第一个节点的值) |
---|---|---|---|
pop() 如果第一个是null的话会报错 | push() | offer(E e) | peek() |
poll() 如果第一个是null就返回null | |||
remove() 如果第一个是null的话会报错 |
1.7 遍历
public static void main(String[] args) {
LinkedList<Integer> list = getLinkedList();
//通过快速随机访问遍历LinkedList
listByNormalFor(list);
//通过增强for循环遍历LinkedList
listByStrongThenFor(list);
//通过快迭代器遍历LinkedList
listByIterator(list);
}
/**
* 构建一个LinkedList集合,包含元素50000个
* @return
*/
private static LinkedList<Integer> getLinkedList() {
LinkedList list = new LinkedList();
for (int i = 0; i < 50000; i++){
list.add(i);
}
return list;
}
- 普通for循环
/**
* 通过快速随机访问遍历LinkedList
*/
private static void listByNormalFor(LinkedList<Integer> list) {
// 记录开始时间
long start = System.currentTimeMillis();
int size = list.size();
for (int i = 0; i < size; i++) {
list.get(i);
}
// 记录用时
long interval = System.currentTimeMillis() - start;
System.out.println("listByNormalFor:" + interval + " ms");
}
- 增强型for循环
/**
* 通过增强for循环遍历LinkedList
* @param list
*/
public static void listByStrongThenFor(LinkedList<Integer> list){
// 记录开始时间
long start = System.currentTimeMillis();
for (Integer i : list) { }
// 记录用时
long interval = System.currentTimeMillis() - start;
System.out.println("listByStrongThenFor:" + interval + " ms");
}
- Iterator迭代器
/**
* 通过快迭代器遍历LinkedList
*/
private static void listByIterator(LinkedList<Integer> list) {
// 记录开始时间
long start = System.currentTimeMillis();
for(Iterator iter = list.iterator(); iter.hasNext();) {
iter.next();
}
// 记录用时
long interval = System.currentTimeMillis() - start;
System.out.println("listByIterator:" + interval + " ms");
}
用时对比:
listByNormalFor:1042 ms
listByStrongThenFor:8 ms
listByIterator:4 ms
普通for循环的用时远大于迭代器的遍历。
二、LinkedList常见面试问题
2.1 LinkedList和ArrayList的区别?
- ArrayList是数组,内存连续;LinkedList是节点,内存不连续。
- 正是因为内存连续,所以ArrayList查找的时间复杂度是O(1),插入删除的时间复杂度是O(n);LinkedList查找的时间复杂度是O(n),插入删除的时间复杂度是O(1)。
2.2 如何用LinkedList实现堆栈和队列的数据结构?
public class MyStack {
private LinkedList list;
public MyStack() {
this.list = new LinkedList();
}
public void push(Object o) {
this.list.addLast(o);
}
public Object pop() {
//堆栈
return this.list.removeLast();
//队列
//return this.list.removeFirst();
}
public boolean isEmpty() {
return this.list.isEmpty();
}
}
2.3 为什么用普通for循环遍历比迭代器要慢?
因为普通for循环每一次循环都要从头节点开始遍历,而迭代器每取一个元素就将游标指向下一个节点,根据游标可以直接得到下一个元素。
三、总结
- LinkedList是双向链表,内存地址不连续,查找的时间复杂度是O(n),插入删除的时间复杂度是O(1)。
- 尽量不使用普通for循环遍历LinkedList。