LinkedList源码解析
底层数据结构—双向链表
如图所示,双向链表的存储结构,每个节点都由三部分组成,prev是指向上一个节点的指针域,item是存储数据的数据域,next是指向下一个节点的指针域。每个链表都有头节点first和尾节点last,头节点因为是第一个节点,所以在它的前面没有元素,first的prev为null,尾节点因为是最后一个节点,所以在它的后面没有元素,last的next为null。
为什么有了单向链表还要有双向链表?
双向链表是单链表的扩展,原单链表只能从前向后遍历,而双向链表还支持从后向前遍历,还有就是单链表要删除一个节点,需要遍历此节点前的所有节点,而双向链表因为可以双向遍历,所以在删除节点时,相比较而言可以减少遍历,提高效率。
双向链表的效率?
在表头表尾插入和删除元素速度很快,因为只需要修改一两个引用值,所以花费O(1)的时间。 平均起来,查找,删除和在指定的节点后面插入都需要搜索表中一半的节点。需要O(n)次比较。在数组中执行这些操作也需要O(n)次比较,但是因为链表不需要移动任何元素,所以链表更快一些。当然,链表比数组优越的的另一个地方在于,链表需要多少内存就可以用多少内存,并且可以扩展到所有可用内存,数组的大小在于它创建的时候就固定了;所以经常由于内存太小数组太大导致效率低下,或者数组太小导致空间溢出。 双向链表需要额外的两个空间来存储两个指针域(前驱节点和后继节点)。所以,如果存储同样多的数据,双向链表要比单链表要占用更多的内存空间。虽然两个指针比较浪费存储空间,但可以支持双向遍历,提高了链表的效率。
linkedList的优缺点?
优点:插入和删除时只需要添加或删除前后对象的引用,不需要像数组一样移动复制元素,插入删除较快 缺点:在内存中存储不连续,只能通过遍历查询,效率相对较低
linkedList属性
size表示链表长度,first表示头节点,last表示尾节点
transient int size = 0;
transient Node<E> first;
transient Node<E> last;
Node私有内部类
item 表示节点的数据域,next 表示节点指向下一个节点的指针域,prev 表示节点指向上一个节点的指针域
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;
}
}
linkedList构造方法
public LinkedList() {
}
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
getFirst(),getLast(),get(int index)
get(int index)实际是调用了node(int index)方法返回指定索引的值,如果索引在链表前半段,从前面遍历查找元素,否则从后面遍历查找元素。由此可见双向链表的遍历效率是优于单向链表的
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
Node<E> node(int 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;
}
}
set(int index)
将某个位置的元素重新赋值
public E set(int index, E element) {
checkElementIndex(index);
Node<E> x = node(index);
E oldVal = x.item;
x.item = element;
return oldVal;
}
addAll(Collection<? extends E> c)
实际上是调用了addAll(int index, Collection<? extends E> c)方法
public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index);
Object[] a = c.toArray();
int numNew = a.length;
if (numNew == 0)
return false;
Node<E> pred, succ;
if (index == size) {
succ = null;
pred = last;
} else {
succ = node(index);
pred = succ.prev;
}
for (Object o : a) {
@SuppressWarnings("unchecked") E e = (E) o;
Node<E> newNode = new Node<>(pred, e, null);
if (pred == null)
first = newNode;
else
pred.next = newNode;
pred = newNode;
}
if (succ == null) {
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}
size += numNew;
modCount++;
return true;
}
add(),add(int index, E element),addLast(E e),addFirst(E e)
add(int index, E element)将元素添加到指定位置,addFirst()方法将元素添加为头节点,addLast()方法将元素添加为尾节点。 linkBefore(E e, Node succ),e表示新增的节点,succ表示链表中已有的某个节点。 首先定义链表已有节点的头指针域为pred(原来succ的上一个节点),新节点newNode(即e),将succ链表的头指针指向新节点。 如果添加在原有链表的头节点前面,则新节点newNode设置为新的头节点。添加节点成功链表长度+1(size++)
public boolean add(E e) {
linkLast(e);
return true;
}
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
public void addLast(E e) {
linkLast(e);
}
public void addFirst(E e) {
linkFirst(e);
}
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) {
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++;
}
removeFirst(),removeLast(),remove(Object o),remove(int index)
removeFirst()删除第一个非空节点,removeLast()删除最后一个非空节点,remove(Object o)从此列表中删除指定元素的第一个匹配项,remove(int index)删除指定索引元素。
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
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;
}
public E removeFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return unlinkFirst(f);
}
public E removeLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return unlinkLast(l);
}
private E unlinkFirst(Node<E> f) {
final E element = f.item;
final Node<E> next = f.next;
f.item = null;
f.next = null;
first = next;
if (next == null)
last = null;
else
next.prev = null;
size--;
modCount++;
return element;
}
private E unlinkLast(Node<E> l) {
final E element = l.item;
final Node<E> prev = l.prev;
l.item = null;
l.prev = null;
last = prev;
if (prev == null)
first = null;
else
prev.next = null;
size--;
modCount++;
return element;
}
unlink(Node x)方法
首先需要定义被删除节点的指针域和数据域,element是数据域,next指向下一个节点的指针域,prev指向上一个节点的指针域。 开始判断需要删除的节点是头节点还是尾节点,如果prev为空则表明该被删除的节点是头节点,头节点被删除直接定义下个节点为新的头节点。 如果不是头节点,则将上一个节点的next指针域指向当前被删除节点的next,并取消当前被删除节点和上一个节点的链接。 将被删除节点的数据域设置为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;
}
public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
E unlink(Node<E> x) {
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;
}
contains(Object o)方法
判断链表中是否存在该元素,存在返回true,否则false。使用equals判断。
public boolean contains(Object o) {
return indexOf(o) != -1;
}
public int indexOf(Object o) {
int index = 0;
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null)
return index;
index++;
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item))
return index;
index++;
}
}
return -1;
}
clear()方法
从此列表中删除所有元素。此调用返回后,列表将为空。为了让GC回收更快,需要将每个node节点关系置空。
public void clear() {
for (Node<E> x = first; x != null; ) {
Node<E> next = x.next;
x.item = null;
x.next = null;
x.prev = null;
x = next;
}
first = last = null;
size = 0;
modCount++;
}
Queue 方法
public E peek() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
public E element() {
return getFirst();
}
public E poll() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
public E remove() {
return removeFirst();
}
public boolean offer(E e) {
return add(e);
}
Deque方法
public boolean offerFirst(E e) {
addFirst(e);
return true;
}
public boolean offerLast(E e) {
addLast(e);
return true;
}
public E peekFirst() {
final Node<E> f = first;
return (f == null) ? null : f.item;
}
public E peekLast() {
final Node<E> l = last;
return (l == null) ? null : l.item;
}
public E pollFirst() {
final Node<E> f = first;
return (f == null) ? null : unlinkFirst(f);
}
public E pollLast() {
final Node<E> l = last;
return (l == null) ? null : unlinkLast(l);
}
public void push(E e) {
addFirst(e);
}
public E pop() {
return removeFirst();
}
public boolean removeFirstOccurrence(Object o) {
return remove(o);
}
public boolean removeLastOccurrence(Object o) {
if (o == null) {
for (Node<E> x = last; x != null; x = x.prev) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<E> x = last; x != null; x = x.prev) {
if (o.equals(x.item)) {
unlink(x);
return true;
}
}
}
return false;
}
clone()方法
对链表进行浅拷贝
@SuppressWarnings("unchecked")
private LinkedList<E> superClone() {
try {
return (LinkedList<E>) super.clone();
} catch (CloneNotSupportedException e) {
throw new InternalError(e);
}
}
public Object clone() {
LinkedList<E> clone = superClone();
clone.first = clone.last = null;
clone.size = 0;
clone.modCount = 0;
for (Node<E> x = first; x != null; x = x.next)
clone.add(x.item);
return clone;
}
Just Do It:
“记忆是一条早已干涸的河流,只在毫无生气的河床中剩下零落的砾石”
|