在JDK中,实现了链表这种数据结构,就是LinkedList,它是双向链表的实现。首先看看它的继承层次。了解它的功能:
- LinkeList继承的抽象类是AbstractSequentialList,在这个抽象类中有几个随机访问元素的方法,这与LinkedList基于链表实现相关,因为链表可以快速的add和remove而无需移动元素。上源码:
/**
* 在指定的index插入一个元素,原来位置的元素往后靠
*
*/
public void add(int index, E element) {
try {
// 调用listIterator的add方法,如果没有实现就会报错 UnsupportedOperationException
listIterator(index).add(element);
} catch (NoSuchElementException exc) {
throw new IndexOutOfBoundsException("Index: "+index);
}
}
/**
*
*/
public E remove(int index) {
try {
// 根据index的到listIterator
ListIterator<E> e = listIterator(index);
// 这里注意,listIterator使用remove之前,需要next调用一下
E outCast = e.next();
e.remove();
return outCast;
} catch (NoSuchElementException exc) {
throw new IndexOutOfBoundsException("Index: "+index);
}
}
- AbstractSequentialList 的add和remove方法都是调用ListIterator 声明的,实现是根据集合的类型来的,如果没有实现的话,就会抛出UnsupportedOperationException。
- 但是在LinkeList 中,针对这些方法都重新实现了:
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
如果插入的index和集合的大小相等,那么直接加在后面
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++;
}
如果不是最后一个那么就需要加在中间的位置:
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++;
}
这里还需要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;
}
}
remove方法和add方法实现是差不多的,我们可以看出LinkedList的add和remove方法都针对链表的特性重写了。
从继承层次中我们可以看到LinkedList还实现了Deque接口,而这个接口来自于Queue,在数据结构中,队列是一种先进先出的数据结构,看看在JDK中,定义了哪些方法:
public interface Queue<E> extends Collection<E> {
// 添加一个元素到队列尾,容量不够时抛出异常
boolean add(E e);
// 添加一个元素到队列尾,容量不够返回false
boolean offer(E e);
// 返回并删除队列头的元素,为空时返回null
E remove();
// 返回并删除队列头的元素。 如果为为空抛出异常
E poll();
// 返回但不删除队列的头元素,为空时抛出异常
E element();
// 返回但不删除队列的头元素,为空时返回null
E peek();
}
- 添加,删除,返回对头都有两个方法,注意区别就是是否在空时抛出异常还是返回null
Deque继承了Queue接口,并在次基础上添加了在队列两端都可以访问的方法,是一个双向队列。然后在LinkedList中使用链表做了实现。
还有一点就是,我们知道链表的特点就是可以快速的随机add和remove,但是对于随机的get和set,就需要使用迭代器遍历链表知道找到,在JDK中,LinkedList对于set和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;
}
}
- 在node方法中,如果index小于size /2 ,那么从前往后找,否则就从后往前找。
在继承层次中,LinkedList还是实现了Cloneable接口,表示一个链表是可以克隆的,看看重写的clone方法:
public Object clone() {
LinkedList<E> clone = superClone();
// Put clone into "virgin" state
clone.first = clone.last = null;
clone.size = 0;
clone.modCount = 0;
// Initialize clone with our elements
for (Node<E> x = first; x != null; x = x.next)
clone.add(x.item);
return clone;
}
private LinkedList<E> superClone() {
try {
return (LinkedList<E>) super.clone();
} catch (CloneNotSupportedException e) {
throw new InternalError(e);
}
}
- 根据Object类的clone方法创建了一个LinkedList,然后再依次把每个元素复制到新集合中并返回,并且是浅拷贝的。
其他的就都是一些集合通用的方法了,在Collection接口或者在AbstractCollection中实现的。