您的位置:首页 > 编程语言 > Java开发

Java源码剖析之LinkedList

2017-03-28 09:50 489 查看
LinkedLists 是我们最常用的集合之一,通过节点Node来储存元素。下面我们来剖析LinkedList源码。

我们先来查看LinkedList的基本存储单元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;
}
}


static保证了Node是类共享而不是实例共享。并且Node中存储了next和orev两个节点,分别指向了Node的子节点和父节点。这是双向链表的基础。

在jdk1.6中,LinkedList是环形结构,而在1.7中,去掉了该环形结构,变为了线性双向链表结构。

1.类定义

public class LinkedList<E>

extends AbstractSequentialList<E>

implements List<E>, Deque<E>, Cloneable, java.io.Serializable


LinkedList继承了抽象链表,实现了List、双向队列Deque,克隆Cloneable和序列化接口。

2.私有属性

LinkedList有三个属性。

transient int size = 0;

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

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


size存储链表长度,first和last分别是头指针和尾指针。

3.构造方法

public LinkedList() {
}
public LinkedList(Collection<? extends E> c) {
this();
addAll(c);
}


4.插入操作

两个构造方法很好理解,下面我们看一下addAll()方法

public boolean addAll(Collection<? extends E> c) {
return addAll(size, c);
}
public boolean addAll(int index, Collection<? extends E> c) {
checkPositionIndex(index);  //检查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);     //原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)                   //如果前节点为null,被插入节点即为头结点
first = newNode;
else
pred.next = newNode;                //更新前节点的next
pred = newNode;                 //更新前节点
}

if (succ == null) {                 //同样操作,更新后节点
last = pred;
} else {
pred.next = succ;
succ.prev = pred;
}

size += numNew;     //更新size modCount
modCount++;
return true;
}


LinkedList的addAll()操作是一个标准的链表插入操作,将两个节点之间之间插入新的元素即可,这里是对所有的Collection都可以进行操作的,因此需要调用collection的toArray方法,再进行数组迭代插入。下面我们在看下add(E)操作。

public boolean add(E e) {
linkLast(e);        //调用linkLast()方法向链表末尾添加元素
return true;
}
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;                    //如果原尾节点为null,则原链表不存在,需要将插入节点设置为头结点
else
l.next = newNode;                   //如果原尾节点存在,更新其next
size++;
modCount++;
}


LinkedList的add()操作会将每个带插入元素包装为节点并存入尾节点。最后要考虑原链表为空的情况。

5.删除操作

我们先看一下之前出现的node(int index)方法,其返回index处的节点

Node<E> node(int index) {
// assert isElementIndex(index);    //在调用该方法前,需要判断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(int index)方法

public E remove(int index) {
checkElementIndex(index);
return unlink(node(index));
}
E unlink(Node<E> x) {
// assert x != null;
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;
}


对于removeFirst()和removeLast()方法,分别对应unlinkFirst(Node e)和unlinkLast(Node e)方法,实现类似,不再赘述。

6.修改操作

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


修改操作先进行边界检查,再通过node()方法取得待操作节点,然后修改节点值即可。

7.查询操作

public E get(int index) {
checkElementIndex(index);
return node(index).item;
}


查询操作先进行边界检查,再通过node()方法取得该节点,返回节点值即可。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签:  java linkedlist 源码