在数组/ArrayList中读取和存储(get/set)的性能非常高,为O(1),但插入(add(int index, E element))和删除(remove(int index))却花费了O(N)时间,效率并不高。
今天我们来看Java中的另一种List即LinkedList,LinkedList是基于双向链表来实现的
LinkedList
1.内部类Node
节点有两个指针,说明是双向链表
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;
}
}
2.删除操作
2.1 按元素删除
假设从找到被删除元素这刻开始算起:
- ArrayList删除元素后,底层数组要往前复制一格,ArrayList底层数组删除元素时间复杂度为O(n)。
- LinkedList底层链表删除元素只是简单的修改了一下引用地址,时间复杂度为O(1)
但是LinkedList也要遍历寻找该元素,所以总的时间复杂度还是O(n)
2.2 按索引删除
public E remove(int index) {
checkElementIndex(index);
return unlink(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;
}
}
- ArrayList是O(1)
- LinkedList是O(n)
3.添加操作
public boolean add(E e) {
linkLast(e);
return true;
}
- O(1)
4.get操作
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
- O(n)
5.set操作
public E set(int index, E element) {
checkElementIndex(index);
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
- O(n)
6. 实现的接口
除了实现 List 接口外,LinkedList类还实现了Deque接口,可以用作双向队列、单项队列、栈 来使用
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable{
...
transient int size = 0;
transient Node<E> first;
transient Node<E> last;
...
}
6.1 作为List的常用方法
增
public boolean add(E e) {
linkLast(e);
return true;
}
//add(E e):默认添加元素是在尾部添加
删
public boolean remove(Object o) {...}
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
改
public E set(int index, E element) {
checkElementIndex(index);
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
查
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
6.2 做栈使用
JDK1.6之后LinkedList实现了Deque接口。双端队列也可用作 LIFO(后进先出)的栈。
如果要使用堆栈结构的集合,可以考虑使用LinkedList,而不是Stack
从Deque继承来的栈的方法 LinkedList作为双向链表自身的等效方法;这些方法也继承自Deque接口
push(e) addFirst(e)
pop() removeFirst()
peek() peekFirst()
public static void main(String[] args) {
LinkedList list = new LinkedList();
//入栈
list.addFirst(1);
list.addFirst(2);
list.addFirst(3);
//出栈: LIFO(后进先出)
System.out.println(list.removeFirst());//3
System.out.println(list.removeFirst());//2
System.out.println(list.removeFirst());//1
//栈空了,会报异常java.util.NoSuchElementException
System.out.println(list.removeFirst());
}
6.3 作为队列使用
用作队列时,将得到 FIFO(先进先出)行为。将元素添加到双端队列的末尾,从双端队列的开头移除元素。
Queue 方法 等效 Deque 方法
add(e):队尾入队 addLast(e)
offer(e):队尾入队 offerLast(e)
remove():队头出队 removeFirst()
poll():队头出队 pollFirst()
element():查询队头 getFirst()
peek():查询队头 peekFirst()
注意:作为队列使用时,入队是在last添加,出队是从first删。
remove()方法时删除first元素