C#学习总结之集合

时间:2023-03-09 19:03:08
C#学习总结之集合

一、集合接口和类型

命名空间:

 集合类型  命名空间
 一般集合 System.Collections 
 泛型集合 System.Collections.Generic 
 特定类型集合 System.Collections.Specialized 
 线程安全集合 System.Collections.Concurrent 
 不可变集合 System.Collections.Immutable 

接口说明:

接口 说明
IEnumerable<T> 如果将foreach语句用于集合,就需要IEnumerable接口。此接口定义了GetEnumerator()方法,返回一个实现了IEnumerator接口的枚举。
ICollection<T> ICollection<T>接口由泛型集合类实现。使用这个接口可以获得集合中的元素个数(Count属性),把集合复制到数组中(CopyTo()方法),从集合添加和删除元素(Add(),Remove(),Clear())。
IList<T>

IList<T>接口用于可通过位置访问其中的元素列表,此接口定义了一个索引器,可以在集合的指定位置插入或删除某些项(Insert()和RemoveAt()方法)。IList<T>接口派生自ICollection<T>接口。

ISet<T>

ISet<T>接口由集实现。集允许合并不同的集,获得两个集的交集,检查两个集是否重叠。ISet<T>接口派生自ICollection<T>接口。

IDictionary<TKey,TValue> IDictionary<TKey,TValue>接口由包含键和值的泛型集合类实现。使用此接口可以访问所有的键和值,使用键类型的索引器可以访问某些项,还可以添加或删除某些项。
ILookup<TKey,TValue> 类似于IDictionary<TKey,TValue>接口,可以通过一个键包含多个值。 
IComparer<T> 由比较器实现,通过Compare()方法给集合中的元素排序。
IEqualityComparer<T> 由比较器实现,用于字典中的键。使用此接口可对对象进行相等性比较。
IProductConsumerCollection<T> .NET4.0后添加,支持新的线程安全的集合类。 

IReadOnlyCollectiton<T>

IReadOnlyList<T>

IReadOnlyDictionary<TKey,TValue>

用于初始化后不能修改的集合,这些接口的成员只允许检索对象,不能添加或修改它们。 

IImmutableArray<T>

IImmutableList<T>

IImmutableQueue<T>

IImmutableSet<T>

IImmutableDictionary<TKey,TValue>

不可变接口定义了用于不可变集合的方法和属性,这些集合在初始化后不能修改。

二、列表

  调用默认构造函数创建列表对象:

var intList = new List<int>();

*使用默认构造函数创建空列表,元素添加到列表中后,列表容量会首先扩大到可容纳4个元素,每次超过容量后会重新设置为原来的2倍。列表容量改变,就要重新分配到一个新的内存块中,创建一个新的数组,使用Array.Copy()方法将旧的数组中的元素复制到新的数组中。

获取和设置集合的容量:

intList.Capacity = ;

获取集合元素的数量(Count属性):

Console.WriteLine(intList.Count);

释放不需要的容量:

intList.TrimExcess();
  • 集合初始化设定项
,};

*集合初始值设定项没有反映在已编译的程序集的IL代码中,编译器会把集合初始值设定项变成对初始值设定项列表中的每一项调用Add()方法。

  • 添加元素
intList.Add();
intList.AddRange(new int[]{1,2,3});
  • 插入元素
intList.Insert(,);
intList.InsertRange(,,,,});
  • 访问元素

使用for循环遍历集合中的元素,通过索引器访问元素:

; i < intList.Count; i++)
{
      Console.WriteLine(intList[i]);
}

使用foreach语句遍历集合中的元素:

 foreach (int i in intList)
 {
      Console.WriteLine(i);
 }

使用ForEach()方法:

 intList.ForEach(Console.WriteLine);
  • 删除元素

使用RemoveAt()方法删除元素:

intList.RemoveAt();

使用Remove()方法删除元素:

];
intList.Remove(i);

