《C#本质论》读书笔记(16)构建自定义集合

16.1 更多集合接口

集合类(这里指IEnumerable层次结构)实现的接口层次结构



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

字典类一般只按照键进行索引,而不按位置索引。
列表“键”总是一个整数,“键集”总是从0开始的非负整数的一个连续集合。
解决数据存储或数据获取问题时,考虑  IList<T>(侧重位置索引获取值)与 IDictionary<TKey,TValue>(侧重通过键来获取值)。

16.1.2 ICompatable<T>

ICompatable<T>是实现排序的关键。
例如, List<T>.Sort() ,你需要采取某种方式比较对象,一个办法就是使用 ICompatable<T> 接口。该接口有一个 CompareTo() 方法,返回一个整数,指出传递元素是大于、小于还是等于当前元素,所以,键的数据类型必须实现 ICompatable<T> 


高级主题:用IComparer<T>排序

      为了实现自定义排序,另一个方法是向排序方法传递实现了 IComparer<T> 的元素。集合中的元素通常不支持这个接口。
    IComparable<T>  IComparer<T> 的区别很细微,但重要。
IComparable<T> 说:“我知道如何将自己和我的类型的另一个实例进行比较
IComparer<T> 说:“我知道如何比较给定类型的两个实例

  1. public static void Main()    

  2. {    

  3.     Contact aaa = new Contact() { LastName = "bbb", FirstName = "ddd" };    

  4.     Contact bbb = new Contact() { LastName = "aaa", FirstName = "ccc" };    

  5.     

  6.     //Console.WriteLine(new NameComparison().Compare(aaa, bbb));    

  7.     

  8.     List<Contact> contactlist = new List<Contact>();    

  9.     contactlist.Add(aaa);    

  10.     contactlist.Add(bbb);    

  11.     

  12.     foreach (var contact in contactlist)    

  13.     {    

  14.         Console.WriteLine(contact.LastName + " ");    

  15.     }    

  16.     //排序    

  17.     contactlist.Sort(new NameComparison());    

  18.     

  19.     foreach (var contact in contactlist)    

  20.     {    

  21.         Console.WriteLine(contact.LastName + " ");    

  22.     }    

  23.     

  24.     Console.Read();    

  25.     

  26. }    

  27.     

  28. class Contact    

  29. {    

  30.     public string FirstName { getset; }    

  31.     public string LastName { getset; }    

  32. }    

  33.     

  34.     

  35. class NameComparison : IComparer<Contact>    

  36. {    

  37.     public int Compare(Contact x, Contact y)    

  38.     {    

  39.         int result;    

  40.     

  41.         if (Contact.ReferenceEquals(x, y))    

  42.         {    

  43.             result = 0;    

  44.         }    

  45.         else    

  46.         {    

  47.             if (x == null)    

  48.             {    

  49.                 result = 1;    

  50.             }    

  51.             else if (y == null)    

  52.             {    

  53.                 result = -1;    

  54.             }    

  55.             else    

  56.             {    

  57.                 result = StringCompare(x.LastName, y.LastName);    

  58.                 if (result == 0)    

  59.                 {    

  60.                     result =    

  61.                         StringCompare(x.FirstName, y.FirstName);    

  62.                 }    

  63.             }    

  64.         }    

  65.         return result;    

  66.     }    

  67.     

  68.     private static int StringCompare(string x, string y)    

  69.     {    

  70.         int result;    

  71.         if (x == null)    

  72.         {    

  73.             if (y == null)    

  74.             {    

  75.                 result = 0;    

  76.             }    

  77.             else    

  78.             {    

  79.                 result = 1;    

  80.             }    

  81.         }    

  82.         else    

  83.         {    

  84.             result = x.CompareTo(y);    

  85.         }    

  86.         return result;    

  87.     }    

  88. }    


16.2 主要集合类

共有5类关键的集合类,所有泛型类都位于 System.Collections.Generic命名空间。

16.2.1 列表集合:List<T>

List<T>具有与数组相似的属性。关键在于会自动的扩展,也可以显示调用 TrimToSize() 或 Capacity 来缩小。

  1. List<string> list = new List<string>();  

  2.      

  3. // Lists automatically expand as elements  

  4. // are added.  

  5. list.Add("Sneezy");  

  6. list.Add("Happy");  

  7. list.Add("Dopey");  

  8. list.Add("Doc");  

  9. list.Add("Sleepy");  

  10. list.Add("Bashful");  

  11. list.Add("Grumpy");  

  12.      

  13. list.Sort();  

  14.      

  15. Console.WriteLine(  

  16.     "In alphabetical order {0} is the "  

  17.     + "first dwarf while {1} is the last.",  

  18.     list[0], list[6]);  

  19.      

  20. list.Remove("Grumpy");  


16.2.3 搜索List<T>

