Java源码阅读——LinkedList

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的关系。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值