C# 常用集合

ArrayList

根据大小动态添加的数据

  • ArrayList不保证进行排序。 在执行 (操作(例如BinarySearch需要 ArrayList 排序的) )之前,必须ArrayList调用其 Sort 方法对 进行排序。 若要维护在添加新元素时自动排序的集合,可以使用 SortedSet 类。

  • 将元素添加到 时 ArrayList,容量会根据需要通过重新分配自动增加。 可以通过调用 TrimToSize 或 显式设置 属性来 Capacity 减小容量。

  • 仅对于.NET Framework:对于非常大ArrayList的对象,可以通过在运行时环境中将配置元素的 属性设置为 enabled ,将 64 位系统上的最大容量增加到 true 20 亿个元素。
    可以使用整数索引访问此集合中的元素。 此集合中的索引从零开始。

  • 集合 ArrayList 接受 null 作为有效值。 它还允许重复元素。

  • 不支持将多维数组用作集合中的 ArrayList 元素。

  • ArrayList会进行装修和拆箱操作

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SetTest
{
    internal class ArrayListTest : ISet
    {
        public void Show()
        {
            ArrayList myAL = new ArrayList();
            myAL.Add("Hello");
            myAL.Add("World");
            myAL.Add("!");

            Console.WriteLine($"个数:{myAL.Count}");
            Console.WriteLine($"Capacity:{myAL.Capacity}");
            Console.Write("值为:");
            foreach (Object obj in myAL)
            {
                Console.Write(obj + " ");
            }
            Console.WriteLine();
        }
    }
}

微软建议:不建议使用 ArrayList 类进行新的开发。 建议改用泛型 List

List

可通过索引访问的对象的强类型集合

  • List 等效于ArrayList,它实现了IList

  • 它在System.Collection.Generic命名空间下。

  • List可以包含指定类型的元素。它提供编译时类型检查,并且不执行装箱/拆箱,因为它是泛型的。

  • 可以使用Add(),AddRange()方法或collection-initializer(集合初始化器)语法添加元素。

  • 可以通过传递索引来访问元素,例如myList[0]。索引从零开始。

  • List与ArrayList相比,执行速度更快,出错更少。

  • List 接受 null 作为引用类型的有效值,并允许重复元素

public class Part : IEquatable<Part>
    {
        public string PartName { get; set; }

        public int PartId { get; set; }

        public override string ToString()
        {
            return "ID: " + PartId + "   Name: " + PartName;
        }

        public override bool Equals(object obj)
        {
            if (obj == null) return false;
            Part objAsPart = obj as Part;
            if (objAsPart == null) return false;
            else return Equals(objAsPart);
        }

        public override int GetHashCode()
        {
            return PartId;
        }

        public bool Equals(Part other)
        {
            if (other == null) return false;
            return (this.PartId.Equals(other.PartId));
        }
    }
public class ListTest : ISet
    {
        public void Show()
        {
            List<Part> parts = new List<Part>();

            parts.Add(new Part() { PartName = "Test1", PartId = 1234 });
            parts.Add(new Part() { PartName = "Test2", PartId = 1334 });
            parts.Add(new Part() { PartName = "Test3", PartId = 1434 });
            parts.Add(new Part() { PartName = "Test4", PartId = 1444 });
            parts.Add(new Part() { PartName = "Test5", PartId = 1534 });
            parts.Add(new Part() { PartName = "Test6", PartId = 1634 });

            Console.WriteLine();
            foreach (Part aPart in parts)
            {
                Console.WriteLine(aPart);
            }

            parts.Contains(new Part { PartId = 1734, PartName = "" });

            Console.WriteLine("\nInsert(2, \"1834\")");
            parts.Insert(2, new Part() { PartName = "Test Insert", PartId = 1834 });
            foreach (Part aPart in parts)
            {
                Console.WriteLine(aPart);
            }

            Console.WriteLine("\nParts[3]: {0}", parts[3]);

            Console.WriteLine("\nRemove(\"1534\")");

            parts.Remove(new Part() { PartId = 1534, PartName = "Test5" });

            Console.WriteLine();
            foreach (Part aPart in parts)
            {
                Console.WriteLine(aPart);
            }
            Console.WriteLine("\nRemoveAt(3)");

            parts.RemoveAt(3);

            Console.WriteLine();
            foreach (Part aPart in parts)
            {
                Console.WriteLine(aPart);
            }
        }
    }

以上的结果为:

Queue

