链表数据结构

  当前节点会保存上一个、下一个节点。 参见 LinkedList的Node类

  实现:
    1. 内部链表的方式。
      1.1 添加元素。追加的方式,创建一个新的节点[Node],用最后一个节点关联新的节点。
      1.2 删除元素
        1.2.1 通过对象删除。遍历链表,删除第一个匹配的对象
            修改链表关联结构
    2. 内部是同步[modCount]
      2.1 LinkedList数据结构变化的时候,都会将modCount++。
      2.2 采用Iterator遍历的元素, next()会去检查集合是否被修改[checkForComodification],如果集合变更会抛出异常
        类似于数据库层面的 乐观锁 机制。 可以通过 Iterator的api去删除元素
    3. 数组结构,内部存储数据是有序的,并且数据可以为null

源码实现

package xin.rtime.collections.list;

import java.util.ConcurrentModificationException;
import java.util.Iterator;
import java.util.ListIterator;
import java.util.NoSuchElementException; // 链表的源码实现
public class LinkedList<E> { transient int size = ; // 当前链表的size transient Node<E> first; // 首节点 transient Node<E> last; // 尾节点 private int modCount = ; // 结构修改次数 // 添加节点
public boolean add(E e) {
linkLast(e);
return true;
} // 删除节点
public boolean remove(Object o) {
if (o == null) {
for (Node<E> x = first; x != null; x = x.next) {
if (x.item == null) {
unlink(x);
return true;
}
}
} else {
for (Node<E> x = first; x != null; x = x.next) {
if (o.equals(x.item)) { // equals
unlink(x);
return true;
}
}
}
return false;
} // 获取节点
public E get(int index) {
checkElementIndex(index); // 检查元素Index是否存在 , 不存在会抛出数组越界
return node(index).item;
} // 获取链表size
public int size() {
return size;
} public Iterator<E> iterator() {
return listIterator();
} public ListIterator<E> listIterator() {
return listIterator();
} Node<E> node(int index) {
// assert isElementIndex(index); if (index < (size >> )) { // 当前index 小于 当前一半容量的 size
Node<E> x = first; // 当前节点 等于 首节点
for (int i = ; i < index; i++) // 遍历 index -1 次
x = x.next; // x 等于当前节点的下一个节点
return x;
} else { // 大于
Node<E> x = last;
for (int i = size - ; i > index; i--) // 从尾部开始遍历
x = x.prev; // x 等于当前节点的上一个节点
return x;
}
} private void checkElementIndex(int index) {
if (!isElementIndex(index)) // 节点index是否在链表的容量范围内
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
} private boolean isElementIndex(int index) {
return index >= && index < size;
} private String outOfBoundsMsg(int index) {
return "Index: "+index+", Size: "+size;
} // 获得首个节点值
public E getFirst() {
final Node<E> f = first;
if (f == null)
throw new NoSuchElementException();
return f.item;
} // 获得尾部节点值
public E getLast() {
final Node<E> l = last;
if (l == null)
throw new NoSuchElementException();
return l.item;
} // 获得所有节点值
public Object[] toArray() {
Object[] result = new Object[size];
int i = ;
for (Node<E> x = first; x != null; x = x.next)
result[i++] = x.item;
return result;
} public ListIterator<E> listIterator(int index) {
checkPositionIndex(index);
return new ListItr(index);
} private void checkPositionIndex(int index) {
if (!isPositionIndex(index))
throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
} // 判断当前节点index是否存在
private boolean isPositionIndex(int index) {
return index >= && index <= size;
} private class ListItr implements ListIterator<E> {
private Node<E> lastReturned = null; // 返回的节点
private Node<E> next; // 下个节点
private int nextIndex; // 下个节点 下标
private int expectedModCount = modCount; // 预期的修改次数 = 等于遍历时的修改次数 ListItr(int index) {
// assert isPositionIndex(index);
next = (index == size) ? null : node(index);
nextIndex = index;
} public boolean hasNext() {
return nextIndex < size;
} public E next() {
checkForComodification(); // 校验当前链表是否被改动
if (!hasNext())
throw new NoSuchElementException(); lastReturned = next;
next = next.next;
nextIndex++;
return lastReturned.item;
} public boolean hasPrevious() {
return nextIndex > ;
} public E previous() {
checkForComodification(); // 校验当前链表是否被改动
if (!hasPrevious())
throw new NoSuchElementException(); lastReturned = next = (next == null) ? last : next.prev;
nextIndex--;
return lastReturned.item;
} public int nextIndex() {
return nextIndex;
} public int previousIndex() {
return nextIndex - ;
} public void remove() {
checkForComodification(); // 校验当前链表是否被改动
if (lastReturned == null)
throw new IllegalStateException(); Node<E> lastNext = lastReturned.next;
unlink(lastReturned); // 剔除节点
if (next == lastReturned)
next = lastNext;
else
nextIndex--;
lastReturned = null;
expectedModCount++;
} // 当前节点设置值
public void set(E e) {
if (lastReturned == null)
throw new IllegalStateException();
checkForComodification();
lastReturned.item = e;
} public void add(E e) {
checkForComodification();
lastReturned = null;
if (next == null) // 下一个节点为null
linkLast(e); // 在尾部插入
else
linkBefore(e, next); // 在指定节点之前插入
nextIndex++;
expectedModCount++;
} final void checkForComodification() {
if (modCount != expectedModCount)
throw new ConcurrentModificationException();
}
} private E unlink(Node<E> x) { 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; // 当前节点的上一个节点置为null gc回收
} if (next == null) { // 下一个节点为空
last = prev; // 最后节点 = 当前节点的上一个节点
} else { // 不为空
next.prev = prev; // 上一个节点的上一个节点 等于 当前节点的上一个节点
x.next = null; // 当前节点的下一个节点置为null gc回收
} x.item = null; // 当前元素置为null gc回收
size--; // 长度 + 1
modCount++; // 修改次数+1
return element;
} // 在链表的尾部插入节点
void linkLast(E e) {
final Node<E> l = last; // 最后一个元素
final Node<E> newNode = new Node<>(l, e, null); // 上一个元素,当前元素,null
last = newNode; // 最后一个节点等新建的节点
if (l == null) // 如果最后一个节点为空
first = newNode; // 出现一个情况: 当链表为空的时候 , first 和 last 都为 newNode
else
l.next = newNode; //最后节点的下一个节点,等于当前节点
size++; // 链表长度+1
modCount++; // 修改次数+1
} // 在指定节点添加上一个节点
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++;
} // 链表节点
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;
}
}
}

