JDK容器与并发—List—LinkedList

概述

      基于双向链表的List实现,也实现了Deque接口,无界List。

1)非线程安全;

2)fail-fast:同ArrayList。

数据结构

      基于非环形双链表,维护first、last引用

/**
 * Pointer to first node.
 * Invariant: (first == null && last == null) ||
 *            (first.prev == null && first.item != null)
 */
transient Node first;

/**
 * Pointer to last node.
 * Invariant: (first == null && last == null) ||
 *            (last.next == null && last.item != null)
 */
transient Node last;

// 双链表
private static class Node {
	E item;
	Node next;
	Node prev;

	Node(Node prev, E element, Node next) {
		this.item = element;
		this.next = next;
		this.prev = prev;
	}
}

若first=last=null,则链表为空,首尾节点的item不能为null。

构造器

// 无参构造
public LinkedList() {
}

// 指定Collection参数构造,将其中的元素添加进来
public LinkedList(Collection c) {
	this();
	addAll(c);
}

增删改查

基础方法

      基础方法中涉及到链表首、尾、中间的插入与删除,都是关于Node的prev、next引用的操作,所以动态插入、删除都是很方便的。由于LinkedList维护了first、last引用,所以在链表首尾进行插入、删除是高效的,但是在链表中间插入、删除都需要先根据索引遍历获取到索引位置节点,所以有一个遍历成本。

// 如果链表只有一个节点,则first=last=node,其中node为节点
// 在链表头添加节点
private void linkFirst(E e) {
	final Node f = first;
	final Node newNode = new Node<>(null, e, f);
	first = newNode; // 维护first,指向新增节点
	if (f == null)
		last = newNode; // 链表为空,维护last,指向新增节点
	else
		f.prev = newNode; // 将旧的链表头的prev指向新增节点
	size++;
	modCount++; // 结构性修改次数加1
}

// 在链表尾添加节点
void linkLast(E e) {
	final Node l = last;
	final Node newNode = new Node<>(l, e, null);
	last = newNode; // 维护last,指向新增节点
	if (l == null)
		first = newNode; // 链表为空,维护first,指向新增节点
	else
		l.next = newNode; // 将旧的链表尾的next指向新增节点
	size++;
	modCount++;
}

// 在链表中间插入节点
void linkBefore(E e, Node succ) {
	// assert succ != null;
	final Node pred = succ.prev;
	final Node newNode = new Node<>(pred, e, succ);
	succ.prev = newNode; // 将succ的prev指向新增节点
	if (pred == null)
		first = newNode; // succ为旧的链表首节点,维护first,指向新增节点
	else
		pred.next = newNode; // 将新增节点插入进来
	size++;
	modCount++;
}

// 在链表首删除节点
private E unlinkFirst(Node f) {
	// assert f == first && f != null;
	final E element = f.item;
	final Node next = f.next;
	f.item = null;
	f.next = null; // help GC
	first = next;
	if (next == null)
		last = null; // 链表为空,维护first、last
	else
		next.prev = null; // 更新链表首节点
	size--;
	modCount++;
	return element;
}

// 在链表尾删除节点
private E unlinkLast(Node l) {
	// assert l == last && l != null;
	final E element = l.item;
	final Node prev = l.prev;
	l.item = null;
	l.prev = null; // help GC
	last = prev;
	if (prev == null)
		first = null; // 链表为空,维护first、last
	else
		prev.next = null; // 更新链表尾节点
	size--;
	modCount++;
	return element;
}

// 在链表中间删除节点
E unlink(Node x) {
	// assert x != null;
	final E element = x.item;
	final Node next = x.next;
	final Node prev = x.prev;

	if (prev == null) {
		first = next; // 删除的节点为旧的链表首节点,维护first指向新的节点
	} else {
		prev.next = next; // 更新prev的next
		x.prev = null; // help GC
	}

	if (next == null) {
		last = prev; // 删除的节点为旧的链表尾节点,维护last指向新的节点 
	} else {
		next.prev = prev; // 更新next的prev
		x.next = null; // help GC
	}

	x.item = null; // 清空item
	size--;
	modCount++;
	return element;
}

      涉及到的方法有:

// List methods
public boolean add(E e)
public void add(int index, E element)
public boolean addAll(Collection c)
public boolean addAll(int index, Collection c)

// Deque methods
public void addFirst(E e)
public void addLast(E e)
public boolean offerFirst(E e)
public boolean offerLast(E e)

// Queue methods
public boolean offer(E e)

// Stack methods
public void push(E e)

由于LinkedList是无界的,关于添加元素的方法大同小异,都用到了基础方法来做。重点看下随机add、addAll:

public void add(int index, E element) {
	checkPositionIndex(index);

	if (index == size)
		linkLast(element);
	else
		linkBefore(element, node(index));
}

// 将index与一半容量比较,从first或last最快遍历
Node node(int index) {
	// assert isElementIndex(index);

	if (index < (size >> 1)) {
		Node x = first;
		for (int i = 0; i < index; i++)
			x = x.next;
		return x;
	} else {
		Node x = last;
		for (int i = size - 1; i > index; i--)
			x = x.prev;
		return x;
	}
}

public boolean addAll(int index, Collection c) {
	checkPositionIndex(index);

	Object[] a = c.toArray();
	int numNew = a.length;
	if (numNew == 0)
		return false;

	Node pred, succ;
	if (index == size) {
		succ = null;
		pred = last;
	} else {
		succ = node(index); // 根据索引遍历获取索引位置的节点
		pred = succ.prev;
	}

	// 将数组a转化为一个双链表
	for (Object o : a) {
		@SuppressWarnings("unchecked") E e = (E) o;
		Node 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;
}

随机插入都需要根据索引遍历获取索引位置的节点,然后在其前面插入,维护好prev、next引用。

涉及到的方法有:

// List methods
public E remove(int index)
public boolean remove(Object o)

// Deque methods
public E removeFirst()
public E removeLast()
public E pollFirst()
public E pollLast()

// Queue methods
public E remove()
public E poll()

// Stack methods
public E pop()

删除节点就是用到unlink。

      来自List接口,这里是随机set:

public E set(int index, E element) {
        checkElementIndex(index);
        Node x = node(index);
        E oldVal = x.item;
        x.item = element;
        return oldVal;
    }

涉及到的方法有:

// List methods
public E get(int index)

// Deque methods
public E getFirst()
public E getLast()
public E peekFirst()
public E peekLast()

// Queue methods
public E element()
public E peek()

迭代器

public ListIterator listIterator(int index) {
	checkPositionIndex(index);
	return new ListItr(index);
}

private class ListItr implements ListIterator

在双链表的基础上实现了ListIterator,支持从索引位置遍历,另外还提供了与ListIterator遍历顺序相反的迭代器,利用了代理设计模式:

public Iterator descendingIterator() {
	return new DescendingIterator();
}

private class DescendingIterator implements Iterator {
	private final ListItr itr = new ListItr(size());
	public boolean hasNext() {
		return itr.hasPrevious();
	}
	public E next() {
		return itr.previous();
	}
	public void remove() {
		itr.remove();
	}
}

特性

      List中的随机插入、删除、修改、访问都需要先根据索引遍历获取到索引位置的节点,再进行操作,有遍历成本,由于维护了first、last引用,所以在链表的首尾操作是高效的。

你可能感兴趣的:(JDK容器与并发)