表示先进先出的集合

  • Enqueue将元素添加到 Queue

  • Dequeue 从 Queue开头删除开头的元素。

  • Peek 返回开头的元素,该元素位于开头 Queue ,但不删除该元素

  • Queue 接受 null 为有效值,并允许重复元素。

  • 没有实现 IList,ICollection。所以它不能按索引访问元素,不能使用Add和Remove

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SetTest
{
    public class QueueTest : ISet
    {
        public void Show()
        {
            Queue myQ = new Queue();
            myQ.Enqueue("Hello");
            myQ.Enqueue("World");
            myQ.Enqueue("!");

            Console.WriteLine("数量为:{0}", myQ.Count);
            Console.Write("值:");
            foreach (Object obj in myQ)
            {
                Console.Write("    {0}", obj);
            }
            Console.WriteLine();
        }
    }
}

以上的执行结果为:

Stack

后进先出的集合

  • Push将元素添加到 Stack

  • Pop 从 Stack开头删除开头的元素。

  • Queue 接受 null 为有效值,并允许重复元素。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SetTest
{
    public class StackTest : ISet
    {
        public void Show()
        {
            Stack myStack = new Stack();
            myStack.Push("Hello");
            myStack.Push("World");
            myStack.Push("!");

            Console.WriteLine("数量:{0}", myStack.Count);
            Console.Write("值:");
            foreach (Object obj in myStack)
            {
                Console.Write("    {0}", obj);
            }
            Console.WriteLine();
        }
    }
}

以上的执行的结果为:

LinkedList

双重链表

  • LinkedList 是通用链表。它支持枚举器。

  • 插入和移除是O(1)操作。

  • 你可以删除节点并将它们重新插入同一列表或另一个列表中, 这将导致在堆上没有分配其他对象。

  • 由于列表还维护内部计数, 因此获取Count属性是O(1)操作。

  • LinkedList中的每个节点T>对象的类型为LinkedListNode

  • LinkedList类不支持链接, 拆分, 循环或其他可能会使列表处于不一致状态的功能。

  • 如果LinkedList为空, 则第一和持续属性包含null。

  • LinkedList是双重链接的, 因此, 每个节点都指向下一个节点, 并指向上一个节点

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SetTest
{
    public class LinkedListTest : ISet
    {
        public void Show()
        {
            string[] words = { "the", "fox", "jumps", "over", "the", "dog" };
            LinkedList<string> sentence = new LinkedList<string>(words);
            Display(sentence, "链表值为:");
            Console.WriteLine("链表是否包含值(\"jumps\") = {0}", sentence.Contains("jumps"));

            sentence.AddFirst("today");
            Display(sentence, "测试 1: 新增值:'today'到链表开头:");

            LinkedListNode<string> mark1 = sentence.First;
            sentence.RemoveFirst();
            sentence.AddLast(mark1);
            Display(sentence, "测试 2: 将第一个节点移动到最后一个节点:");

            sentence.RemoveLast();
            sentence.AddLast("yesterday");
            Display(sentence, "测试 3:修改最后一个节点的值为: 'yesterday':");

            mark1 = sentence.Last;
            sentence.RemoveLast();
            sentence.AddFirst(mark1);
            Display(sentence, "测试 4: 将最后一个节点移动到第一个节点:");

            sentence.RemoveFirst();
            LinkedListNode<string> current = sentence.FindLast("the");
            IndicateNode(current, "测试 5: 找到最后一个值为: 'the' 的节点:");

            sentence.AddAfter(current, "old");
            sentence.AddAfter(current, "lazy");
            IndicateNode(current, "测试 6: 将值: 'lazy' 和 'old' 添加到 'the' 之后:");

            current = sentence.Find("fox");
            IndicateNode(current, "测试 7: 找到值为: 'fox' 的节点:");

            sentence.AddBefore(current, "quick");
            sentence.AddBefore(current, "brown");
            IndicateNode(current, "测试 8: 将值为: 'quick' 和 'brown' 添加到值: 'fox' 之前:");

            mark1 = current;
            LinkedListNode<string> mark2 = current.Previous;
            current = sentence.Find("dog");
            IndicateNode(current, "测试 9: 找到值为: 'dog' 的节点:");

            Console.WriteLine("测试 10: 抛出一个异常: 添加一个已经存在的节点值 'fox' :");
            try
            {
                sentence.AddBefore(current, mark1);
            }
            catch (InvalidOperationException ex)
            {
                Console.WriteLine("错误信息为: {0}", ex.Message);
            }
            Console.WriteLine();

            sentence.Remove(mark1);
            sentence.AddBefore(current, mark1);
            IndicateNode(current, "测试 11: 在节点'fox'前添加节点 'dog':");

            sentence.Remove(current);
            IndicateNode(current, "测试 12: 删除节点 'dog' and attempt to indicate it:");

            sentence.AddAfter(mark2, current);
            IndicateNode(current, "测试 13: 在节点'' 后添加新节点 'brown':");

            sentence.Remove("old");
            Display(sentence, "测试 14:删除 'old' 节点值:");

            sentence.RemoveLast();
            ICollection<string> icoll = sentence;
            icoll.Add("rhinoceros");
            Display(sentence, "测试 15: 删除最后一个节点, 将链表转化成 ICollection, 然后添加值: 'rhinoceros':");

            Console.WriteLine("测试 16: 拷贝当前链表至数组:");
            string[] sArray = new string[sentence.Count];
            sentence.CopyTo(sArray, 0);

            foreach (string s in sArray)
            {
                Console.WriteLine(s);
            }

            //清空链表
            sentence.Clear();

            Console.WriteLine();
            Console.WriteLine("测试 17: Clear linked list. Contains 'jumps' = {0}", sentence.Contains("jumps"));

            Console.ReadLine();
        }

        private static void Display(LinkedList<string> words, string test)
        {
            Console.WriteLine(test);
            foreach (string word in words)
            {
                Console.Write(word + " ");
            }
            Console.WriteLine();
            Console.WriteLine();
        }

        private static void IndicateNode(LinkedListNode<string> node, string test)
        {
            Console.WriteLine(test);
            if (node.List == null)
            {
                Console.WriteLine("Node '{0}' is not in the list.\n",
                    node.Value);
                return;
            }

            StringBuilder result = new StringBuilder("(" + node.Value + ")");
            LinkedListNode<string> nodeP = node.Previous;

            while (nodeP != null)
            {
                result.Insert(0, nodeP.Value + " ");
                nodeP = nodeP.Previous;
            }

            node = node.Next;
            while (node != null)
            {
                result.Append(" " + node.Value);
                node = node.Next;
            }

            Console.WriteLine(result);
            Console.WriteLine();
        }
    }
}