要在 List<T> 查找特定的元素,可以使用 Contains()  IndexOf()  LastIndexOf()  BinarySerch() 方法。
   BinarySerch() 采用的是快得多的二分搜索算法,但要求元素已经排序好。一个有用的功能是假如元素没找到,会返回一个负整数。该值的按位取反(~)结果是”大于被查找元素的下一个元素“的索引,如果没有更大的值,则是元素的总数。这样就可以在特定位置方便插入新值。
  1. List<string> list = new List<string>();  

  2. int search;  

  3.   

  4. list.Add("public");  

  5. list.Add("protected");           

  6. list.Add("private");  

  7.   

  8.   

  9. list.Sort();  

  10.   

  11. search = list.BinarySearch("protected internal");  

  12. if (search < 0)  

  13. {  

  14.     list.Insert(~search, "protected internal");  

  15. }  

  16.   

  17. foreach (string accessModifier in list)  

  18. {  

  19.     Console.WriteLine(accessModifier);  

  20. }  


高级主题:使用 FindAll() 查找多个数据项

   FindAll() 获取 Predicate<T> 类型的一个参数,它是对称为“委托”的一个方法的引用
  1. public static void Main()  

  2. {  

  3.     List<int> list = new List<int>();  

  4.     list.Add(1);  

  5.     list.Add(2);  

  6.     list.Add(3);  

  7.     list.Add(2);  

  8.     list.Add(4);  

  9.   

  10.     List<int> results = list.FindAll(Even);  

  11.   

  12.     foreach (int number in results)  

  13.     {  

  14.         Console.WriteLine(number);  

  15.     }  

  16.     //2,2,4  

  17.     Console.Read();  

  18.   

  19. }  

  20.   

  21. public static bool Even(int value)  

  22. {  

  23.     return (value % 2) == 0;  

  24. }  


传递一个委托实例 Even() 。若整数实参值是偶数,就返回 true 。

16.2.4 字典集合:Dictonary<TKey,TValue>

和列表集合不同,字典类存储是“名称/值”对。
 插入元素,一个选择是使用 Add() 方法。


  1. Dictionary<Guid, string> dictionary =  

  2. new Dictionary<Guid, string>();  

  3. Guid key = Guid.NewGuid();  

  4.   

  5. dictionary.Add(key, "object");  


还有个选择是索引操作符

  1. Dictionary<Guid, string> dictionary =  

  2.    new Dictionary<Guid, string>();  

  3. Guid key = Guid.NewGuid();  

  4.   

  5. dictionary[key] = "object";  

  6. dictionary[key] = "byte";  


由于键和值都要添加到字典中,所以用于枚举字典中的元素的 foreach 循环的循环变量必须是 KeyValuePair<TKey,TValue> 

  1. Dictionary<stringstring> dictionary = new  

  2.    Dictionary<stringstring>();  

  3.   

  4. int index = 0;  

  5.   

  6. dictionary.Add(index++.ToString(), "object");  

  7. dictionary.Add(index++.ToString(), "byte");  

  8. dictionary.Add(index++.ToString(), "uint");  

  9. dictionary.Add(index++.ToString(), "ulong");  

  10. dictionary.Add(index++.ToString(), "float");  

  11. dictionary.Add(index++.ToString(), "char");  

  12. dictionary.Add(index++.ToString(), "bool");  

  13. dictionary.Add(index++.ToString(), "ushort");  

  14. dictionary.Add(index++.ToString(), "decimal");  

  15. dictionary.Add(index++.ToString(), "int");  

  16. dictionary.Add(index++.ToString(), "sbyte");  

  17. dictionary.Add(index++.ToString(), "short");  

  18. dictionary.Add(index++.ToString(), "long");  

  19. dictionary.Add(index++.ToString(), "void");  

  20. dictionary.Add(index++.ToString(), "double");  

  21. dictionary.Add(index++.ToString(), "string");  

  22.   

  23. Console.WriteLine("Key  Value    Hashcode");  

  24. Console.WriteLine("---  -------  --------");  

  25. foreach (KeyValuePair<stringstring> i in dictionary)  

  26. {  

  27.     Console.WriteLine("{0,-5}{1,-9}{2}",  

  28.         i.Key, i.Value, i.Key.GetHashCode());  

  29. }  

 如果只处理字典中的键或值,那么可以用 Keys  Values 属性。这些属性返回的数据类型是 ICollection<T> 。他们返回的是对原始字典集合中的数据的引用,而不是返回的副本。


16.2.5 已排序集合:SortedDictionary<TKey,TValue>和SortedList<T>

