关于 LindedList 我想说
LinkedList 的一些认识:
对于锁链的认识还是以前看动画片<圣斗士星矢>中阿舜的武器,锁链被无数次的击碎断裂,然后小宇宙爆发,锁链会自动前后拼接,组成强大的链条。"星云锁链" - 锁链无边无际、攻击范围广,仙女座暴走也是很恐怖的 ^_^
可能我这样比喻并不怎么准确,双向链表也是基于这种前后节点的操作。我的理解是:
- 继承于AbstractSequentialList的双向链表,可以被当作堆栈、队列或双端队列进行操作
- 有序,非线程安全的双向链表,默认使用尾部插入法
- 适用于频繁新增或删除场景,频繁访问场景请选用ArrayList
- 插入和删除时间复杂为O(1),其余最差O(n)
- 由于实现Deque接口,双端队列相关方法众多,会专门来讲,这里不多加详述
■ 类定义
- public class LinkedList<E>
- extends AbstractSequentialList<E>
- implements List<E>, Deque<E>, Cloneable, java.io.Serializable
- 继承 AbstractSequentialList,能被当作堆栈、队列或双端队列进行操作
- 实现 List 接口,能进行队列操作
- 实现 Deque 接口,能将LinkedList当作双端队列使用
- 实现 Cloneable 接口,重写 clone() ,能克隆(浅拷贝)
- 实现 java.io.Serializable 接口,支持序列化
■ 重要全局变量
- /**
- * 当前链表元素数量
- */
- 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;
■ 构造器
- /**
- * Constructs an empty list.
- * 默认空构造器 -- 注意LinkedList并不提供指定容量的构造器
- */
- public LinkedList() {
- }
- /**
- * Constructs a list containing the elements of the specified
- * collection, in the order they are returned by the collection's iterator.
- * 支持将一个Collection转换成LinkedList
- *
- * @param c the collection whose elements are to be placed into this list
- * @throws NullPointerException if the specified collection is null
- */
- public LinkedList(Collection<? extends E> c) {
- this();
- addAll(c);
- }
■ Node节点 - 可看作链条的两头拼接点
- /**
- * 存储对象的结构:
- * 每个Node节点包含了上一个节点和下一个节点的引用,从而构成了双向的链表
- */
- private static class Node<E> {
- E item; //存储元素
- Node<E> next; // 指向下一个节点
- Node<E> prev; // 指向上一个节点
- //注意第一个元素是prev,第二个元素才是存储元素即可
- Node(Node<E> prev, E element, Node<E> next){
- this.item = element;
- this.next = next;
- this.prev = prev;
- }
- }
■ LinkedList 的存贮
add()
- /**
- * Appends the specified element to the end of this list.
- * <p>This method is equivalent to {@link #addLast}.
- * 插入一个新元素到链表尾部
- * @param e element to be appended to this list
- * @return {@code true} (as specified by {@link Collection#add}) 返回插入结果
- */
- public boolean add(E e) {
- linkLast(e);
- return true;
- }
- /**
- * Inserts the specified element at the specified position in this list.
- * Shifts the element currently at that position (if any) and any
- * subsequent elements to the right (adds one to their indices).
- * 插入一个新元素到指定下标位置,大于该下标的所有元素统一向右移动一位
- * @param index index at which the specified element is to be inserted
- * @param element element to be inserted
- * @throws IndexOutOfBoundsException {@inheritDoc}
- */
- public void add(int index, E element) {
- checkPositionIndex(index);//下标边界校验
- if (index == size) //当下标==链表长度时,尾部插入
- linkLast(element);
- else
- linkBefore(element, node(index));//否则,前部插入(起始位置为index)
- }
- checkPositionIndex() :
- private void checkPositionIndex(int index) {
- if (!isPositionIndex(index))
- throw new IndexOutOfBoundsException(outOfBoundsMsg(index));
- }
- /**
- * Tells if the argument is the index of a valid position for an iterator or an add operation.
- * 当迭代或插入操作时,需要判断下标的边界
- */
- private boolean isPositionIndex(int index) {
- return index >= 0 && index <= size;
- }
- linkLast()
- /**
- * Links e as last element.
- * 将e变为链表的最后一个元素
- */
- void linkLast(E e) {
- final Node<E> l = last;
- final Node<E> newNode = new Node<>(l, e, null);//注意:新建node的next为null
- last = newNode;//将新建node作为链表尾部节点
- //当原队尾为null时,即链表为空时
- if (l == null)
- first = newNode;//将新建node同时作为链表头部节点
- else
- l.next = newNode;//将原链表尾部节点的next引用指向新建node,形成链表结构
- size++;//当前链表长度+1
- modCount++;//新增操作属于结构性变动,modCount计数+1
- }
- linkBefore()
- /**
- * Inserts element e before non-null Node succ.
- * 插入一个新的元素到指定非空节点之前
- */
- void linkBefore(E e, Node<E> succ) {
- // assert succ != null;
- //逻辑与linkLast基本一致,区别在于将last变成prev,将新节点插入到succ节点之前
- final Node<E> pred = succ.prev;
- final Node<E> newNode = new Node<>(pred, e, succ);
- succ.prev = newNode;//唯一的区别,将新节点插入到succ节点之前
- if (pred == null)
- first = newNode;
- else
- pred.next = newNode;
- size++;
- modCount++;//插入属于结构性变动,modCount计数+1
- }
■ LinkedList 的读取
- get()
- /**
- * Returns the element at the specified position in this list.
- * 获取指定下标元素
- * @param index index of the element to return
- * @return the element at the specified position in this list
- * @throws IndexOutOfBoundsException {@inheritDoc}
- */
- public E get(int index) {
- checkElementIndex(index); //判断下标是否存在元素
- return node(index).item; //注意返回不是node,而是item;同时node一定不为null,而item允许为null
- }
- node()
- /**
- * Returns the (non-null) Node at the specified element index.
- * 返回指定下标的非空node
- */
- Node<E> node(int index) {
- // assert isElementIndex(index);
- //值得一提的是,为了提高查询效率,node查询选择使用二分查找法
- 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;
- }
- }
■ LinkedList 的移除
- remove()
- /**
- * Retrieves and removes the head (first element) of this list.
- * 默认删除头部节点
- * @return the head of this list
- * @throws NoSuchElementException if this list is empty
- * @since 1.5
- */
- public E remove() {
- return removeFirst();
- }
- /**
- * Removes the element at the specified position in this list. Shifts any
- * subsequent elements to the left (subtracts one from their indices).
- * Returns the element that was removed from the list.
- * 根据下标删除元素
- * @param index the index of the element to be removed
- * @return the element previously at the specified position
- * @throws IndexOutOfBoundsException {@inheritDoc}
- */
- public E remove(int index) {
- checkElementIndex(index);//边界校验 index >= 0 && index < size
- return unlink(node(index));//解绑操作
- }
- /**
- * Removes the first occurrence of the specified element from this list,if it is present.
- * If this list does not contain the element, it is unchanged.
- * More formally, removes the element with the lowest index {@code i} such that
- * <tt>(o==null?get(i)==null:;o.equals(get(i)))</tt> (if such an element exists).
- * Returns {@code true} if this list contained the specified element (or equivalently,
- * if this list changed as a result of the call).
- * 直接移除某个元素:
- * 当该元素不存在,不会发生任何变化
- * 当该元素存在且成功移除时,返回true,否则false
- * 当有重复元素时,只删除第一次出现的同名元素 :
- * 例如只移除第一次出现的null(即下标最小时出现的null)
- * @param o element to be removed from this list, if present
- * @return {@code true} if this list contained the specified element
- */
- public boolean remove(Object o) {
- //虽然跟ArrayList一样需要遍历,但由于不需要调用耗时的`System.arraycopy`,效率更高
- 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)) {
- unlink(x);
- return true;
- }
- }
- }
- return false;
- }
- unlink()
- /**
- * Unlinks non-null node x.
- * 解除node链接,主要干了三件事情:
- * 1.解绑当前元素的前后节点链接,前后节点重新绑定关系
- * 2.当前元素的所有属性清空,help gc
- * 3.链表长度-1,modCount计数+1(help fail-fast)
- * @return 返回元素本身 注意是item,而不是node
- */
- 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;//将前一位节点的next指向下一位节点
- x.prev = null;//当前节点的前一位节点清空 ,help gc
- }
- //解绑后一位节点
- if (next == null) {//当前节点位于链表尾部
- last = prev;//前一位节点放链表尾部
- } else {
- //非链表尾部
- next.prev = prev;//将后一位节点的prev指向前一位节点
- x.next = null;//当前节点的后一位节点清空 ,help gc
- }
- x.item = null;//当前节点元素清空
- size--;//链表长度-1
- modCount++;//删除操作属于结构性变动,modCount计数+1
- return element;//返回元素本身
- }
** unlinkFirst / unlinkLast 思路基本一致,有兴趣读者可参考JDK
■ LinkedList 实现堆栈
- 栈(Stack)是限定仅在一端进行插入(push)、输出删除(pop) 运算的线性表
- 根据后进先出(LIFO: last in first output)原则,顶部称为栈顶(top),底部称为栈底(bottom)
- /**
- * 堆栈(Stack)的LinkedList版本简单实现
- * 这里使用first(使用last原理也一样,保证只在一端操作即可)
- */
- class Stack<T> {
- LinkedList<T> linkedList = new LinkedList<T>();
- /**
- * 入栈
- */
- public void push(T v) {
- linkedList.addFirst(v);
- }
- /**
- * 出栈,不删除栈顶元素
- */
- public T peek() {
- return storage.getFirst();
- }
- /**
- * 出栈 ,删除栈顶元素
- */
- public T pop() {
- return storage.removeFirst();
- }
- }
■ LinkedList 实现队列
- 队列(Queue)是限定插入和删除各在一端进行的线性表
- 根据先进先出(FIFO)原则,表中允许插入的一端称为队尾(Rear),允许删除的一端称为队头(Front)
- 队列的操作方式和堆栈类似,唯一的区别在于队列只允许新数据在后端进行添加
- /**
- * 队列的LinkedList版本简单实现
- * 这里使用队尾插入,对头删除的写法(反过来原理一致,只要保证插入和删除各占一端即可)
- */
- class Queue<T> {
- LinkedList<T> linkedList = new LinkedList<T>();
- /**
- * 入队,将指定的元素插入队尾
- */
- public void offer(T v) {
- linkedList.offer(v);
- }
- /**
- * 出队,获取头部元素,但不删除,如果此队列为空,则返回 null
- */
- public T peek() {
- return linkedList.peek();
- }
- /**
- * 出队,获取头部元素,但不删除,如果此队列为空,则抛异常
- */
- public T element() {
- return linkedList.element();
- }
- /**
- * 出队,获取头部元素并删除,如果队列为空,则返回 null
- */
- public T poll() {
- return linkedList.poll();
- }
- /**
- * 出队,获取头部元素并删除,如果队列为空,则抛异常
- */
- public T remove() {
- return linkedList.remove();
- }
- }
关于 LindedList 我想说的更多相关文章
- 看完SQL Server 2014 Q/A答疑集锦:想不升级都难!
看完SQL Server 2014 Q/A答疑集锦:想不升级都难! 转载自:http://mp.weixin.qq.com/s/5rZCgnMKmJqeC7hbe4CZ_g 本期嘉宾为微软技术中心技术 ...
- CoreCRM 开发实录——想用国货不容易
昨天(2016年12月29日)发了开始开发的文章.本来晚上准备在 Coding.NET 上添加几个任务开始搞起了.可是真的开始用的时候才发现:Coding.NET 的任务功能只针对私有的任务开放.我想 ...
- 【踩坑速记】二次依赖?android studio编译运行各种踩坑解决方案,杜绝弯路,总有你想要的~
这篇博客,只是把自己在开发中经常遇到的打包编译问题以及解决方案给大家稍微分享一下,不求吸睛,但求有用. 1.大家都知道我们常常会遇到dex超出方法数的问题,所以很多人都会采用android.suppo ...
- 要想提高PHP的编程效率,你必须知道的要点
1.当操作字符串并需要检验其长度是否满足某种要求时,你想当然地会使用strlen()函数.此函数执行起来相当快,因为它不做任何计算,只返回在zval 结构(C的内置数据结构,用于存储PHP变量)中存储 ...
- 我想立刻辞职,然后闭关学习编程语言,我给自己3个月时间学习C语言!这样行的通吗
文章背景,回答提问:我想立刻辞职,然后闭关学习编程语言,我给自己3个月时间学习C语言!这样行的通吗? 我的建议是这样:1. 不要辞职.首先说,你对整个开发没有一个简单的了解,或一个系统的入门学习.换句 ...
- ES6 箭头函数中的 this?你可能想多了(翻译)
箭头函数=>无疑是ES6中最受关注的一个新特性了,通过它可以简写 function 函数表达式,你也可以在各种提及箭头函数的地方看到这样的观点——“=> 就是一个新的 function”. ...
- Windbg Extension NetExt 使用指南 【3】 ---- 挖掘你想要的数据 Managed Heap
摘要 : NetExt中有两个比较常用的命令可以用来分析heap上面的对象. 一个是!wheap, 另外一个是!windex. !wheap 这个命令可以用于打印出heap structure信息. ...
- JS的Object漫想:从现象到“本质”
转自:http://zzy603.iteye.com/blog/973649 写的挺好,用于记录,把对象分成概念的Object(var f={})和 类的Object(function F(){}) ...
- 个人随想:对于一个.Neter来说,如果一直想走技术路线,该怎么走下去
前言 首先我不是一个合格的.Neter,也许在这里我的技术算是很菜的,不过我也是有想法的人,下面罗列出我的想法和将要实现的技术路线图. 1.学习一门底层语言 比如学习C语言,学习C语言的最终目的我觉得 ...
随机推荐
- [2017-08-28]Abp系列——业务异常与错误码设计及提示语的本地化
本系列目录:Abp介绍和经验分享-目录 前言 ABP中有个异常UserFriendlyException经常被使用,但是它所在的命名空间是Abp.UI,总觉得和展现层联系过于紧密,在AppServic ...
- 【完整资料】TC358779XBG:HDMI转MIPI DSI芯片方案
TC358779XBG是一颗HDMI1.4转MIPI DSI带缩放功能的芯片,分辨率1920*1080,封装BGA80.通信方式:IIC,电源3.3/1.8/2.2,应用领域:平板,广告机,VR,显 ...
- Laravel学习基础篇之--路由
终于还是决定再多学一门重量级框架,当然首选必备还是被称为最优雅的Web开发框架--Laravel 对于框架的入门,首先了解它的路由规则是先前必备的,以下是laravel 中几种常见的基础路由规则 // ...
- 向GitHub 提交你的源代码
之前的这篇文章「Git入门篇」相信大家都已经对 Git 的基本操作熟悉了,但是这篇文章只介绍了对本地 Git 仓库的基本操作,今天我就来介绍下如何跟远程仓库一起协作,教你们向 GitHub 上提交你们 ...
- Oracle 的process和Session
Oracle 的process和Session 1.process 和session的概念:process:这个参数限制了能够连接到SGA的操作系统进程数(或者是Windows 系统中的线程数),这个 ...
- 3.修改第一个程序来点亮LED
在上一节中已经将驱动程序框架搭建好了 接下来开始写硬件的操作(控制LED): (1)看原理图,确定引脚 (2)看2440手册 (3)写代码(需要使用ioremap()函数映射虚拟地址,在linux中只 ...
- JSONP的实现流程
在进行AJAX的时候会经常产生这样一个报错: 看红字,这是浏览器的同源策略,使跨域进行的AJAX无效.注意,不是不发送AJAX请求(其实就是HTTP请求),而是请求了,也返回了,但浏览器‘咔擦’一声, ...
- [转载]历上最强的音乐播放器(jetAudio-8.0.5.320-Plus-VX
原文地址:历上最强的音乐播放器(jetAudio-8.0.5.320-Plus-VX-完全汉化版)下载作者:盖世天星 历上最强的音乐播放器(jetAudio-8.0.5.320-Plus-VX-完全汉 ...
- 团队作业8——Beta 阶段冲刺6th day
一.当天站立式会议 二.每个人的工作 (1)昨天已完成的工作(具体在表格中) 完善订单功能 (2)今天计划完成的工作(具体如下) 完善支付功能 (3) 工作中遇到的困难(在表格中) 成员 昨天已完成的 ...
- Java计算器1.0版
此版本只是设计了页面,还没有实现事件监听 代码: package com.niit.javagui; import java.awt.Button; import java.awt.FlowLay ...