以上的输出结果为:

SortedList

有序键值对集合

  • SortedList对象在内部维护两个数组来存储列表的元素;即,一个数组用于键Key,另一个数组用于关联值Value。 每个元素都是可作为 对象访问 DictionaryEntry 的键/值对。

  • 对象的容量 SortedList 是 可以容纳的元素 SortedList 数。 将元素添加到 时 SortedList,容量会根据需要通过重新分配自动增加。 可以通过调用 TrimToSize 或 显式设置 属性来 Capacity 减小容量。

  • 仅.NET Framework:对于非常大SortedList的对象,可以通过在运行时环境中将配置元素的 gcAllowVeryLargeObjects 属性设置为 enabled ,将 64 位系统上的最大容量增加到 20 亿个元素。

  • 对象的元素SortedList根据创建 时SortedList指定的特定IComparer实现或IComparable键本身提供的实现按键排序。

  • 在任一 SortedList 情况下, 都不允许重复键。

  • 索引序列基于排序序列。 添加元素时,它将按正确的排序顺序插入到 中 SortedList ,索引会相应地调整。 删除元素时,索引也会相应地进行调整。 因此,在对象中添加或移除元素时,特定键/值对的 SortedList 索引可能会更改。

  • 由于排序, SortedList 对对象的操作往往比对 Hashtable 对象的操作慢。 但是, SortedList 允许通过关联的键或索引访问值,从而提供了更大的灵活性。

  • 可以使用整数索引访问此集合中的元素。 此集合中的索引从零开始。

  • 键不能是 null,但值可以是 。

using System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SetTest
{
    public class SortedListTest : ISet
    {
        public void Show()
        {
            SortedList mySL = new SortedList();
            mySL.Add("Third", "!");
            mySL.Add("Second", "World");
            mySL.Add("First", "Hello");

            Console.WriteLine("个数:{0}", mySL.Count);
            Console.WriteLine("容量:{0}", mySL.Capacity);
            Console.WriteLine("键和值:");
            Console.WriteLine("-键-\t-值-");
            for (int i = 0; i < mySL.Count; i++)
            {
                Console.WriteLine("{0}:\t{1}", mySL.GetKey(i), mySL.GetByIndex(i));
            }
            Console.WriteLine();
        }
    }
}

以上测试的输出结果:

Dictionary

