开篇寄语:与其期盼未来,不如专注当前。
总述: LinkedList是一种底层采用链表数据结构实现的List,它自然也具备List的特点,允许重复元素。
1. 类声明:
public class LinkedList
extends AbstractSequentialList
implements List
, Deque
, Cloneable, java.io.Serializable
2. 构造器
private transient Entry
header = new Entry
(null, null, null);
private transient int size = 0;
public LinkedList() {
header.next = header.previous = header;
}
public LinkedList(Collection
c) {
this();
addAll(c);
}
3. 节点类型
private static class Entry
{
E element;
Entry
next;
Entry
previous;
Entry(E element, Entry
next, Entry
previous) { this.element = element; this.next = next; this.previous = previous; } }
4. 添加元素
/**
* 在该List的首节点处,插入指定元素
*/
public void addFirst(E e) {
addBefore(e, header.next);
}
/**
* 在该List的尾节点处,插入指定元素
*/
public void addLast(E e) {
addBefore(e, header);
}
/**
* 默认增加到List的尾节点
*/
public boolean add(E e) {
addBefore(e, header);
return true;
}
/**
* 在该List的指定索引处,插入指定元素
*/
public void add(int index, E element) {
addBefore(element, (index==size ? header : entry(index)));
}
private Entry
addBefore(E e, Entry
entry) {
Entry
newEntry = new Entry
(e, entry, entry.previous);
newEntry.previous.next = newEntry;
newEntry.next.previous = newEntry;
size++;
modCount++;
return newEntry;
}
可以看出这三个方法封装的内聚性很好,符合单一职责原则,因此复用性很好。我们在平常的编码中,完全可以借鉴这种技巧。这样的代码,看起来就很漂亮。除了这三个方法,还有一些队列的常用操作实现,如:offer、push等等,会在后续队列的源码分析博文中,再详细介绍。
5. 删除元素
/**
* 删除List的首节点
*/
public E removeFirst() {
return remove(header.next);
}
/**
* 删除List的尾节点
*/
public E removeLast() {
return remove(header.previous);
}
/**
* 删除List指定索引处的元素
*/
public E remove(int index) {
return remove(entry(index));
}
/**
* 删除List的指定元素
*/
public boolean remove(Object o) {
if (o==null) {
for (Entry
e = header.next; e != header; e = e.next) {
if (e.element==null) {
remove(e);
return true;
}
}
} else {
for (Entry
e = header.next; e != header; e = e.next) {
if (o.equals(e.element)) {
remove(e);
return true;
}
}
}
return false;
}
private E remove(Entry
e) {
if (e == header)
throw new NoSuchElementException();
E result = e.element;
e.previous.next = e.next;
e.next.previous = e.previous;
e.next = e.previous = null;
e.element = null;
size--;
modCount++;
return result;
}
/**
* 返回指定索引处的元素
*/
private Entry
entry(int index) {
if (index < 0 || index >= size)
throw new IndexOutOfBoundsException("Index: "+index+
", Size: "+size);
Entry
e = header; if (index < (size >> 1)) { for (int i = 0; i <= index; i++) e = e.next; } else { for (int i = size; i > index; i--) e = e.previous; } return e; }
7. 时间复杂度
| 查询指定索引元素的时间复杂度为O(N),由于采用双向链表实现,因此在实际应用中,还是要比单向链表实现节省一半时间。
| 插入元素的时间复杂度为O(1);插入元素到指定索引处的时间复杂度为O(N),其实主要是查询索引的时间耗费,实际插入操作的时间复杂度为O(1),因为链表的结构决定了其 插入操作无需移动元素。
| 删除元素和插入一样。