.NET源码中的链表
.NET中自带的链表是LinkedList类,并且已经直接实现成了双向循环链表。
其节点类LinkedListNode的数据结构如下,数据项包括指示到某个链表的引用,以及左,右节点和值。
- public sealed class LinkedListNode<T>
- {
- internal LinkedList<T> list;
- internal LinkedListNode<T> next;
- internal LinkedListNode<T> prev;
- internal T item;
- }
另外,获取前一个节点和后一个节点的实现如下:注意:里面的if-else结构的意义是当前一个(后一个)节点不为空且不是头节点时才不返回null,这样做的意义是当链表内只有1个节点时,其prev和next是指向自身的。
- [__DynamicallyInvokable]
- public LinkedListNode<T> Next
- {
- [__DynamicallyInvokable] get
- {
- if (this.next != null && this.next != this.list.head)
- return this.next;
- else
- return (LinkedListNode<T>) null;
- }
- }
- [__DynamicallyInvokable]
- public LinkedListNode<T> Previous
- {
- [__DynamicallyInvokable] get
- {
- if (this.prev != null && this != this.list.head)
- return this.prev;
- else
- return (LinkedListNode<T>) null;
- }
- }
过有一个把链表置为无效的方法定义如下:
- internal void Invalidate()
- {
- this.list = (LinkedList<T>) null;
- this.next = (LinkedListNode<T>) null;
- this.prev = (LinkedListNode<T>) null;
- }
而LinkedList的定义如下:主要的两个数据是头节点head以及长度count。
- public class LinkedList<T> : ICollection<T>, IEnumerable<T>, ICollection, IEnumerable, ISerializable, IDeserializationCallback
- {
- internal LinkedListNode<T> head;
- internal int count;
- internal int version;
- private object _syncRoot;
- private SerializationInfo siInfo;
- private const string VersionName = "Version";
- private const string CountName = "Count";
- private const string ValuesName = "Data";
而对此链表的主要操作,包括:
- 插入节点到最后,Add(),也是AddLast()。
- 在某个节点后插入,AddAfter(Node, T)。
- 在某个节点前插入,AddBefore(Node, T)。
- 插入到头节点之前,AddFirst(T)。
- 清除所有节点,Clear()。
- 是否包含某个值,Contains(T),也就是Find()。
- 查找某个节点的引用,Find()和FindLast()。
- 复制到数组,CopyTo(Array)
- 删除某个节点,Remove(T)。
- 内部插入节点,InternalInsertNodeBefore()
- 内部插入节点到空链表,InternalInsertNodeToEmptyList()
- 内部删除节点,InternalRemoveNode()
- 验证新节点是否有效,ValidateNewNode()
- 验证节点是否有效,ValidateNode()
- public void AddLast(LinkedListNode<T> node)
- {
- this.ValidateNewNode(node);
- if (this.head == null)
- this.InternalInsertNodeToEmptyList(node);
- else
- this.InternalInsertNodeBefore(this.head, node);
- node.list = this;
- }
插入操作的第一步是验证节点是否有效,即节点不为null,且节点不属于其他链表。
- internal void ValidateNewNode(LinkedListNode<T> node)
- {
- if (node == null)
- throw new ArgumentNullException("node");
- if (node.list != null)
- throw new InvalidOperationException(SR.GetString("LinkedListNodeIsAttached"));
- }
如果头节点为空,则执行插入到空链表的操作:将节点的next和prev都指向为自己,并作为头节点。
- private void InternalInsertNodeToEmptyList(LinkedListNode<T> newNode)
- {
- newNode.next = newNode;
- newNode.prev = newNode;
- this.head = newNode;
- ++this.version;
- ++this.count;
- }
如果头节点不为空,则执行插入到头节点之前(注:因为是双向链表,所以插到头节点之前就相当于插到链表的最后了),具体的指针指向操作如下:
- private void InternalInsertNodeBefore(LinkedListNode<T> node, LinkedListNode<T> newNode)
- {
- newNode.next = node;
- newNode.prev = node.prev;
- node.prev.next = newNode;
- node.prev = newNode;
- ++this.version;
- ++this.count;
- }
而插入新节点到指定节点之后的操作如下:同样还是调用的内部函数,把新节点插入到指定节点的下一个节点的之前。有点绕,但确实让这个内部函数起到多个作用了。
- public void AddAfter(LinkedListNode<T> node, LinkedListNode<T> newNode)
- {
- this.ValidateNode(node);
- this.ValidateNewNode(newNode);
- this.InternalInsertNodeBefore(node.next, newNode);
- newNode.list = this;
- }
而插入新节点到指定节点之前的操作如下:直接调用插入新节点的内部函数,另外还要判断指定的节点是否是头节点,如果是的话,要把头节点变成新的节点。
- public void AddBefore(LinkedListNode<T> node, LinkedListNode<T> newNode)
- {
- this.ValidateNode(node);
- this.ValidateNewNode(newNode);
- this.InternalInsertNodeBefore(node, newNode);
- newNode.list = this;
- if (node != this.head)
- return;
- this.head = newNode;
- }
把新链表插入到第一个节点(也就是变成头节点)的操作如下:如果链表为空就直接变成头节点,否则就插入到头节点之前,取代头节点。
- public void AddFirst(LinkedListNode<T> node)
- {
- this.ValidateNewNode(node);
- if (this.head == null)
- {
- this.InternalInsertNodeToEmptyList(node);
- }
- else
- {
- this.InternalInsertNodeBefore(this.head, node);
- this.head = node;
- }
- node.list = this;
- }
查找链表中某个值的操作如下:注意直接返回null的条件是头节点为空。然后就是遍历了,因为是双向链表,所以要避免死循环(遍历到头节点时跳出)。
- public LinkedListNode<T> Find(T value)
- {
- LinkedListNode<T> linkedListNode = this.head;
- EqualityComparer<T> @default = EqualityComparer<T>.Default;
- if (linkedListNode != null)
- {
- if ((object) value != null)
- {
- while (!@default.Equals(linkedListNode.item, value))
- {
- linkedListNode = linkedListNode.next;
- if (linkedListNode == this.head)
- goto label_8;
- }
- return linkedListNode;
- }
- else
- {
- while ((object) linkedListNode.item != null)
- {
- linkedListNode = linkedListNode.next;
- if (linkedListNode == this.head)
- goto label_8;
- }
- return linkedListNode;
- }
- }
- label_8:
- return (LinkedListNode<T>) null;
- }
删除某个节点的操作如下:
- public void Remove(LinkedListNode<T> node)
- {
- this.ValidateNode(node);
- this.InternalRemoveNode(node);
- }
同样,内部删除节点的实现如下:如果节点指向自己,说明是头节点,所以直接把头节点置null。然后就是指针的指向操作了。
- internal void InternalRemoveNode(LinkedListNode<T> node)
- {
- if (node.next == node)
- {
- this.head = (LinkedListNode<T>) null;
- }
- else
- {
- node.next.prev = node.prev;
- node.prev.next = node.next;
- if (this.head == node)
- this.head = node.next;
- }
- node.Invalidate();
- --this.count;
- ++this.version;
- }
而清空链表的操作如下:遍历链表,逐个设置为无效,最后将内部的头节点也置为null。
- public void Clear()
- {
- LinkedListNode<T> linkedListNode1 = this.head;
- while (linkedListNode1 != null)
- {
- LinkedListNode<T> linkedListNode2 = linkedListNode1;
- linkedListNode1 = linkedListNode1.Next;
- linkedListNode2.Invalidate();
- }
- this.head = (LinkedListNode<T>) null;
- this.count = 0;
- ++this.version;
- }
- public void CopyTo(T[] array, int index)
- {
- if (array == null)
- throw new ArgumentNullException("array");
- if (index < 0 || index > array.Length)
- {
- throw new ArgumentOutOfRangeException("index", SR.GetString("IndexOutOfRange", new object[1]
- {
- (object) index
- }));
- }
- else
- {
- if (array.Length - index < this.Count)
- throw new ArgumentException(SR.GetString("Arg_InsufficientSpace"));
- LinkedListNode<T> linkedListNode = this.head;
- if (linkedListNode == null)
- return;
- do
- {
- array[index++] = linkedListNode.item;
- linkedListNode = linkedListNode.next;
- }
- while (linkedListNode != this.head);
- }
- }
以上。
.NET源码中的链表的更多相关文章
- ASP.NET MVC Filters 4种默认过滤器的使用【附示例】 数据库常见死锁原因及处理 .NET源码中的链表 多线程下C#如何保证线程安全? .net实现支付宝在线支付 彻头彻尾理解单例模式与多线程 App.Config详解及读写操作 判断客户端是iOS还是Android,判断是不是在微信浏览器打开
ASP.NET MVC Filters 4种默认过滤器的使用[附示例] 过滤器(Filters)的出现使得我们可以在ASP.NET MVC程序里更好的控制浏览器请求过来的URL,不是每个请求都会响 ...
- Android 源码中的设计模式
最近看了一些android的源码,发现设计模式无处不在啊!感觉有点乱,于是决定要把设计模式好好梳理一下,于是有了这篇文章. 面向对象的六大原则 单一职责原则 所谓职责是指类变化的原因.如果一个类有多于 ...
- 从express源码中探析其路由机制
引言 在web开发中,一个简化的处理流程就是:客户端发起请求,然后服务端进行处理,最后返回相关数据.不管对于哪种语言哪种框架,除去细节的处理,简化后的模型都是一样的.客户端要发起请求,首先需要一个标识 ...
- Android 网络框架之Retrofit2使用详解及从源码中解析原理
就目前来说Retrofit2使用的已相当的广泛,那么我们先来了解下两个问题: 1 . 什么是Retrofit? Retrofit是针对于Android/Java的.基于okHttp的.一种轻量级且安全 ...
- Eclipse与Android源码中ProGuard工具的使用
由于工作需要,这两天和同事在研究android下面的ProGuard工具的使用,通过查看android官网对该工具的介绍以及网络上其它相关资料,再加上自己的亲手实践,算是有了一个基本了解.下面将自己的 ...
- String源码中的"avoid getfield opcode"
引言: 之前一篇文章梳理了String的不变性原则,还提到了一段源码中注释"avoid getfield opcode",当时通过查阅资料发现,这是为了防止 getfield(获取 ...
- android源码中修改wifi热点默认始终开启
在项目\frameworks\base\wifi\java\android\net\wifi\WifiStateMachine.java里面,有如下的代码,是设置wifi热点保持状态的:如下: pri ...
- rxjava源码中的线程知识
rxjava源码中的线程知识 rx的最精简的总结就是:异步 这里说一下以下的五个类 1.Future2.ConcurrentLinkedQueue3.volatile关键字4.AtomicRefere ...
- MMS源码中异步处理简析
1,信息数据的查询,删除使用AsycnQueryHandler处理 AsycnQueryHandler继承了Handler public abstract class AsyncQueryHandle ...
随机推荐
- SQLite3命令操作大全
SQLite3命令操作大全 SQLite库包含一个名字叫做sqlite3的命令行,它可以让用户手工输入并执行面向SQLite数据库的SQL命令.本文档提供一个样使用sqlite3的简要说明. 一.ql ...
- (PMP)第10章-----项目沟通管理
10.1 规划沟通管理 相关方的数量:(n * (n-1))/2 交互式沟通,推式沟通,拉式沟通 沟通管理计划 10.2 管理沟通 电子项目管理工具 电子沟通管理 社交媒体管理 10.3 监督沟通 B
- 高效率php注意事项
1.尽量静态化: 如果一个方法能被静态,那就声明它为静态的,速度可提高1/4,甚至我测试的时候,这个提高了近三倍. 当然了,这个测试方法需要在十万级以上次执行,效果才明显. 其实静态方法和非静态方法的 ...
- Raft知识图谱
- UML标准建模语言与应用实例
一.基本信息 标题:UML标准建模语言与应用实例 时间:2012 出版源:科技创新导报 领域分类:UML标准建模语言 面向对象 系统分析与设计 二.研究背景 问题定义:UML建模语言用图形来表现典型的 ...
- NET Core微服务之路:实战SkyWalking+Exceptionless体验生产环境下的追踪系统
前言 当一个APM或一个日志中心实际部署在生产环境中时,是有点力不从心的. 比如如下场景分析的问题: 从APM上说,知道某个节点出现异常,或延迟过过高,却不能及时知道日志反馈情况,总不可能去相应的节点 ...
- Ubuntu 16.04虚拟机调整窗口大小自适应Windows 7
Windows 7上Ubuntu 16.04虚拟机安装成功后,默认的虚拟机窗口比较小,需要适当调整,才能把虚拟机的屏幕放大, 适合使用,以下介绍调整方法. 安装VMware Tools 启动虚拟机, ...
- 《Opencv 3 计算机视觉 python语言实现》· 第二遍 —— 读后笔记
概览 代码实战 https://github.com/xinghalo/ml-in-action/tree/master/book-opencv
- 开源播放器 ijkplayer (一) :使用Ijkplayer播放直播视频
1.ijkplayer 编码 IjkPlayer支持硬解码和软解码. 软解码时不会旋转视频角度这时需要你通过onInfo的what == IMediaPlayer.MEDIA_INFO_VIDEO_R ...
- 使用cygwin中的awk工具进行mysql binlog日志查看[利刃篇]
linux工具确实强悍,然而作为没有linux机器使用权以及开发没有使用linux进行的人,有时想用一些命令确实不方便,所以,才去试着用用cygwin,一款在windows平台上运行的类UNIX模拟环 ...