*按索引删除比较快,因为必须在集合中搜索要删除的元素。Remove()方法先检查元素类型是否实现了IEquatable<T>接口。如果是,就调用这个接口的Equals()方法,确定集合中的元素是否存在等于传递给Equals()方法的元素。如果没有实现这个接口,就使用Object类的Equals()方法比较这些元素。Object类中的Equals()方法的默认实现代码对值类型进行按位比较,对引用类型只比较其引用。

使用RemoveRange()方法删除元素(索引加位数):

intList.RemoveRange(,);

使用RemoveAll()方法删除指定特性的所有元素:

 == );

使用Clear()方法删除集合中的所有元素:

intList.Clear();
  • 搜索

使用Exists()方法检查元素是否存在:

intList.Exists(it => it == );

使用IndexOf()方法返回指定元素在集合中的索引,如果没有找到,则返回-1:

Console.WriteLine(intList.IndexOf());

使用FindIndex()、FindLastIndex()方法返回指定元素在集合中的索引:

);
int lastIndex = intList.FindLastIndex(r => r == 6);

使用Find()方法返回集合中指定的元素:

);

使用FindAll()方法获取与Predicate<T>类型匹配的所有项:

List<);
foreach (int i in newIntList)
{
       Console.WriteLine(i);
}
  • 排序

使用Sort()方法排序:

intList.Sort();
  • 类型转换

使用ConvertAll<TOutput>()方法把集合转换为另一种类型:

List<string> stringList = intList.ConvertAll<string>(r => r.ToString() + "(I'm string)");

使用AsReadOnly()方法返回只读集合:

ReadOnlyCollection<int> readOnlyCollection = intList.AsReadOnly();

三、队列

  队列是其元素以先进先出的原则来处理的集合,队列使用System.Collections.Generic命名空间下的泛型类Queue<T>实现。它实现了ICollection和IEnumberable<T>接口,但没有实现ICollection<T>接口,因此不能使用Add()和Remove()方法,且没有实现IList<T>接口,所以不能使用索引器访问队列。

Queue<T>类的方法说明

Queue<T>类的成员 说明
Count Count属性返回队列中的元素个数
Enqueue Enqueue()方法在队列一端添加一个元素
Dequeue Dequeue()方法在队列的头部读取和删除一个元素。如果调用时队列中没有元素,则抛出一个InvalidOperationException类型的异常
Peek Peek()方法从队列的头部读取一个元素,但不删除它
TrimExcess TrimExcess()方法重新设置队列的容量。Dequeue()方法从队列中删除元素,但它不会重新设置队列的容量。要从队列的头部去除空元素,应使用TrimExcess()方法
 public class Document
 {
        public string Title { get; private set; }
        public string Content { get; private set; }

public Document(string title, string content) { this.Title = title; this.Content = content; } } public class DocumentManager { private readonly Queue<Document> documentQueue = new Queue<Document>(); public void AddDocument(Document doc) { lock (this) { documentQueue.Enqueue(doc); } } public Document GetDocument() { Document doc = null; lock (this) { doc = documentQueue.Dequeue(); } return doc; } public bool IsDocumentAvailable { get { ; } } }

四、栈

  栈是一个后进先出的容器,使用Push()方法在栈中添加元素,用Pop()方法获取最近添加的元素。

Stack<T>类的方法说明

Stack<T>类的成员 说明
Count 返回栈中的元素个数
Push 在栈顶添加一个元素
Pop 从栈顶删除一个元素,并返回该元素,如果栈是空的,就抛出InvalidOperationException异常
Peek 返回栈顶的元素,但不会删除它
Contains 确定某个元素是否在栈中,如果是,就返回true
 var alphabet = new Stack<char>();
 alphabet.Push('A');
 alphabet.Push('B');
 alphabet.Push('C');

 foreach (char item in alphabet)
 {
        Console.WriteLine(item);
 }
 Console.ReadLine();
 //返回内容为C、B、A

五、链表

  LinedList<T>是一个双向链表,其元素指向它前面和后面的元素。

链表的优点:在插入一个元素时,只需要修改上一个元素的Next引用和下一个元素的Previous引用即可。

链表的缺点:链表的元素只能一个接一个地访问,这需要较长的时间来查找位于链表中间或尾部的元素。

