zoukankan      html  css  js  c++  java
  • Collection

        • 1.概述
        • 2.集合接口和类型
          • 2.1集合接口
            • 2.1.1IList<T>与IDictionary<TKey, TValue>
            • 2.1.2ICollection<T>
          • 2.2主要集合类
          • 2.3索引器
            • 2.3.1 定义索引器
            • 2.3.2 索引类型
            • 2.3.3 接口中的索引
          • 2.4迭代器
            • 2.4.1 简单的迭代器
            • 2.4.2 创建集合类
            • 2.4.3 对泛型列表使用迭代器
            • 2.4.4 语法信息
          • 2.5返回null或空集合
        • 3.列表
          • 3.1创建列表
            • 3.1.1集合初始值设定项
            • 3.1.2添加元素
            • 3.1.3插入元素
            • 3.1.4访问元素
            • 3.1.5删除元素
            • 3.1.6搜索
            • 3.1.7排序
            • 3.1.8类型转换
          • 3.2只读集合
        • 4.队列
        • 5.栈
        • 6.链表
        • 7.有序列表
        • 8.字典
          • 8.1键的类型
          • 8.2字典示例
          • 8.3Lookup类
          • 8.4有序字典
        • 9.集
        • 10.可观察的集合
        • 11.位数组
          • 11.1BitArray类
          • 11.2BitVectory32结构
        • 12.不变的集合
        • 13.并发集合
        • 14.性能

    1.概述

    使用数组和Array接口可是存放数据,但数组的大小是固定的,当元素个数是动态时,就应使用集合类。
    List<T>是与数组相当的集合类,其他类型还包括:队列,栈,链表,字典和集。


    2.集合接口和类型

    大多数集合类都包含在System.CollectionSystem.Collection.Generic命名空间内。
    常见非泛型和泛型接口

    非泛型接口泛型接口说明
    ICollection ICollection<T> 定义所有集合的大小(Count),枚举器(foreach)和同步(copyto)方法,继承自IEnumerable
    IList IList<T> 表示可以按照索引单独访问的一组对象(像数组一样)
    IDictionary IDictionary<T> 表示键/值对的集合
    IComparer IComparer<T> 定义类型为比较两个对象而实现的方法
    IEqualityComparer IEqualityComparer<T> 定义方法以支持对象的相等比较
    IEnumerable IEnumerable<T> 公开枚举器,实现了该接口意味着允许foreach语句循环访问集合中的元素
    IEnumerator IEnumerator<T> 支持在泛型集合上进行简单迭代

    2.1集合接口

    泛型集合接口层次结构
    image 
    这些接口提供了一种标准的方式来执行常规任务,包括遍历、索引和计数。

    2.1.1IList<T>与IDictionary<TKey, TValue>


    在选择集合类来解决数据存储或者数据获取问题时,首先考虑的接口是IList<T>和IDictionary<TKey, TValue>。这两个接口决定了集合类型是侧重于通过位置索引来获取值,还是侧重于通过键来获取值。
    实现这两个接口都必须提供索引器,两者的索引器操作数分别为一个整数和一个值关联的键。

    2.1.2ICollection<T>

    IList<T>与IDictionary<TKey, TValue>都实现了ICollection<T>,ICollection<T>由IEnumerable<T>派生而来,包含两个成员:Count和CopyTo()。

    • Count属性返回集合中的元素总数。
    • CopyTo()方法允许将集合转换成数组。

    2.2主要集合类

    后续第4至第9小节分别讲解了列表、队列、栈、链表、有序列表、字典和集。

    2.3索引器

    2.3.1 定义索引器

    索引器的实质是有参属性,以数组的风格实现(重载[]运算符)

    class NameList
    {
        string[] nameArray = new string[size];
        public static int size = 10;
        public NameList()
        {
            for(int i = 0; i< size; i++)
            {
                nameArray[i] = "NA";
            }
        }
        public string this[int index] // 使用this,只允许在对象实例上定义索引器
        {
            get
            {
                string tmp;
                if(index >= 0 && index < size)
                {
                    tmp = nameArray[index];
                }
                else
                {
                    tmp = "";
                }
                return tmp;
            }
            set
            {
                if(index >=0 && index < size)
                {
                    nameArray[index] = value;
                }
            }
        }
        static void Main(string[] args)
        {
            NameList name = new NameList();
            name[0] = "kyle";
            name[1] = "bob";
            name[2] = "lily";
            Console.WriteLine($"{name[0]}, {name[1]}, {name[2]}");
            Console.ReadKey();
        }
    }
    
    2.3.2 索引类型

    C# 不将索引类型限制为整数。 例如,对索引器使用字符串可能有用。 通过搜索集合内的字符串并返回相应的值,可以实现此类索引器。 由于访问器可被重载,字符串和整数版本可以共存。

    class Test
    {
        string[] weekdays = new string[] { "11111", "22222", "33333", "44444", "55555" };
        public int GetDay(string s)
        {
            for (int j = 0; j < 5; j++)
            {
                if (weekdays[j] == s)
                {
                    return j;
                }
            }
            throw new System.ArgumentOutOfRangeException(s, "error");
        }
        public int this[string index]
        {
            get { return GetDay(index); }
        }
    }
    class Program
    {
        static void Main()
        {
            Test t = new Test();
            Console.WriteLine(t["44444"]);
            Console.ReadKey();
        }
    }
    
    2.3.3 接口中的索引

    可以在接口上声明索引器。 接口索引器的访问器与类索引器的访问器有所不同,差异如下:

    • 接口访问器不使用修饰符。
    • 接口访问器没有正文。

    因此,访问器的用途是指示索引器为读写、只读还是只写。

    下面是接口索引器访问器的示例:

    public interface ISomeInterface
    {
        //...
    
        // Indexer declaration:
        string this[int index]
        {
            get;
            set;
        }
    }
    

    索引器的签名必须不同于同一接口中声明的所有其他索引器的签名。

    下面的示例演示如何实现接口索引器。

    // Indexer on an interface:
    public interface ISomeInterface
    {
        // Indexer declaration:
        int this[int index]
        {
            get;
            set;
        }
    }
    
    // Implementing the interface.
    class IndexerClass : ISomeInterface
    {
        private int[] arr = new int[100];
        public int this[int index]   // indexer declaration
        {
            get
            {
                // The arr object will throw IndexOutOfRange exception.
                return arr[index];
            }
            set
            {
                arr[index] = value;
            }
        }
    }
    
    class MainClass
    {
        static void Main()
        {
            IndexerClass test = new IndexerClass();
            System.Random rand = new System.Random();
            // Call the indexer to initialize its elements.
            for (int i = 0; i < 10; i++)
            {
                test[i] = rand.Next();
            }
            for (int i = 0; i < 10; i++)
            {
                System.Console.WriteLine("Element #{0} = {1}", i, test[i]);
            }
    
            // Keep the console window open in debug mode.
            System.Console.WriteLine("Press any key to exit.");
            System.Console.ReadKey();
        }
    }
    /* Sample output:
        Element #0 = 360877544
        Element #1 = 327058047
        Element #2 = 1913480832
        Element #3 = 1519039937
        Element #4 = 601472233
        Element #5 = 323352310
        Element #6 = 1422639981
        Element #7 = 1797892494
        Element #8 = 875761049
        Element #9 = 393083859
     */
    

    在前面的示例中,可通过使用接口成员的完全限定名来使用显示接口成员实现。 例如:

    string ISomeInterface.this[int index]   
    {   
    }
    

    但仅当类采用相同的索引签名实现多个接口时,才需用到完全限定名称以避免歧义。 例如,如果 Employee 类正在实现接口 ICitizen 和接口 IEmployee,而这两个接口具有相同的索引签名,则需要用到显式接口成员实现。

    即是说以下索引器声明:

    string IEmployee.this[int index]   
    {   
    }   
    

    在 IEmployee 接口中实现索引器,而以下声明:

    string ICitizen.this[int index]
    {   
    }   
    

    在 ICitizen 接口中实现索引器。

    2.4迭代器

    迭代器可用于逐步迭代集合,例如列表和数组。

    迭代器方法或 get 访问器可对集合执行自定义迭代。 迭代器方法使用 yield return 语句返回元素,每次返回一个。 到达 yield return 语句时,会记住当前在代码中的位置。 下次调用迭代器函数时,将从该位置重新开始执行。

    通过 foreach 语句或 LINQ 查询从客户端代码中使用迭代器。

    在以下示例中,foreach 循环的首次迭代导致 SomeNumbers 迭代器方法继续执行,直至到达第一个 yield return 语句。 此迭代返回的值为 3,并保留当前在迭代器方法中的位置。

    在循环的下次迭代中,迭代器方法的执行将从其暂停的位置继续,直至到达 yield return 语句后才会停止。 此迭代返回的值为 5,并再次保留当前在迭代器方法中的位置。 到达迭代器方法的结尾时,循环便已完成。

    static void Main()
    {
        foreach (int number in SomeNumbers())
        {
            Console.Write(number.ToString() + " ");
        }
        // Output: 3 5 8
        Console.ReadKey();
    }
    
    public static System.Collections.IEnumerable SomeNumbers()
    {
        yield return 3;
        yield return 5;
        yield return 8;
    }
    

    迭代器方法或 get 访问器的返回类型可以是 IEnumerable、IEnumerable<T>、IEnumerator 或 IEnumerator<T>。

    可以使用 yield break 语句来终止迭代。

    迭代器实际过程:

    int[] myArray = { 1, 32, 43, 343 };
    IEnumerator myie = myArray.GetEnumerator();  //获取需要遍历的枚举数
    myie.Reset();      //重置
    while (myie.MoveNext())  
    {
          int i = (int)myie.Current;         //这边涉及一个装箱的操作
          Console.WriteLine("Value: {0}", i);
    }
    
    2.4.1 简单的迭代器

    下例包含一个位于 for 循环内的 yield return 语句。 在 Main 中,foreach 语句体的每次迭代都会创建一个对迭代器函数的调用,并将继续到下一个 yield return 语句。

    static void Main()
    {
        foreach (int number in EvenSequence(5, 18))
        {
            Console.Write(number.ToString() + " ");
        }
        // Output: 6 8 10 12 14 16 18
        Console.ReadKey();
    }
    
    public static System.Collections.Generic.IEnumerable<int>
        EvenSequence(int firstNumber, int lastNumber)
    {
        // Yield even numbers in the range.
        for (int number = firstNumber; number <= lastNumber; number++)
        {
            if (number % 2 == 0)
            {
                yield return number;
            }
        }
    }
    
    2.4.2 创建集合类

    在以下示例中,DaysOfTheWeek 类实现 IEnumerable 接口,此操作需要 GetEnumerator 方法。 编译器隐式调用 GetEnumerator 方法,此方法返回 IEnumerator。 GetEnumerator 方法通过使用 yield return 语句每次返回 1 个字符串。

    static void Main()
    {
        DaysOfTheWeek days = new DaysOfTheWeek();
    
        foreach (string day in days)
        {
            Console.Write(day + " ");
        }
        // Output: Sun Mon Tue Wed Thu Fri Sat
        Console.ReadKey();
    }
    
    public class DaysOfTheWeek : IEnumerable
    {
        private string[] days = { "Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat" };
    
        public IEnumerator GetEnumerator()
        {
            for (int index = 0; index < days.Length; index++)
            {
                // Yield each day of the week.
                yield return days[index];
            }
        }
    }
    

    下例创建了一个包含动物集合的 Zoo 类。

    引用类实例 (theZoo) 的 foreach 语句隐式调用 GetEnumerator 方法。 引用 Birds 和 Mammals 属性的 foreach 语句使用 AnimalsForType 命名迭代器方法。

    static void Main()
    {
        Zoo theZoo = new Zoo();
    
        theZoo.AddMammal("Whale");
        theZoo.AddMammal("Rhinoceros");
        theZoo.AddBird("Penguin");
        theZoo.AddBird("Warbler");
    
        foreach (string name in theZoo)
        {
            Console.Write(name + " ");
        }
        Console.WriteLine();
        // Output: Whale Rhinoceros Penguin Warbler
    
        foreach (string name in theZoo.Birds)
        {
            Console.Write(name + " ");
        }
        Console.WriteLine();
        // Output: Penguin Warbler
    
        foreach (string name in theZoo.Mammals)
        {
            Console.Write(name + " ");
        }
        Console.WriteLine();
        // Output: Whale Rhinoceros
    
        Console.ReadKey();
    }
    
    public class Zoo : IEnumerable
    {
        // Private members.
        private List<Animal> animals = new List<Animal>();
    
        // Public methods.
        public void AddMammal(string name)
        {
            animals.Add(new Animal { Name = name, Type = Animal.TypeEnum.Mammal });
        }
    
        public void AddBird(string name)
        {
            animals.Add(new Animal { Name = name, Type = Animal.TypeEnum.Bird });
        }
    
        public IEnumerator GetEnumerator()
        {
            foreach (Animal theAnimal in animals)
            {
                yield return theAnimal.Name;
            }
        }
    
        // Public members.
        public IEnumerable Mammals
        {
            get { return AnimalsForType(Animal.TypeEnum.Mammal); }
        }
    
        public IEnumerable Birds
        {
            get { return AnimalsForType(Animal.TypeEnum.Bird); }
        }
    
        // Private methods.
        private IEnumerable AnimalsForType(Animal.TypeEnum type)
        {
            foreach (Animal theAnimal in animals)
            {
                if (theAnimal.Type == type)
                {
                    yield return theAnimal.Name;
                }
            }
        }
    
        // Private class.
        private class Animal
        {
            public enum TypeEnum { Bird, Mammal }
    
            public string Name { get; set; }
            public TypeEnum Type { get; set; }
        }
    }
    
    2.4.3 对泛型列表使用迭代器

    在以下示例中,Stack<T> 泛型类实现 IEnumerable<T> 泛型接口。 Push 方法将值分配给类型为 T 的数组。 GetEnumerator 方法通过使用 yield return 语句返回数组值。

    除了泛型 GetEnumerator 方法,还必须实现非泛型 GetEnumerator 方法。 这是因为从 IEnumerable 继承了 IEnumerable<T>。 非泛型实现遵从泛型实现的规则。

    本示例使用命名迭代器来支持通过各种方法循环访问同一数据集合。 这些命名迭代器为 TopToBottom 和 BottomToTop 属性,以及 TopN 方法。

    BottomToTop 属性在 get 访问器中使用迭代器。

    static void Main()
    {
        Stack<int> theStack = new Stack<int>();
    
        //  Add items to the stack.
        for (int number = 0; number <= 9; number++)
        {
            theStack.Push(number);
        }
    
        // Retrieve items from the stack.
        // foreach is allowed because theStack implements IEnumerable<int>.
        foreach (int number in theStack)
        {
            Console.Write("{0} ", number);
        }
        Console.WriteLine();
        // Output: 9 8 7 6 5 4 3 2 1 0
    
        // foreach is allowed, because theStack.TopToBottom returns IEnumerable(Of Integer).
        foreach (int number in theStack.TopToBottom)
        {
            Console.Write("{0} ", number);
        }
        Console.WriteLine();
        // Output: 9 8 7 6 5 4 3 2 1 0
    
        foreach (int number in theStack.BottomToTop)
        {
            Console.Write("{0} ", number);
        }
        Console.WriteLine();
        // Output: 0 1 2 3 4 5 6 7 8 9
    
        foreach (int number in theStack.TopN(7))
        {
            Console.Write("{0} ", number);
        }
        Console.WriteLine();
        // Output: 9 8 7 6 5 4 3
    
        Console.ReadKey();
    }
    
    public class Stack<T> : IEnumerable<T>
    {
        private T[] values = new T[100];
        private int top = 0;
    
        public void Push(T t)
        {
            values[top] = t;
            top++;
        }
        public T Pop()
        {
            top--;
            return values[top];
        }
    
        // This method implements the GetEnumerator method. It allows
        // an instance of the class to be used in a foreach statement.
        public IEnumerator<T> GetEnumerator()
        {
            for (int index = top - 1; index >= 0; index--)
            {
                yield return values[index];
            }
        }
    
        IEnumerator IEnumerable.GetEnumerator()
        {
            return GetEnumerator();
        }
    
        public IEnumerable<T> TopToBottom
        {
            get { return this; }
        }
    
        public IEnumerable<T> BottomToTop
        {
            get
            {
                for (int index = 0; index <= top - 1; index++)
                {
                    yield return values[index];
                }
            }
        }
    
        public IEnumerable<T> TopN(int itemsFromTop)
        {
            // Return less than itemsFromTop if necessary.
            int startIndex = itemsFromTop >= top ? 0 : top - itemsFromTop;
    
            for (int index = top - 1; index >= startIndex; index--)
            {
                yield return values[index];
            }
        }
    
    }
    
    2.4.4 语法信息

    迭代器可用作一种方法,或一个 get 访问器。

    不能在事件、实例构造函数、静态构造函数或静态终结器中使用迭代器。

    必须存在从 yield return 语句中的表达式类型到迭代器返回的 IEnumerable 类型参数的隐式转换。

    在 C# 中,迭代器方法不能有任何 in、ref 或 out 参数。

    在 C# 中,“yield”不是保留字,只有在 return 或 break 关键字之前使用时才有特殊含义。

    2.5返回null或空集合

    假定现在有一个IEnumerable<T>集合,应该使用Enumerable.Empty<T>()方法来表示空集合。


    3.列表

    .NET Framwork为动态列表提供了List<T>,这个类实现了IList,ICollection,IEnumerable,IList<T>,ICollection<T>,IEnumerable<T>接口。

    3.1创建列表

    在泛型List<T>中,必须为声明为列表的值指定类型。(Array是一个非泛型列表,它可以将任意Object类型作为元素)
    使用默认构造函数创建一个空列表,添加第一个元素后,列表容量扩大为可接纳4个元素,如果添加了第五个元素,容量扩大到8个元素,如果8个元素还不够,则扩大至16个元素,即列表每设置容量都为原来的2倍。

    var intList = new List<int>();
    var classList = new List<ClassName>();
    

    若创建一个容量为10的集合,若容量不足以容纳所有元素,则扩大至20,40,此时会对集合的内存进行重新分配。

    List<int> intList = new List<int>(10);
    

    使用Capacity属性可以获取或设置集合的容量。

    intList.Capacity = 20;
    Console.WriteLine(intList.Capacity);
    

    使用Count属性查看元素个数。

    Console.WriteLine(intList.Count);
    

    如果无需再添加新的元素,可调用TrimExcess()方法去除不需要的容量,若元素占比达到90%,则该方法不做任何操作。

    intList.TrimExcess();
    
    3.1.1集合初始值设定项

    可以使用集合初始值设定项给集合赋值。

    var intList = new List<int>() { 1, 2 };
    var stringList = new List<string>() { "one", "two" };
    
    3.1.2添加元素

    使用Add()方法可以给列表y添加元素。

    var intList = new List<int>();
    intList.Add(1);
    var stringList = new List<string>() { "one", "two" };
    stringList.Add("three");
    

    同时,可以初始化一个类类型的List<T>

    var a = new ClassName(param1, param2, param3);
    var b = new ClassName(param1, param2, param3);
    var c = new ClassName(param1, param2, param3);
    var classList = new List<ClassName>() { a, b, c };
    classList.Add(new ClassName(param1, param2, param3));
    

    使用List<T>的AddRange()方法,可以一次给集合添加多个元素。因为AddRange()方法的参数是IEnumerable<T>类型的对象,所以也可以传递一个数组。

    classList.AddRange(new ClassList[] {
        new ClassName(param1, param2, param3),
        new ClassName(param1, param2, param3)
    });
    

    可使用实现IEnumerable<T>类型的任意对象传递给构造函数进行实例化

    var classList = new List<ClassName>{
        new ClassName[] {
            new ClassName(param1, param2, param3),
            new ClassName(param1, param2, param3)
        }
    };
    
    3.1.3插入元素

    使用Insert()方法在指定位置插入元素。

    intList.Insert(3, 10); // 在索引为3处插入元素10
    

    使用InsertRange()方法插入大量元素,类似于AddRange()方法。

    3.1.4访问元素

    实现了IList和IList<T>接口的所有类都提供了一个索引器,通过索引进行访问。

    int i = intList[0];
    

    使用for语句遍历集合中所有元素。

    for (int i = 0; i < intList.Count; i++)
        Console.WriteLine(intList[i]);
    

    因为IList<T>集合实现了IEnumerable接口,所以可以使用foreach语句遍历所有元素。

    foreach (int i in intList)
        Console.WriteLine(i);
    
    3.1.5删除元素

    利用索引删除元素。

    intList.RemoveAt(3);
    

    也可以使用Remove()方法删除某元素,但由于要搜索并获取d删除元素的索引,故效率较低。
    RemoveRange()方法从集合中删除多个元素。

    intList.RemoveRange(index, count);
    

    使用RemoveAll()方法删除指定特性所有元素,使用Clear()方法删除所有元素。

    3.1.6搜索

    获得要查找的元素的索引或元素本身,有以下方法:Contains()、IndexOf()、 LastIndexOF()、FindIndex()、FindLastIndex()、Find()、 FindLast()、BinarySearch()。其中BinarySearch()采用二分搜索算法,前提是元素已排好序。
    使用FindAll()方法查找多个数据项。

    List<int> results = list.FindAll(Even); // 委托实例Even()
    

    如果只是检查元素是否存在List<T>提供了Exists()方法。

    3.1.7排序

    List<T>类可以使用Sort()对元素排序。

    public void List<T>.Sort();
    public void List<T>.Sort(Comparison<T>);
    public void List<T>.Sort(IComparer<T>);
    public void List<T>.Sort(Int32, Int32, IComparer<T>);
    

    可以使用Reverse()方法,反转集合。

    3.1.8类型转换

    使用List<T>类的ConvertAll<TOutput>()方法,可以把所有类型的集合转换成另一种类型。

    List<NewClass> newClass = 
        ClassName.ConvertAll<NewClass>(
            r => new NewClass(r.Param1, r.Param2)); // 将ClassName转为NewClass,并用Param1,Param2初始化
    

    3.2只读集合

    List<T>集合的AsReadOnly()方法返回ReadOnlyCollection<T>类型的对象,其实现的接口与List<T>集合相同,但所有修改集合的方法和属性都抛出NotSupportedException异常。


    4.队列

    队列是其元素以先进先出(FIFO)的方式来处理的集合。
    队列使用System.Collection.Generic名称空间中的泛型Queue<T>实现,在内部,Queue<T>类使用T类型的数组,,它实现了ICollection和IEnumerable<T>接口,但没有实现ICollection<T>接口。 因为Queue<T>未实现IList<T>接口,所以无法使用索引器访问队列。队列只允许向队列尾部添加元素(Enqueue()方法),从队列的头部获取元素(Dequeue()方法)。
    image 
    Queue<T>类的方法如下表所示:

    Queue<T>类的成员说明
    Count Count属性返回队列中的元素个数
    Enqueue Enqueue()方法在队列尾部添加一个元素
    Dequeue Dequeue()方法在队列的头部读取和删除一个元素,若队列中无元素则抛出异常
    Peek Peek()方法从队列头部读取一个元素,d但不删除它
    TrimExcess TrimExcess()方法去除空位,重新设置队列的容量
    Contains 确定某个元素是否在队列中,如果是,就返回true
    Clear 移除队列中所有对象

    5.栈

    栈是一个后进先出(LIFO)的容器,最后添加到栈中的元素会最先读取。
    使用Push()方法在栈中添加元素,用Pop()方法获取最近添加的元素。
    与Queue<T>类相同,Stack<T>类实现了IEnumerable<T>和ICollection接口。
    image 
    Stack<T>类的成员如下表所示

    Stack<T>类的成员说明
    Count 返回栈中的元素个数
    Push 在栈顶添加一个元素
    Pop 从栈顶删除一个元素m,f并返回该元素,若栈为空则抛出异常
    Peek 返回栈顶的元素,但不删除它
    Contains 确定某个元素是否在栈中,如果是,就返回true
    Clear 移除栈中所有对象

    6.链表

    LinkedList<T>是一个双向链表,其元素指向它前面和后面的元素,,这样,通过移动到下一个元素可以正向遍历整个链表,通过移动到前一个元素可以反向遍历整个链表。
    在存储元素时,链表还必须存储每个元素的下一个元素和上一个元素。
    LinkedList<T>类定义的成员可以访问链表的第一个和最后一个元素(First和Last),在指定位置插入元素(AddAfter(),AddBefore(),AddFirst()和AddLast()方法),删除指定位置的元素(Remove(),RemoveFirst()和RemoveLast()方法),从链表的开头(Find()方法)或结尾(FindLast()方法)开始搜索元素。
    image


    7.有序列表

    如果需要基于键对所需集合排序,就可以使用SortedList<TKey, TValue>类。

    var books = new SortedList<string, string>();
    books.Add("C#", "123");
    books["Python"] = "234";
    books["Java"] = "345";
    foreach(KeyValuePair<string, string> book in books)
        Console.WriteLine($"{book.Key}, {book.Value}");
    foreach (string isbn in books.Values)
        Console.WriteLine(isbn);
    foreach (string title in books.Keys)
        Console.WriteLine(title);
    

    8.字典

    字典允许按照某个键来访问元素,字典也称为映射或散列表。字典的主要特性是能根据键快速查找值,也可以自由添加和删除元素,这点与List<T>类相似,但没有在内存中移动后续元素的性能开销。
    .NET Framwork使用的最主要的字典类是Dictionary<TKey, TValue>。
    image

    8.1键的类型

    用作字典中键的类型必须重写Object类的GetHashCode()方法,只要字典类需要确定元素的位置,就要调用GetHashCode()方法,用于返回对应索引的int。

    8.2字典示例

    在检索时,使用Dictionary<TKey, TValue>类的TryGetValue()方法可以避免使用索引器时找不到指定键的异常

    8.3Lookup类

    Lookup<TKey, TElement>类似于Dictionary<TKey, TValue>,但把键映射到一个值集上。
    Lookup<TKey, TElement>类的创建必须调用ToLookup()方法,该方法返回一个Lookup<TKey, TElement>对象。

    var students = new List<Student>();
    students.Add(new Student(param1, param2));
    students.Add(new Student(param1, param2));
    students.Add(new Student(param1, param2));
    var lookupStudent = student.ToLookup(r => r.Grade);
    foreach (Student s in lookupStudent["one"])
        Console.WriteLine(s);
    

    8.4有序字典

    SortedDictionary<TKey, TValue>类是一个二叉搜索树,其中的元素根据键来排序。该键类型必须实现接口IComparable<TKey>接口。


    9.集

    包含不重复元素的集合称为“集(set)”。.NET Framework包含两个集,他们都实现了ISet<T>接口。HashSet<T>集包含不重复元素的无序列表,SortedSet<T>集包含不重复元素的有序列表。
    ISet<T>接口提供的方法可以创建合集、交集,或者给出一个集是另一个集的超集或子集的信息。 使用Add()方法返回一个bool值,说明是否添加了元素,若该元素已在集中,加不再添加,并返回false

    var teamA = new HashSet<string>() { "a", "b" };
    var teamB = new HashSet<string>() { "a", "b", "c" };
    var teamC = new HashSet<string>() { "a", "c", "d" };
    if (teamA.Add("c"))
        Console.WriteLine("c");
    if (!teamA.Add("c"))
        Console.WriteLine("c was already in this set"); 
    

    使用IsSubsetOf()和IsSupersetOf()方法返回bool值,已验证子集和超集。

    if (teamA.IsSubsetOf(teamB))
    if (teamA.IsSupersetOf(teamB))
    

    使用UnionWith()方法填充合集。

    var allTeam = new SortedSet<string>(teamA);
    allTeam.UnionWith(teamB);
    allTeam.UnionWith(teamC);
    

    使用ExceptWith()方法删除某个集中的元素。

    allTeam.ExceptWith(teamA);
    

    10.可观察的集合

    如果需要集合中元素何时删除或添加的信息,就可以使用ObservableCollection<T>类。


    11.位数组

    如果需要处理的数字有许多位,就可以使用BitArray类和BitVector32结构。

    11.1BitArray类

    BitArray类是一个引用类型,它包含一个int数组,其中每32位使用一个新整数。
    BitArray类的成员如下表所示:

    BitArray类的成员说明
    Count Length Count和Length属性的get访问器返回数组中的位数。使用Length属性还可以定义新的数组大小,重新设置集合的大小
    Item Get Set 使用索引器读写数组中的位,索引器为bool类型,使用Get()和Set()方法访问数组中的位
    SetAll 根据传送给该方法的参数,SetAll()方法设置所有位的值
    Not Not()方法对数组中所有位的值取反
    And Or Xor And()、Or()、Xor()方法可以合并两个BitArray对象(与、或、异或)

    辅助方法DisplayBits()遍历BitArray:

    static void DisplayBits(BitArray bits)
    {
        foreach(bool bit in bits)
            Console.Write(bit ? 1: 0);
    }
    
    var bits1 = new BitArray(8);
    bits1.SetAll(true);
    bits1.Set(1, false);
    bits1[4] = false;
    

    11.2BitVectory32结构

    如果事先知道需要的位数,就可以使用BitVectory32结构代替BitArray类,BitVectory32结构效率较高,因为它是一个值类型,在整数栈上存储位。
    下表列出了BitVectory32结构与BitArray类中完全不同的成员

    BitVectory32结构的成员说明
    Data Data属性把BitVectory32结构中的数据返回为整数
    Item BitVectory32的值可以使用索引器设置
    CreateMask 这是一个静态方法,用于访问BitVectory32结构中的特定位创建掩码
    CreateSection 这是一个静态方法,用于创建32位中的几个片段

    12.不变的集合

    // 创建一个空数组
    ImmutableArray<string> a1 = ImmutableArray.Create<string>();
    // Add方法返回新的不变集合赋给a2,此a1仍是空集合
    ImmutableArray<string> a2 = a1.Add("a");
    ImmutableArray<string> a3 = a2.Add("b").Add("c");
    

    13.并发集合


    14.性能

  • 相关阅读:
    自己实现的一个简单的C# IOC 容器
    C# 内存缓存工具类 MemoryCacheUtil
    使用触发器和C#程序实现数据同步
    Maven 命令安装指定 jar 包到本地仓库
    C# RSA 非对称加密
    JS可选链操作符?.和双问号??
    Learn D3 入门文档: Introduction
    Lerna 基本概念
    图片 src 为二进制的处理
    ASCII 和 Base64
  • 原文地址:https://www.cnblogs.com/jizhiqiliao/p/10648966.html
Copyright © 2011-2022 走看看