ITPub博客

首页 > IT职业 > IT图书 > C#基础提升系列——C#集合

C#基础提升系列——C#集合

IT图书 作者:lslzf 时间:2021-09-09 09:30:25 0 删除 编辑
System.Array :用于数组,提供创建,操作,搜索和排序数组的方法,是所有数组的基类。
  • System.Collections :是ArrayList、Queue、Hashtable等基类,包含定义对象的各种集合。
  • System.Collections.Concurrent :提供了线程安全的集合类。
  • System.Collections.Generic :包含接口和定义泛型集合,它允许用户创建强类型集合能提供比非泛型强类型集合更好的类型安全和性能等级。
  • System.Collections.Specialized:包含强类型集合。专用于特定类型的集合类。
  • System.Collections.Immutable :包含了定义不可变的集合接口和类 。
  • 官方文档。

    排序

    List类可以使用Sort()方法对元素进行排序。Sort()方法有如下几个重载方法:

    public void Sort(int index, int count, IComparer comparer);
    public void Sort(Comparison comparison);
    public void Sort();
    public void Sort(IComparer comparer);

    只有集合中的元素实现了IComparable接口,才能使用不带参数的Sort()方法。

    如果需要按照元素类型 不 默认支持的方式排序,就需要使用其他重载方法,比如传递一个实现了IComparer接口的对象。

    下面将用一个具体的示例进行说明:

    public class Racer : IComparable
    {
        public int Id { get; }
        public string FirstName { get; set; }
        public string LastName { get; set; }
        public string Country { get; set; }
        public int Wins { get; set; }
        //实现接口中的方法
        public int CompareTo(Racer other)
        {
            int compare = LastName?.CompareTo(other?.LastName) ?? -1;
            if (compare == 0)
            {
                return FirstName?.CompareTo(other?.FirstName) ?? -1;
            }
            return compare;
        }
        //定义构造函数
        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 Racer(int id, string firstName, string lastName, string country)
            : this(id, firstName, lastName, country, wins: 0) { }
        //重写object的Tostring()方法
        public override string ToString()
        {
            return $"{FirstName} {LastName}";
        }
    }

    上述的类直接实现了IComparable泛型接口,所以可以直接使用不带参数的Sort()方法进行排序,排序的依据基于重写的CompareTo()方法。

    var racers = new List {
        new Racer(1,"zhang","bsan","中国"),
        new Racer(3,"li","asi","中国"),
        new Racer(2,"wang","dwu","中国")
    };
    racers.Sort();

    执行上述语句,将会按照LastName进行排序后输出,依次为li asi、zhang bsan、wang dwu。

    对上述示例进行扩展,使用传递一个实现了IComparer接口的对象进行排序。如下:

    public class RacerComparer : IComparer
    {
        //定义一个枚举,可以直接通过类名.枚举名进行访问
        public enum CompareType
        {
            FirstName,
            LastName,
            Country,
            Wins
        }
        //定义枚举变量
        private CompareType _compareType;
        //定义构造函数,通过外部指定枚举类型
        public RacerComparer(CompareType compareType)
        {
            _compareType = compareType;
        }
        //重写接口方法
        public int Compare(Racer x, Racer y)
        {
            if (x == null && y == null) return 0;
            if (x == null) return -1;
            if (y == null) return 1;
            int result;
            switch (_compareType)
            {
                case CompareType.FirstName:
                    return string.Compare(x.FirstName, y.FirstName);
                case CompareType.LastName:
                    return string.Compare(x.LastName, y.LastName);
                case CompareType.Country:
                    result = string.Compare(x.Country, y.Country);
                    if (result == 0)
                        return string.Compare(x.LastName, y.LastName);
                    else return result;
                case CompareType.Wins:
                    return x.Wins.CompareTo(y.Wins);
                default:
                    throw new ArgumentException("Invalid Compare Type");
            }
        }
    }

    上述类RacerComparer 实现了泛型接口IComparer,其中泛型类型为Racer(实现IComparer接口中的泛型类型T应该是将要进行排序的元素的类型),并重写了Compare()方法,因此可以调用Srot(IComparer>)方法进行排序。

    var racers = new List {
        new Racer(1,"zhang","bsan","中国"),
        new Racer(3,"li","asi","中国"),
        new Racer(2,"wang","dwu","中国")
    };
    racers.Sort(new RacerComparer(RacerComparer.CompareType.FirstName));
    //将会按照FirstName进行排序

    还可以调用Sort(Comparison comparison)进行排序,Comparison是一个泛型委托,它的定义如下:

    public delegate int Comparison(T x, T y);

    它需要传入两个T类型的参数,返回类型为int。如果参数值相等,该方法返回0;如果第一个参数比第二个小,返回一个小于0的值;否则,返回一个大于0的值。

    比如示例中如果按照Id进行排序,可以使用如下方法调用:

    //由于是委托,此处可以使用lambda表达式
    racers.Sort((r1, r2) => r1.Id.CompareTo(r2.Id));

    上述将会按照Id升序排序。

    如果使用了Sort()进行排序后,可以调用Reverse()方法,逆转整个集合的排序。

    只读集合

    可以调用List集合的AsReadOnly()方法返回ReadOnlyCollection类型的对象。ReadOnlyCollection类实现的接口与List集合相同,除此之外还实现了IReadOnlyCollection和IReadOnlyList接口。因为这些接口的成员,集合不能修改。所有修改集合的方法和属性都抛出NotSupportedException异常。

    public class ReadOnlyCollection : IList, ICollection, IEnumerable, 
    IEnumerable, IList, ICollection, IReadOnlyList, IReadOnlyCollection
    Dequeue() :删除并返回队列开头的元素。如果队列中没有元素,在调用该方法时,将会抛出一个InvalidOperationException类型的异常。

    Enqueue(T) :将元素添加到队列的末尾。

    Peek() :返回但不删除队列开头的元素。

    TrimExcess() :如果该数量小于当前容量的90%,则将容量设置为队列的实际元素数。

    Count :获取队列中的元素的个数

    可以使用默认的构造函数创建一个空队列,也可以使用构造函数指定容量。在把元素添加到队列中时,如果没有指定容量,将会类似于List类,队列的容量也总是根据需要成倍增加,从而包含4、8、16和32个元素等。

    下面将使用一个复杂的示例说明队列是如何使用的。首先定义一个简单的类:

    public class Document
    {
        public string Title { get; }
        public string Content { get; }
    
        public Document(string title, string content)
        {
            this.Title = title;
            this.Content = content;
        }
    }

    接着为这个类进行队列写入和读取操作:

    public class DocumentManager
    {
        private readonly Queue _documentQueue = new Queue();
        //向队列中添加元素
        public void AddDocument(Document doc)
        {
            //因为后面将要使用多线程,为了避免死锁,进行lock语句限制
            lock (this)
            {
                _documentQueue.Enqueue(doc);
            }
        }
        //获取队列中的元素
        public Document GetDocument()
        {
            Document doc = null;
            lock (this)
            {
                doc = _documentQueue.Dequeue();
            }
            return doc;
        }
        //队列中是否还有元素未读出
        public bool IsDocumentAvailable => _documentQueue.Count > 0;
    }

    然后定义一个操作此类的对外开放的类:

    public class ProcessDocuments
    {
        private DocumentManager _documentManager;
    
        protected ProcessDocuments(DocumentManager dm)
        {
            _documentManager = dm ?? throw new ArgumentNullException(nameof(dm));
        }
    
        protected async Task Run()
        {
            while (true)
            {
                if (_documentManager.IsDocumentAvailable)
                {
                    Document doc = _documentManager.GetDocument();
                    Console.WriteLine(doc.Title + ":" + doc.Content);
                }
                //显式的指定间隔时间,将会在此等待,从而执行该方法之外的代码部分
                await Task.Delay(new Random().Next(1000));
            }
        }
        //对外开放的调用方法
        public static void Start(DocumentManager dm)
        {
            //启动一个新的任务
            Task.Run(new ProcessDocuments(dm).Run);
        }
    }

    调用代码如下:

    public static void Run()
    {
        var dm = new DocumentManager();
        ProcessDocuments.Start(dm);
    
        for (int i = 0; i 
    Pop() :删除并返回栈的顶部的一元素。如果栈没有元素,调用该方法将会抛出InvalidOperationException异常。

    Push(T) :在栈的顶部插入一个元素。

    Peek() :返回但不删除栈的顶部的一个元素。

    Contains(T) :判断一个元素是否在栈中。

    Count :返回栈中元素的个数。

    下面使用一个简单的示例来说明栈的相关操作:

    var mystacks = new Stack();
    mystacks.Push(1);
    mystacks.Push(2);
    mystacks.Push(3);
    foreach(var num in mystacks)
    {
        Console.Write(num+"t"); //将会输出:3   2   1
    }
    Console.WriteLine();
    while (mystacks.Count > 0)
    {
        Console.Write(mystacks.Pop() + "t");
    }
    C#集合和数据结构

    本文后续会随着知识的积累不断补充和更新,内容如有错误,欢迎指正。

    最后一次更新时间 :2018-07-10


    来自 “ ITPUB博客 ” ,链接:https://blog.itpub.net/1070/viewspace-2804010/ ,如需转载,请注明出处,否则将追究法律责任。

    请登录后发表评论 登录
    全部评论

    注册时间:2019-04-15

    • 博文量
      102
    • 访问量
      875