LinkedListNode<T>类型属性说明

属性 说明
List 返回与节点相关的LinkedList<T>对象
Next 用于遍历列表,返回当前节点之后的节点
Previous 用于遍历列表,返回当前节点之前的节点
Value 返回与节点相关的元素

六、有序列表

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

 var books = new SortedList<string, string>();
            books.Add("Professional WPF Programming", "978-0-470-04180-2");
            books.Add("Professional ASP.NET MVC 3", "978-1-1180-7658-3");
            books["Beginning Visual C# 2010"] = "978-0-470-50226-6";
            books["Professional C# 4 and .NET 4"] = "978-0-470-50225-9";

            foreach (var book in books)
            {
                Console.WriteLine("{0} , {1}", book.Key, book.Value);
            }
            Console.ReadLine();

*如果使用索引器访问一个元素,若所传键值不存在,就会抛出一个KeyNotFoundException类型的异常。为了避免异常发生,可以使用ContainsKey()方法判断列表中是否有指定键值。也可以调用TryGetValue()方法尝试获得指定键的值。

string isbn;
string title = "Professional C# 7.0";
if (!books.TryGetValue(title,out isbn))
{
       Console.WriteLine("{0} not found", title);
}

七、字典

  字典是一种可以按照某个键来访问元素的集合,用作字典中键的类型必须重写object类的GetHashCode()方法。字典的容量是一个素数,如果指定的容量不是素数的值,Dictionary<TKey,TValue>类会使用传递给构造函数的整数后面紧接着的一个素数来指定容量。

  • Dictionary<TKey,TValue>类

使用Add()方法添加元素:

var myDictionary = new Dictionary<string, string>();
myDictionary.Add(", "Jack");

使用索引器添加元素:

myDictionary["] = "Mike";

使用TryGetValue()方法检查键值是否存在,若存在则返回true,否则返回false:

string person;
myDictionary.TryGetValue(", out person);
  • Lookup<TKey,TValue>类:

类似Dictionary<TKey,Tvalue>类,但把键映射在一个值集上。Lookup<TKey,Tvalue>类不能像一般字典那样创建,必须调用ToLookup()方法。

class Program
    {
        static void Main(string[] args)
        {
               var racers = new List<Racer>();
               racers.Add(,));
               racers.Add(,));
               racers.Add(,));
               racers.Add(,));
               racers.Add(,));

               var lookupRacers = racers.ToLookup(r => r.Country);
               foreach (Racer r in lookupRacers["Australia"])
              {
                       Console.WriteLine(r);
              }
               Console.ReadLine();
        }

    }
public class Racer
{
        public int Id { get; private set; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Country { get; set; }
        public int Wins { get; set; }

        public Racer(int id, string firstName, string lastName, string country, int wins)
        {
            this.Id = id;
            this.FirstName = firstName;
            this.LastName = lastName;
            this.Country = country;
            this.Wins = wins;
        }

        )
        {

        }

        public override string ToString()
        {
            return String.Format("{0} {1}", FirstName, LastName);
        }
}
  • SortedDictionary<TKey,TValue>类

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

八、集

  包含不重复元素的集合称为"集(set)",.NET Framework包含两个集(HashSet<T>和SortedSet<T>)。HashSet<T>集包含不重复元素的无序列表,SortedSet<T>集包含不重复元素的有序列表。