键值对集合

  • 属于System.Collection.Generic命名空间。

  • 实现 IDictionary <TKey,TValue>接口。

  • 键必须是唯一的,不能为null。值可以为null或重复。

  • Dictionary里面的每一个元素都是一个键值对(由二个元素组成:键和值)

  • 键必须是唯一的,而值不需要唯一的

  • 键和值都可以是任何类型(比如:string, int, 自定义类型等等)

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SetTest
{
    internal class DictionaryTest : ISet
    {
        public void Show()
        {
            Dictionary<string, string> openWith =
                new Dictionary<string, string>();

            openWith.Add("txt", "notepad.exe");
            openWith.Add("bmp", "paint.exe");
            openWith.Add("dib", "paint.exe");
            openWith.Add("rtf", "wordpad.exe");

            try
            {
                openWith.Add("txt", "winword.exe");
            }
            catch (ArgumentException)
            {
                Console.WriteLine("Key为\"txt\"已经存在");
            }

            Console.WriteLine("key为\"rtf\", 字典内值为{0}",
                openWith["rtf"]);

            openWith["rtf"] = "winword.exe";
            Console.WriteLine("key为\"rtf\", 字典内值为{0}",
                openWith["rtf"]);

            openWith["doc"] = "winword.exe";

            try
            {
                Console.WriteLine("key为\"tif\", 字典内值为{0}",
                    openWith["tif"]);
            }
            catch (KeyNotFoundException)
            {
                Console.WriteLine("Key为\"tif\"的字典值不存在");
            }

            string value = "";
            if (openWith.TryGetValue("tif", out value))
            {
                Console.WriteLine("key为\"tif\", 字典内值为{0}.", value);
            }
            else
            {
                Console.WriteLine("Key为\"tif\"的字典值不存在.");
            }

            if (!openWith.ContainsKey("ht"))
            {
                openWith.Add("ht", "hypertrm.exe");
                Console.WriteLine("值新增到 key 为 \"ht\"中: {0}",
                    openWith["ht"]);
            }

            Console.WriteLine("");
            Console.WriteLine("打印字典如下");
            foreach (KeyValuePair<string, string> kvp in openWith)
            {
                Console.WriteLine("Key = {0}, Value = {1}",
                    kvp.Key, kvp.Value);
            }

            Dictionary<string, string>.ValueCollection valueColl =
                openWith.Values;

            Console.WriteLine();
            Console.WriteLine("打印值如下");
            foreach (string s in valueColl)
            {
                Console.WriteLine("Value = {0}", s);
            }

            Dictionary<string, string>.KeyCollection keyColl =
                openWith.Keys;

            Console.WriteLine();
            Console.WriteLine("打印键如下");
            foreach (string s in keyColl)
            {
                Console.WriteLine("Key = {0}", s);
            }

            Console.WriteLine("\n删除key为(\"doc\")");
            openWith.Remove("doc");

            if (!openWith.ContainsKey("doc"))
            {
                Console.WriteLine("key为 \"doc\" 不存在.");
            }
        }
    }
}

以上的输出结果为:

Immutable Collection

不可变对象集合,其中包括以下的具体类型

  1. ImmutableArray
  2. ImmutableStack
  3. ImmutableQueue
  4. ImmutableList
  5. ImmutableHashSet
  6. ImmutableSortedSet
  7. ImmutableDictionary<K, V>
  8. ImmutableSortedDictionary<K, V>
  • 对不可靠的客户代码库来说,它使用安全,可以在未受信任的类库中安全的使用这些对象
  • 线程安全的:immutable对象在多线程下安全,没有竞态条件
  • 不需要支持可变性, 可以尽量节省空间和时间的开销. 所有的不可变集合实现都比可变集合更加有效的利用内存 (analysis)
  • 可以被使用为一个常量,并且期望在未来也是保持不变的
using System;
using System.Collections.Generic;
using System.Collections.Immutable;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SetTest
{
    public class ImmutableListTest : ISet
    {
        public void Show()
        {
            ImmutableList<string> colors = ImmutableList.Create("Red", "Green", "Blue");

            ImmutableList<string> colorsModified = colors.RemoveAt(1).Add("Orange");

            foreach (string s in colorsModified)
            {
                Console.WriteLine(s);
            }

            var colorsModifiedBuilder = colors.ToBuilder();
            colorsModifiedBuilder.Add("Black");
            ImmutableList<string> colorsModified2 = colorsModifiedBuilder.ToImmutable();

            Console.WriteLine();
            foreach (string s in colorsModified2)
            {
                Console.WriteLine(s);
            }
        }
    }
}

以上的输出结果为:

ReadOnlyCollection

只读集合

  • ReadOnlyCollection只是对原对象的包装。修改原对象还是会影响当前对象。所以不是线程安全的
using System;
using System.Collections.Generic;
using System.Collections.ObjectModel;
using System.Linq;
using System.Text;
using System.Threading.Tasks;

