zoukankan      html  css  js  c++  java
  • .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>在保证节省内存空间的同时,在添加数据的顺序方面有极大的灵活性,加上泛型本身避免装箱拆箱的优点,需要用链表的时候,应优先考虑泛型链表。

  • 相关阅读:
    771. Jewels and Stones
    706. Design HashMap
    811. Subdomain Visit Count
    733. Flood Fill
    117. Populating Next Right Pointers in Each Node II
    250. Count Univalue Subtrees
    94. Binary Tree Inorder Traversal
    116. Populating Next Right Pointers in Each Node
    285. Inorder Successor in BST
    292. Nim Game Java Solutin
  • 原文地址:https://www.cnblogs.com/soundcode/p/2108669.html
Copyright © 2011-2022 走看看