方法 说明
Add 返回一个布尔值,说明是否添加了元素
IsSubSetOf 是否为子集
IsSupersetOf 是否为超集
Overlaps 确定是否当前的 Hashset对象和指定的集合共享通用元素
UnionWith 并集
ExceptWith 删除指定集合中的元素
 var companyTeams = new HashSet<string>() { "Ferrari", "McLaren", "Mercedes" };
            var traditionalTeams = new HashSet<string>() { "Ferrari", "McLaren" };
            var privateTeams = new HashSet<string>() { "Red Bull", "Toro Rosso", "Force India", "Sauber" };

            if (privateTeams.Add("Williams"))
            {
                Console.WriteLine("Williams Added");
            }
            if (!companyTeams.Add("McLaren"))
            {
                Console.WriteLine("McLaren was already in this set");
            }

            if (traditionalTeams.IsSubsetOf(companyTeams))
            {
                Console.WriteLine("TraditionalTeams is subset of companyTeams");
            }
            if (companyTeams.IsSupersetOf(traditionalTeams))
            {
                Console.WriteLine("CompanyTeams is a superset of traditionalTeams");
            }
            traditionalTeams.Add("Williams");
            if (privateTeams.Overlaps(traditionalTeams))
            {
                Console.WriteLine("At least one team is the same with the traditional and private teams");
            }
            var allTeams = new SortedSet<string>(companyTeams);
            allTeams.UnionWith(privateTeams);
            allTeams.UnionWith(traditionalTeams);
            Console.WriteLine();
            Console.WriteLine("All teams");
            foreach (var team in allTeams)
            {
                Console.WriteLine(team);
            }
            allTeams.ExceptWith(privateTeams);
            Console.WriteLine();
            Console.WriteLine("no private team left");
            foreach (var team in allTeams)
            {
                Console.WriteLine(team);
            }
            Console.ReadLine();

九、可观察的集合

  如果需要集合中的元素何时删除或添加的信息,就可以使用ObservableCollection<T>类,这个类的命名空间是System.Collections.ObjectModel。用户可以使用INotifyCollectionChanged接口注册CollectionChanged事件。

NotifyCollectionChangedEventArgs属性说明

属性 说明
Action 是否添加或删除一项的信息
OldItems 列出删除的项
NewItems 列出新增的项
class Program
{
     static void Main(string[] args)
        {
            var data = new ObservableCollection<string>();
            data.CollectionChanged += Data_CollectionChanged;
            data.Add("One");
            data.Add("Two");
            data.Insert(, "Three");
            data.Remove("One");
            Console.ReadLine();
        }
      static void Data_CollectionChanged(object sender, NotifyCollectionChangedEventArgs e)
        {
            Console.WriteLine("action: {0}", e.Action.ToString());
            if (e.OldItems != null)
            {
                Console.WriteLine("starting index for old item(s): {0}", e.OldStartingIndex);
                Console.WriteLine("old item(s) :");
                foreach (var item in e.OldItems)
                {
                    Console.WriteLine(item);
                }
            }
            if (e.NewItems != null)
            {
                Console.WriteLine("starting index for new item(s): {0}", e.NewStartingIndex);
                Console.WriteLine("new item(s): ");
                foreach (var item in e.NewItems)
                {
                    Console.WriteLine(item);
                }
            }
            Console.WriteLine();
        }
}

十、位数组

  如果需要处理的数字有许多位,可以使用BitArray类和BitVector32结构。BitArray类位于命名空间System.Collectioins中,BitVector32结构位于命名空间System.Collections.Specialized中。区别:BitArray可以重新设置大小,BitVector32结构仅包含32位。

BitArray类:

BitArray类的成员 说明
Count Length Count和Length属性的get访问器返回数组中的位数。使用Length属性还可以定义新的数组大小,重新设置集合的大小。

Item

Get Set

可以使用索引器读写数组中的位。索引器是布尔类型,除了使用索引器之外,还可以使用Get()和Set()方法访问数组中的位。
SetAll

根据传送给该方法的参数,SetAll()方法设置所有位的值。

Not Not()方法对数组中所有位的值取反

And

Or

Xor

使用And()、Or()、Xor()方法,可以合并两个BitArray对象。And()方法执行二元AND,Or()方法执行二元OR,Xor()方法是异或操作。

BitVector32结构:

BitVector32结构的成员 说明
Data Data属性把BitVector32结构中的数据返回给整数
Item BitVector32的值可以使用索引器设置。索引器是重载的——可以使用掩码或BitVector32.Section类型的片段来获取和设置值。
CreateMask 这是一个静态方法,用于为访问BitVector32结构中的特定位创建掩码。
CreateSection 这是一个静态方法,用于创建32位中的几个片段。