namespace SetTest
{
    public class ReadOnlyCollectionTest : ISet
    {
        public void Show()
        {
            List<string> dinosaurs = new List<string>();

            dinosaurs.Add("Tyrannosaurus");
            dinosaurs.Add("Amargasaurus");
            dinosaurs.Add("Deinonychus");
            dinosaurs.Add("Compsognathus");

            ReadOnlyCollection<string> readOnlyDinosaurs =
                new ReadOnlyCollection<string>(dinosaurs);

            Console.WriteLine("只读集合输出结果如下:");
            foreach (string dinosaur in readOnlyDinosaurs)
            {
                Console.WriteLine(dinosaur);
            }

            Console.WriteLine("\n数量: {0}", readOnlyDinosaurs.Count);

            Console.WriteLine("\n是否包含(\"Deinonychus\"): {0}",
                readOnlyDinosaurs.Contains("Deinonychus"));

            Console.WriteLine("\nreadOnlyDinosaurs[3]: {0}",
                readOnlyDinosaurs[3]);

            Console.WriteLine("\n(\"Compsognathus\")位置: {0}",
                readOnlyDinosaurs.IndexOf("Compsognathus"));

            Console.WriteLine("\n插入到原集合中:");
            Console.WriteLine("Insert(2, \"Oviraptor\")");
            dinosaurs.Insert(2, "Oviraptor");

            Console.WriteLine("只读集合输出结果如下:");
            foreach (string dinosaur in readOnlyDinosaurs)
            {
                Console.WriteLine(dinosaur);
            }

            string[] dinoArray = new string[readOnlyDinosaurs.Count + 2];
            readOnlyDinosaurs.CopyTo(dinoArray, 1);

            Console.WriteLine("\n复制的只读集合包含{0}个:",
                dinoArray.Length);
            foreach (string dinosaur in dinoArray)
            {
                Console.WriteLine("\"{0}\"", dinosaur);
            }
        }
    }
}

以上代码的输出结果为:

Concurrent Collection

其中包括以下具体类型
BlockingCollection
ConcurrentDictionary<TKey,TValue>
ConcurrentQueue
ConcurrentStack
ConcurrentBag
IProducerConsumerCollection

  1. BlockingCollection

提供具有阻塞和限制功能的线程安全集合,实现IProducerConsumerCollection接口。支持容量大小的限制和完成添加限制,当标记为完成添加后只允许移除(Take)操作,无法进行添加(Add)操作。

BoundedCapacity:获取集合限定容量,在通过构造函数时可以指定集合的容量大小。
IsAddingCompleted:判断是否已经标记完成添加,
IsCompleted:判断是否标记完成添加并且为空。
Add:将项添加到集合中。
CompleteAdding:将集合标记为完成添加。调用该函数后IsAddingCompleted为true,如果集合中包含的项数为0,那么IsCompleted也为true。
Take:从集合中移除一项。
TryAdd:尝试将项加入集合
TryTake:尝试从集合中移除项。

  1. ConcurrentBag

提供可供多个线程同时安全访问的无序包。对应非泛型列表List
包(Bag)和数据上的集(Set)的区别是包可包含重复元素,而集中不能包含重复元素。

Count:获取无序包中的元素数量
IsEmpty:判断无序包是否为空。
TryPeek:从无序包中获取一个元素,但不进行移除。
TryTask:从无序包获取一个元素并移除。

  1. ConcurrentDictionary<TKey,TValue>

提供可供多线程同时访问的键值对的线程安全集合,对应Dictionary<TKey, TValue>。

IsEmpty:判断字典是否为空。
AddOrUpdate:将键值对添加到字典中,如果Key值已经存在,则更新Value值。
Clear:将所有键值对从字典中移除。
GetOrAdd:如果键不存在,则将键值对添加到字典中并返回Value值,如果键已经存在,则返回现有值,不会更新现有值。
TryUpdate:尝试更新键对应的Value值。

  1. ConcurrentQueue

队列,提供线程安全的先进先出(FIFO)集合,对应Queue

Enqueue:将对象添加到队列末尾处。
TryDequeue:尝试移除并返回位于队列开头处的对象。
TryPeek:尝试返回队列开头处的对象但不将其移除。

  1. ConcurrentStack

栈,提供线程安全的后进先出(LIFO)集合,对应Stack

Push:将对象插入栈顶部。
PushRange:将多个对象插入栈的顶部。
TryPeek:尝试返回栈顶部的对象但不将其移除。
TryPop:尝试弹出并返回栈顶部的对象。
TryPopRange:尝试弹出并返回栈顶部的多个对象。

posted @ 2023-09-17 13:17  说不出来  阅读(23)  评论(0编辑  收藏  举报