Java源码阅读——LinkedList
上次介绍了ArrayList源码,自然少不了LinkedList。
定义
public class LinkedList<E>
extends AbstractSequentialList<E>
implements List<E>, Deque<E>, Cloneable, java.io.Serializable
继承了AbstractSequentialList抽象类,同时实现了List<E>,Deque<E>, Cloneable, java.io.Serializable接口。其中AbstractSequentialList继承了AbstractList抽象类。和ArrayList不同的是,ArrayList实现RandomAccess接口,而LinkedList实现的是Deque接口,这就是两者不同的根源。一个是数组,一个是双向链表。
如果多个线程同时访问一个链接列表,而其中至少一个线程从结构上修改了该列表,则它必须保持外部同步。可以使用Collections.synchronizedList 方法来“包装”该列表。最好在创建时完成这一操作,以防止对列表进行意外的不同步访问,如下所示:
List list =Collections.synchronizedList(new LinkedList(...));
成员变量
注意,这里三个成员变量都加了transient,
1. transient int size = 0;//列表的长度
2. transient Node<E> first;//头指针
3. transient Node<E> last;//尾指针
构造方法
构造方法有两个
public LinkedList() {
}
默认构造函数,构造空链表,什么也不做,不需要初始化,因为就两个指针。
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}
构造一个包含指定collection 中的元素的列表,这些元素按其collection 的迭代器返回的顺序排列。调用addAll方法,将元素追加到末尾(这里的末尾为0)
静态内部类Node
静态内部类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;
}
}
成员方法
boolean add(E e)
将指定元素添加到此列表的结尾。
public boolean add(E e) {
linkLast(e);
return true;
}
调用linkLast(e)
/**
* Links e as last element.
*/
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++;
}
新建节点,保存last节点引用为l,last节点指向新建节点,如果l为空(之前为空列表),则first节点指向新建的节点,如果l不空,则l指向新建节点,增加size。
void add(int index, E element)
在此列表中指定的位置插入指定的元素。
public void add(int index, E element) {
checkPositionIndex(index);
if (index == size)
linkLast(element);
else
linkBefore(element, node(index));
}
checkPositionIndex(index)检查index合法性,对于列表,读写操作带有index都是必须检查的。如果index为size,也就是在最后linkLast,否则调用linkBefore(e,node(index)),其中node(index)返回列表中index的节点,若index为负数,则从尾部查询(代码这里就不贴了)。
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++;
}
linkBefore(E e,Node<E> succ),则是在列表中succ(也就是index指向的那个节点)节点前面插入。
boolean addAll(Collection<? extends E> c)
添加指定collection 中的所有元素到此列表的结尾,顺序是指定 collection 的迭代器返回这些元素的顺序。
public boolean addAll(Collection<?extends E> c) {
return addAll(size, c);
}
这里调用了下面的addAll函数,直接将c加在列表尾部
boolean addAll(int index, Collection<? extends E> c)
将指定 collection中的所有元素从指定位置开始插入此列表。
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;
}
先将collection转换为数组toArray,然后判断加入的位置在尾部还是中间位置。然后循环数组进行prev和next连接。主要是判断首尾位置,当然这不用我们关心,我们只要理解就好,对于数据结构里的双向链表,这里的源码很值得学习。
void addFirst(E e)
将指定元素插入此列表的开头。
public void addFirst(E e) {
linkFirst(e);
}
调用linkFirst(e)
/**
* Links e as first element.
*/
private void linkFirst(E e) {
final Node<E> f = first;
final Node<E> newNode = new Node<>(null, e, f);
first = newNode;
if (f == null)
last = newNode;
else
f.prev = newNode;
size++;
modCount++;
}
和linkLast(e)类似,在列表头部添加。
void addLast(E e)
将指定元素添加到此列表的结尾。
public void addLast(E e) {
linkLast(e);
}
和add类似调用linkLast,只不过这个没有返回值。
void clear()
从此列表中移除所有元素。
public void clear() {
// Clearing all of the links between nodesis "unnecessary", but:
// - helps a generational GC if thediscarded nodes inhabit
// more than one generation
// - is sure to free memory even ifthere is a reachable Iterator
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++;
}
这里调用了一个循环进行一个一个的置为null,而注释中也写到【清除节点之间的所有链接是“不必要的”,但可以帮助GC确保释放内存】。
Object clone()
返回此 LinkedList的浅表副本。
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;
}
boolean contains(Object o)
如果此列表包含指定元素,则返回 true。
public boolean contains(Object o) {
return indexOf(o) != -1;
}
返回该对象的index值,如果为-1则不存在。
Iterator<E> descendingIterator()
返回以逆向顺序在此双端队列的元素上进行迭代的迭代器。
public Iterator<E> descendingIterator() {
return new DescendingIterator();
}
内部类DescendingIterator定义如下,next()调用的是previous(),所以是反向迭代器。
/**
* Adapter to provide descendingiterators via ListItr.previous
*/
private class DescendingIterator implements Iterator<E> {
private final ListItr itr = new ListItr(size());
public boolean hasNext() {
return itr.hasPrevious();
}
public E next() {
return itr.previous();
}
public void remove() {
itr.remove();
}
}
E element()
获取但不移除此列表的头(第一个元素)。
public E element() {
return getFirst();
}
返回getFirst()
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
返回头结点
E get(int index)
返回此列表中指定位置处的元素。
public E get(int index) {
checkElementIndex(index);
return node(index).item;
}
返回node(index)的值。
E getFirst()
返回此列表的第一个元素。
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
}
返回头结点first,如果为空,会抛异常。
E getLast()
返回此列表的最后一个元素。
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
}
同上
int indexOf(Object 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;
}
这里进行了循环查找,返回index次数,并且考虑的对象为null的情况。
int lastIndexOf(Object o)
返回此列表中最后出现的指定元素的索引,如果此列表中不包含该元素,则返回 -1。
public int lastIndexOf(Object o) {
int index = size;
if (o == null) {
for (Node<E> x = last; x != null; x = x.prev) {
index--;
if (x.item == null)
return index;
}
} else {
for (Node<E> x = last; x != null; x = x.prev) {
index--;
if (o.equals(x.item))
return index;
}
}
return -1;
}
同上。
ListIterator<E> listIterator(int index)
返回此列表中的元素的列表迭代器(按适当顺序),从列表中指定位置开始。
public ListIterator<E> listIterator(int index) {
checkPositionIndex(index);
return new ListItr(index);
}
返回迭代器。ListItr定义为
private class ListItr implements ListIterator<E>
boolean offer(E e)
将指定元素添加到此列表的末尾(最后一个元素)。
public boolean offer(E e) { return add(e); }
boolean offerFirst(E e)
在此列表的开头插入指定的元素。
public boolean offerFirst(E e) { addFirst(e); return true; }
下面这些操作都比较类似,知道其意思,也就知道如何操作,这里就不贴代码了。。。
boolean offerLast(E e)
在此列表末尾插入指定的元素。
Epeek()
获取但不移除此列表的头(第一个元素)。
EpeekFirst()
获取但不移除此列表的第一个元素;如果此列表为空,则返回 null。
EpeekLast()
获取但不移除此列表的最后一个元素;如果此列表为空,则返回 null。
E poll()
获取并移除此列表的头(第一个元素)
E pollFirst()
获取并移除此列表的第一个元素;如果此列表为空,则返回 null。
E pollLast()
获取并移除此列表的最后一个元素;如果此列表为空,则返回 null。
E pop()
从此列表所表示的堆栈处弹出一个元素。
void push(E e)
将元素推入此列表所表示的堆栈。
E remove()
获取并移除此列表的头(第一个元素)。
E remove(int index)
移除此列表中指定位置处的元素。
boolean remove(Object o)
从此列表中移除首次出现的指定元素(如果存在)。
E removeFirst()
移除并返回此列表的第一个元素。
boolean removeFirstOccurrence(Object o)
从此列表中移除第一次出现的指定元素(从头部到尾部遍历列表时)。
E removeLast()
移除并返回此列表的最后一个元素。
boolean removeLastOccurrence(Object o)
从此列表中移除最后一次出现的指定元素(从头部到尾部遍历列表时)。
E set(int index, E element)
将此列表中指定位置的元素替换为指定的元素。
public E set(int index, E element) { checkElementIndex(index); Node<E> x = node(index); E oldVal = x.item; x.item = element; return oldVal; }
返回oldVal。
int size()
返回此列表的元素数。
public int size() { return size; }
Object[] toArray()
返回以适当顺序(从第一个元素到最后一个元素)包含此列表中所有元素的数组。
public Object[] toArray() { Object[] result = new Object[size]; int i = 0; for (Node<E> x = first; x != null; x = x.next) result[i++] = x.item; return result; }
比较好理解,直接new数组,赋值。
<T> T[] toArray(T[] a)
返回以适当顺序(从第一个元素到最后一个元素)包含此列表中所有元素的数组;返回数组的运行时类型为指定数组的类型。
public <T> T[] toArray(T[] a) { if (a.length < size) a = (T[])java.lang.reflect.Array.newInstance( a.getClass().getComponentType(), size); int i = 0; Object[] result = a; for (Node<E> x = first; x != null; x = x.next) result[i++] = x.item; if (a.length > size) a[size] = null; return a; }
参数中的数组的length小于size时,需要重新分配空间。
总结
linkedlist源码比较简单,理解数据结构中的双向链表基本上没什么问题。其中很多API实现的功能都比较类似,有的直接调用其他API。应该是实现了不同的接口,1.5,1.6新增的两个接口Queue和Deque的关系。