已排序集合类的元素是已经排好序的。具体说对于 SortedDictionary<TKey,TValye> 元素是按照键排序的;对于 SortedList<T> ,元素是按照值排序的(还有一个非泛型的  SortedList 实现)。

  1. SortedDictionary<stringstring> sortedDictionary =  

  2.    new SortedDictionary<stringstring>();  

  3.   

  4. int index = 0;  

  5.   

  6. sortedDictionary.Add(index++.ToString(), "object");  

  7. sortedDictionary.Add(index++.ToString(), "byte");  

  8. sortedDictionary.Add(index++.ToString(), "uint");  

  9. sortedDictionary.Add(index++.ToString(), "ulong");  

  10. sortedDictionary.Add(index++.ToString(), "float");  

  11. sortedDictionary.Add(index++.ToString(), "char");  

  12. sortedDictionary.Add(index++.ToString(), "bool");  

  13. sortedDictionary.Add(index++.ToString(), "ushort");  

  14. sortedDictionary.Add(index++.ToString(), "decimal");  

  15. sortedDictionary.Add(index++.ToString(), "int");  

  16. sortedDictionary.Add(index++.ToString(), "sbyte");  

  17. sortedDictionary.Add(index++.ToString(), "short");  

  18. sortedDictionary.Add(index++.ToString(), "long");  

  19. sortedDictionary.Add(index++.ToString(), "void");  

  20. sortedDictionary.Add(index++.ToString(), "double");  

  21. sortedDictionary.Add(index++.ToString(), "string");  

  22.   

  23. Console.WriteLine("Key  Value    Hashcode");  

  24. Console.WriteLine("---  -------  ----------");  

  25. foreach (  

  26.     KeyValuePair<stringstring> i in sortedDictionary)  

  27. {  

  28.     Console.WriteLine("{0,-5}{1,-9}{2}",  

  29.         i.Key, i.Value, i.Key.GetHashCode());  

  30. }  


 键中元素采用的是字幕顺序,而不是数值顺序,这是由于键是字符串,而不是整数。
在一个已排序的字典集合中插入或删除元素时,由于要保持集合中的元素顺序,所以相对前面的 Dictionary<TKey,TValue> 执行事件要稍微长一些。它内部使用两个数组,一个用于键的检索,一个勇于索引的检索。
对于 System.Collections.Sorted 排序列表,索引操作是通过 GetByIndex()  SetByIndex() 方法来支持的。
对于 System.Collections.Generic.SortedList<TKey,TValue> Keys  Values 属性分别返回 IList<TKey>  IList<TValue> 实例。这些方法使得已排序列表具有字典行为,也有列表类型的集合的行为。

16.2.6 栈集合:Stack<T>

栈集合被设计为:后进先出
两个方法:
Push():插入元素。
Pop():按照与添加时相反的顺序获取并删除元素。
 为了不修改栈的前提下访问栈中的元素,使用 Peek()  Contains() 方法。
 Peek() :返回 Pop() 将获取的下一个元素。
 Contains() :勇于判断一个元素是否存在于栈的某个地方。

16.2.7队列集合:Queue<T>

队列集合类和栈集合类基本相同,遵循先入先出模式
 Enqueue() 入队
 Dequeue() :出队。
队列集合根据需要自动增大。但缩小时不一定回收之前使用的存储空间,因为这会使插入新元素的动作变得很昂贵。如果确定队列长时间大小不变,可以使用 TrimToSize() 方法提醒队列集合你希望回收存储空间。

16.2.8 链表:LinkedList<T>

 链表允许正向和反向遍历。(没有对应的非泛型类型)

16.4 返回null或者空集合

返回数组或集合时,必须允许返回 null ,或者返回不包含任何数据的集合实例。
通常,返回没有数据的集合实例是更好的选择,可避免遍历集合数据前检查 null 值。
但是这个准则也有例外,比如 null 被有意的用来表示有别于“零个项目”的情况。
例如,网站用户名集合可能会是 null ,以此来表示出于某种原因未获得最新集合。

16.5 迭代器

本节讨论利用迭代器为自定义集合实现自己的 IEnumerator<T>  IEnumerable<T> 和对应的非泛型接口。
   加入某个类希望支持 foreach 进行迭代,就必须实现枚举器( enumerator )模式。

16.5.1 迭代器定义

迭代器是实现类的方法的一个途径,是更加复杂的枚举数模式的语法简化形式。由于在生成的CIL代码中仍然采用枚举数模式,所以并不会带来真正的运行时性能优势。不过,使用迭代器而不是手动实现枚举数模式,能显著提高程序员的编程效率。

16.5.2 迭代器语法

迭代器提供了迭代器接口(也就是 IEnumerator<T>  IEnumerable<T> 的组合)的一个快捷实现。
创建一个 GetEnumerator() 方法,表示声明了一个迭代器。接着要添加对迭代器接口的支持
  1. public IEnumerator<T> GetEnumerator()  

  2. {  

  3.         //...  

  4.         return new List<T>.Enumerator();//This will be implimented in 16.16  

  5. }  


16.1.3 从迭代器生成值

迭代器类似于函数,但它不返(renturn)值,而是生成(yield)一系列值。
未完待续。。。









原文地址:https://www.cnblogs.com/tangge/p/6088257.html