LinkedList源码学习的更多相关文章

  1. 基于JDK1.8的LinkedList源码学习笔记

    LinkedList作为一种常用的List,是除了ArrayList之外最有用的List.其同样实现了List接口,但是除此之外它同样实现了Deque接口,而Deque是一个双端队列接口,其继承自Qu ...

  2. 集合框架源码学习之LinkedList

    0-1. 简介 0-2. 内部结构分析 0-3. LinkedList源码分析 0-3-1. 构造方法 0-3-2. 添加add方法 0-3-3. 根据位置取数据的方法 0-3-4. 根据对象得到索引 ...

  3. JDK1.8源码学习-LinkedList

    JDK1.8源码学习-LinkedList 目录 一.LinkedList简介 LinkedList是一个继承于AbstractSequentialList的双向链表,是可以在任意位置进行插入和移除操 ...

  4. [数据结构-线性表1.2] 链表与 LinkedList<T>(.NET 源码学习)

    [数据结构-线性表1.2] 链表与 LinkedList<T> [注:本篇文章源码内容较少,分析度较浅,请酌情选择阅读] 关键词:链表(数据结构)    C#中的链表(源码)    可空类 ...

  5. JDK源码学习笔记——LinkedList

    一.类定义 public class LinkedList<E> extends AbstractSequentialList<E> implements List<E& ...

  6. Java集合专题总结(1):HashMap 和 HashTable 源码学习和面试总结

    2017年的秋招彻底结束了,感觉Java上面的最常见的集合相关的问题就是hash--系列和一些常用并发集合和队列,堆等结合算法一起考察,不完全统计,本人经历:先后百度.唯品会.58同城.新浪微博.趣分 ...

  7. JDK源码学习系列05----LinkedList

                                             JDK源码学习系列05----LinkedList 1.LinkedList简介 LinkedList是基于双向链表实 ...

  8. LinkedList源码解读

    一.内部类Node数据结构 在讲解LinkedList源码之前,首先我们需要了解一个内部类.内部类Node来表示集合中的节点,元素的值赋值给item属性,节点的next属性指向下一个节点,节点的pre ...

  9. Java集合框架之二:LinkedList源码解析

    版权声明:本文为博主原创文章,转载请注明出处,欢迎交流学习! LinkedList底层是通过双向循环链表来实现的,其结构如下图所示: 链表的组成元素我们称之为节点,节点由三部分组成:前一个节点的引用地 ...

随机推荐

  1. jQuery ajax在IE浏览器的跨域问题--jquery.xdomainrequest.min.js

    jquery.ajax 加载数据, chrome, firefox, IE10+ 都可以顺利加载数据,但是IE9及以后版本不执,通过执行 jquery.ajax error 函数显示未执行 拒绝访问. ...

  2. SparkSession - Spark SQL 的 入口

    SparkSession - Spark SQL 的 入口 翻译自:https://jaceklaskowski.gitbooks.io/mastering-apache-spark/content/ ...

  3. JS报错:Cannot read property 'type' of undefined

    在做图片上传功能的时候,遇到了JS无法识别图片type的问题,在使用过程中是没有问题的,但是不知道为什么浏览器的Console报这个错误: Uncaught TypeError: Cannot rea ...

  4. FCC高级编程篇之Make a Person

    Make a Person Fill in the object constructor with the following methods below: getfirstname() getLas ...

  5. layui层级

    zIndex:layer.zIndex, success : function(layero){ var zIndex = layer.index; $(layero).css(‘z-index’,z ...

  6. SP687 REPEATS - Repeats(后缀数组)

    一个初步的想法是我们枚举重复子串的长度\(L\).然后跑一遍SA.然后我们枚举一个点\(i\),令他的对应点为\(i+L\),然后求出这两个点的LCP和LCS的长度答案就是这个点的答案就是\((len ...

  7. Camera Calibration 相机标定:原理简介(三)

    3 绝对圆锥曲线 在进一步了解相机标定前,有必要了解绝对圆锥曲线(Absolute Conic)这一概念. 对于一个3D空间的点x,其投影空间的坐标为:x~=[x1,x2,x3,x4]T.我们定义无穷 ...

  8. js模拟支付宝提交表单

    弄过支付宝的程序猿可能都知道,里面有非常多地方都用到了自提交表单的方式,支付宝的接口通过请求API的形式取得server返回的表单字符串,使用out.print("表单字符串")在 ...

  9. 查看suse系统版本

    cat /etc/*-release OR lsb_release -d

  10. uwsgi和wsgi

    一个Web应用的本质就是: 浏览器发送一个HTTP请求: 服务器收到请求,生成一个HTML文档: 服务器把HTML文档作为HTTP响应的Body发送给浏览器: 浏览器收到HTTP响应,从HTTP Bo ...