.net集合类的研究--链表—ListDictionary,LinkedList<T>

简介:

链表是数据结构中存储数据的一种形式,我们经常使用的List<T>,ArrayList,Hashtable等容器类,存取操作时是用数组Array来保存,ListDictionary和LinkedList<T>不用Array,而是用链表的形式来保存。

链表的优点和缺点

以ListDictionary为例,在源码中,看不到Array类型的的变量,取而代之的是一个DictionaryNode类型的变量,查看该类的源码会发现,只包含一个key,一个value,和一个DictionaryNode类型的next变量,DictionaryNode的代码如下

private class DictionaryNode
{
    public object key;
    public ListDictionary.DictionaryNode next;
    public object value;
}

添加数据的时候,直接把当前节点的next变量赋值为新的节点,这样一个节点扣一个节点,就有了链的形式。

在链表中查找数据时,如调用Contains(object key) :bool 方法,需要从链表的头节点依次遍历,逐个匹配,所以时间复杂度为O(n),和List<T>,ArrayList相比,在查询效率上并没有太大的区别

那么链表的优势在哪里呢?答案是,节省内存空间

在之前的文章有提到过,线性表和哈希表初始化时会将内部Array数组默认一个大小,List<T>的初始值为4,Hashtable的为11,当添加数据碰到容量不足时,会将当前数组扩充2倍,这种做法不可避免要造成浪费。而链表不用数组保存,用节点相连,实实在在,添加几个节点,就占用几个节点的内存,相对于线性表和哈希表,链表没有浪费,因而占用内存空间较少。

除了节省空间以外,链表还有一个优点,那就是插入数据的灵活性

可惜这一点在ListDictionary中并没有体现,每次添加数据,ListDictionary都要遍历整个链表,来确保没有重复节点,导致每次添加都要循环一次,添加数据的时间复杂度和查询数据的时间复杂度都为O(n),比线性表和哈希表要慢的多。

HybridDictionary-结合链表和哈希表的特点扬长避短

在.net的集合容器中,有一个名为HybridDictionary的类,充分利用了Hashtable查询效率高和ListDictionary占用内存空间少的优点,内置了Hashtable和ListDictionary两个容器,添加数据时内部逻辑如下:

当数据量小于8时,Hashtable为null,用ListDictionary保存数据。

当数据量大于8时,实例化Hashtable,数据转移到Hashtable中,然后将ListDictionary置为null。

HybridDictionary的Add方法的代码如下:

public void Add(object key, object value)
{
    if (this.hashtable != null)
    {
        this.hashtable.Add(key, value);
    }
    else if (this.list == null)
    {
        this.list = new ListDictionary(this.caseInsensitive ? StringComparer.OrdinalIgnoreCase : null);
        this.list.Add(key, value);
    }
    else if ((this.list.Count + 1) >= 9)
    {
        //当数据量大于8时,则调用该方法,实例化Hashtable,转移数据,清空list
        this.ChangeOver();
        this.hashtable.Add(key, value);
    }
    else
    {
        this.list.Add(key, value);
    }
}

HybridDictionary类也进一步说明出了链表ListDictionary的特点:相对于Hashtable,占用内存较少,但随着数据量的增加,查询效率远不及Hashtable。

泛型链表-LinkedList<T>

LinkedList是泛型链表,也是用节点存取,节点类型为LinkedListNode<T> ,与ListDictionary的节点不同的是,LinkedListNode<T>有next和prev两个指向,说明LinkedList<T>是双向链表,而ListDictionary是单向链表,代码如下:

public sealed class LinkedListNode<T>
{
    // Fields
    internal T item;
    internal LinkedList<T> list;
    internal LinkedListNode<T> next;
    internal LinkedListNode<T> prev;

    ......
}

除了节省内存空间外,链表的另一个优点--插入数据的灵活性,在LinkedList<T>中完全体现出来,共有4个不同位置的添加数据的方法,分别为链头插入,链尾插入,节点前插入,节点后插入。

每种插入方法又分别有两种插入模式:

1、直接插入LinkedListNode<T>,没有返回值。

2、直接插入T类型的值,返回插入完成后的节点。

四种位置,两种模式,一共就有8个插入数据的方法,运用这些方法,可以在添加数据时灵活控制链表中数据的顺序,这个优势是线性表和哈希表所不能比的。代码如下:

public LinkedListNode<T> AddAfter(LinkedListNode<T> node, T value);
public void AddAfter(LinkedListNode<T> node, LinkedListNode<T> newNode);
public void AddBefore(LinkedListNode<T> node, LinkedListNode<T> newNode);
public LinkedListNode<T> AddBefore(LinkedListNode<T> node, T value);
public void AddFirst(LinkedListNode<T> node);
public LinkedListNode<T> AddFirst(T value);
public LinkedListNode<T> AddLast(T value);
public void AddLast(LinkedListNode<T> node);

此外,由于LinkedList<T>是双向链表,在查询数据方面提供了“从前往后”和“从后往前”两个查询方法,所以虽然理论上链表的时间复杂度为O(n),根据自己在插入数据时对顺序的把握,结合这两个方法,可以相对提高查询效率。

public LinkedListNode<T> Find(T value);//从前往后查
public LinkedListNode<T> FindLast(T value);//从后往前查

结论

相对于线性表和哈希表,链表比较节省内存空间。

ListDictionary在每次添加数据时都要遍历链表,效率较低,数据量较大且插入频繁的情况下,不宜选用。

泛型链表LinkedList<T>在保证节省内存空间的同时,在添加数据的顺序方面有极大的灵活性,加上泛型本身避免装箱拆箱的优点,需要用链表的时候,应优先考虑泛型链表。


本文转自左正博客园博客,原文链接:http://www.cnblogs.com/soundcode/archive/2011/07/17/2108669.html,如需转载请自行联系原作者

目录
相关文章
|
11月前
|
开发框架 .NET C#
C#|.net core 基础 - 删除字符串最后一个字符的七大类N种实现方式
【10月更文挑战第9天】在 C#/.NET Core 中,有多种方法可以删除字符串的最后一个字符,包括使用 `Substring` 方法、`Remove` 方法、`ToCharArray` 与 `Array.Copy`、`StringBuilder`、正则表达式、循环遍历字符数组以及使用 LINQ 的 `SkipLast` 方法。
348 8
|
10月前
|
Java 物联网 C#
C#/.NET/.NET Core学习路线集合,学习不迷路!
C#/.NET/.NET Core学习路线集合,学习不迷路!
394 0
|
7月前
|
人工智能 机器人
D1net阅闻 | 谷歌DeepMind研究发现LLM新特性
D1net阅闻 | 谷歌DeepMind研究发现LLM新特性
|
9月前
|
JSON 安全 API
.net 自定义日志类
在.NET中,创建自定义日志类有助于更好地管理日志信息。示例展示了如何创建、配置和使用日志记录功能,包括写入日志文件、设置日志级别、格式化消息等。注意事项涵盖时间戳、日志级别、JSON序列化、线程安全、日志格式、文件处理及示例使用。请根据需求调整代码。
135 13
|
9月前
|
JSON 数据格式
.net HTTP请求类封装
`HttpRequestHelper` 是一个用于简化 HTTP 请求的辅助类,支持发送 GET 和 POST 请求。它使用 `HttpClient` 发起请求,并通过 `Newtonsoft.Json` 处理 JSON 数据。示例展示了如何使用该类发送请求并处理响应。注意事项包括:简单的错误处理、需安装 `Newtonsoft.Json` 依赖,以及建议重用 `HttpClient` 实例以优化性能。
229 2
|
11月前
|
开发框架 NoSQL MongoDB
C#/.NET/.NET Core开发实战教程集合
C#/.NET/.NET Core开发实战教程集合
203 1
|
11月前
.NET 4.0下实现.NET4.5的Task类相似功能组件
【10月更文挑战第29天】在.NET 4.0 环境下,可以使用 `BackgroundWorker` 类来实现类似于 .NET 4.5 中 `Task` 类的功能。`BackgroundWorker` 允许在后台执行耗时操作,同时不会阻塞用户界面线程,并支持进度报告和取消操作。尽管它有一些局限性,如复杂的事件处理模型和不灵活的任务管理方式,但在某些情况下仍能有效替代 `Task` 类。
140 0
|
11月前
|
API
使用`System.Net.WebClient`类发送HTTP请求来调用阿里云短信API
使用`System.Net.WebClient`类发送HTTP请求来调用阿里云短信API
183 0
|
存储 Java
|
缓存 程序员
封装一个给 .NET Framework 用的内存缓存帮助类
封装一个给 .NET Framework 用的内存缓存帮助类
128 1