Java源码剖析之LinkedList
2017-03-28 09:50
489 查看
LinkedLists 是我们最常用的集合之一,通过节点Node来储存元素。下面我们来剖析LinkedList源码。
我们先来查看LinkedList的基本存储单元Node:
static保证了Node是类共享而不是实例共享。并且Node中存储了next和orev两个节点,分别指向了Node的子节点和父节点。这是双向链表的基础。
在jdk1.6中,LinkedList是环形结构,而在1.7中,去掉了该环形结构,变为了线性双向链表结构。
LinkedList继承了抽象链表,实现了List、双向队列Deque,克隆Cloneable和序列化接口。
size存储链表长度,first和last分别是头指针和尾指针。
LinkedList的addAll()操作是一个标准的链表插入操作,将两个节点之间之间插入新的元素即可,这里是对所有的Collection都可以进行操作的,因此需要调用collection的toArray方法,再进行数组迭代插入。下面我们在看下add(E)操作。
LinkedList的add()操作会将每个带插入元素包装为节点并存入尾节点。最后要考虑原链表为空的情况。
下面看remove(int index)方法
对于removeFirst()和removeLast()方法,分别对应unlinkFirst(Node e)和unlinkLast(Node e)方法,实现类似,不再赘述。
修改操作先进行边界检查,再通过node()方法取得待操作节点,然后修改节点值即可。
查询操作先进行边界检查,再通过node()方法取得该节点,返回节点值即可。
我们先来查看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源码剖析
- 【Java集合源码剖析】LinkedList源码剖析
- Java类集框架之LinkedList源码剖析
- 第二篇:JAVA集合之LinkedList源码剖析
- 【Java集合源码剖析】LinkedList源码剖析
- 【java集合框架源码剖析系列】java源码剖析之LinkedList
- 【java集合框架源码剖析系列】java源码剖析之LinkedList
- 【Java集合源码剖析】LinkedList源码剖析
- Java LinkedList源码剖析
- Java LinkedList源码剖析
- Java LinkedList简介 源码剖析
- 【Java1.7.5集合源码剖析】LinkedList源码剖析
- Java类集框架之LinkedList源码剖析
- Java集合------LinkedList源码剖析
- 转:【Java集合源码剖析】LinkedList源码剖析
- 【Java集合源码剖析】LinkedList源码剖析
- Java LinkedList 源码剖析
- 【Java集合源码剖析】LinkedList源码剖析
- Java记录 -49- LinkedList源码剖析
- 【Java集合源码剖析】LinkedList源码剖析