C#集合类(数据结构)

一、选择数据结构

1)线性容器
List<T>数组/Stack/Dequeue按需求模型选择即可,LinkedList<T>是双向链表增删修改快.
需要有序数组SortList<T>线性排序容器都可以;如果既需要查找快又需要频繁修改那么可以用List<T>记录索引,用LinkedList<T>存储。

2)二叉树类型容器
SortedDictionary<TKey,TValue>可以提供二叉树类型插入删除查找都比较折中的键值对容器。
SortedSet<T>一个集合值类型的容器,比SortedDictionary<TKey,TValue>需要更少的空间。

3)哈希表类型的容器
Dictionary<TKey,TValue>类似于C++/java中的HashMap实现,需要一个哈希函数和一个相等判断函数解决冲突,能够有很高的插入和查找效率。
HashSet<T>适合单个元素的集合操作类型。
ILookup<TKey,TValue>可以获得一个键对应多个值的存储类型,很有用的方面是从指定集合中筛选某种类型的数据集。

4)其它支持容器的接口类,委托,拓展方法和为了观察,封装位操作,封装多线程操作的衍生类型容器
其它功能类型的接口及其委托,拓展方法:
ICollection<T>、IEnumerable<T>、IEquatable<T>、IComparer<T>、IComparable<T>、IEqualityComparer<T>、IFormattable接口
为了观察,封装位操作,封装多线程操作的衍生类型容器:
ObservableCollection<T>,BitArray/BitVector32、IProducerConsumerCollection<T>接口

大多数集合类都在System.Collections.Generic命名空间中,非泛型的System.Collections中已经很少用了。
特定集合类位于System.Collections.Specialized中,线程安全的集合类在System.Collections.Concurrent中。

集合类主要有:

二.ICollection<T>、IEnumerable<T>、IEquatable<T>、IComparer<T>、IComparable<T>、IEqualityComparer<T>、IFormattable接口。


三.Array、List<T>、队列、栈、SortedList<TKey, TValue>、LinkedList<T>双向链表

1.List<T>

List<T>在C#中实现也是数组,动态数组长度不够会加倍。
不确定的数组需要可变数组用List<T>, 确定长度和数量多用Array, 不推荐用ArrayList因为添加的是object类型要装箱和拆箱性能慢。
1)初始化:
初始化时候可以直接赋值,或者指定Count和Capacity来初始化。
对List<T>填充完数据以后可以用TrimExcess()方法去除不需要的容量,只有空白容量超过10%才会去除成功。
List<T>可以用AddRange添加多个元素。
2)访问:
可以通过索引器访问的集合类有:Array,List<T>,ArrayList, StringCollection, List<T>。
List<T>实现了IEnumerable<T>接口,所以也可以用foreach来访问。

List<T>提供了ForEach方法,该方法用Action<T>作为参数。
public void ForEach(Action<T> action);
// 需要当前类中定义该委托的实例赋值给Action委托对象,也可以用Lambda表达式声明该实例。
public delegate void Action<T> (T obj);

3)删除
用RemoveAt效率较快,如果用Remove回先查找值然后删除回查找引用,如果有重写IEquatable<T>或者Object.Equals就会用这些方法判断,否则
是用默认的引用比较,如果是相同的引用地址那么就可以删除成功。
删除还可以用RemoveRange(index, count)来进行删除。
如果要删除指定特性的元素就可以用RemoveAll()方法,指定特性在Predicate<T>参数中指定。
要直接删除所有的元素用ICollection<T>接口中定义的Clear()方法。

4)搜索
IndexOf(),LastIndexOf(), FindIndex(), FindLastIndex(), Find(),FindLast().
判断存在用Exists();
FindIndex()方法需要一个Predicate类型的参数。
public int FindIndex(Predicate<T> match);
需要给委托对象传递一个声明的委托实例,例如:
public bool FindCountryPredicate(Racer racer)
{
   if( racer == null) return false;
   return racer.Country  == country;
}
将FindCountryPredicate传入函数参数,即可,RemoveAll()中也需要传递入该委托实例。
Find(), FindIndex(), FindAll()都需要这样的比较委托实例,委托实例的广泛使用,如果只写一次可以用Lambd表达式来写,多次将其封装为函数。

5)排序
排序Sort方法也需要传递比较大小的委托实例。
有大概三种比较大小的委托函数:
默认的是IComparable<T>一个other参数的比较委托。
IComparer<T>两个参数的比较委托。
重载Sort方法,该方法需要一个Comparison<T>的委托实例。Comparison<T>的委托定义是public delegate int Comparison<T>(T x, T y);

可以调用Reverse()方法逆转整个集合的排序。

6)类型转换
使用List<T>类的ConvertAll<TOutput>()方法,可以把所有的集合类型转换为另一种类型。
该TOutput委托的定义如下:
public sealed delegate TOutput Convert<TInput, TOutput>(TInput from);
需要定义一个委托实例,传入该函数参数即可。

7)只读集合
一般集合都是要支持读写的,但是有些比较特殊的应用需要给客户提供一个只读集合,那么可以使用List<T>集合的AsReadOnly()方法就可以返回

一个ReadOnlyCollection<T>类型的对象。ReadOnlyCollection<T>和List<T>的差别只是不能写排序删除等,其它实现都一样。

List简单例子:

 static void Main()
        {
            var graham = new Racer(7, "Graham", "Hill", "UK", 14);
            var emerson = new Racer(13, "Emerson", "Fittipaldi", "Brazil", 14);
            var mario = new Racer(16, "Mario", "Andretti", "USA", 12);

            var racers = new List<Racer>(20) { graham, emerson, mario };

            racers.Add(new Racer(24, "Michael", "Schumacher", "Germany", 91));
            racers.Add(new Racer(27, "Mika", "Hakkinen", "Finland", 20));

            racers.AddRange(new Racer[] {
               new Racer(14, "Niki", "Lauda", "Austria", 25),
               new Racer(21, "Alain", "Prost", "France", 51)});

            var racers2 = new List<Racer>(new Racer[] {
               new Racer(12, "Jochen", "Rindt", "Austria", 6),
               new Racer(22, "Ayrton", "Senna", "Brazil", 41) });

            Console.WriteLine("-------racers------------");
            for( int i = 0; i < racers.Count; i++ )
            {
                Console.WriteLine(racers[i].ToString());
            }
            Console.WriteLine("-------racers2------------");
            for (int i = 0; i < racers2.Count; i++)
            {
                Console.WriteLine(racers2[i].ToString());
            }
        }

2.Queue<T>

先进先出,实现了ICollection和IEnumerable<T>接口,但没有实现ICollection<T>接口,因此这个接口定义的Add()和Remove()方法不能用于队列。
没有实现List<T>接口,所以也不支持索引器访问。

队列中的常用方法,Count返回个数,Dequeue()进队列,Enqueue出队列并删除队列头元素,Peek从队列头部读取队列但不删除元素。
TrimExcess()可以清除Capacity中的大于10%时候的元素。

队列Queue<T>的构造默认会4,8,16,32递增的增加容量,.net 1.0版本的Queue却是一开始就给了个32项的空数组。
队列例子:

public class DocumentManager
    {
        // readonly只是说这个队列对象不写(比如另一个对象拷贝给它),但是内部的元素是可以写的
        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
            {
                return documentQueue.Count > 0;
            }
        }
    }


using System;
using System.Threading;
namespace Wrox.ProCSharp.Collections
{
    public class ProcessDocuments
    {
        private DocumentManager documentManager;
        protected ProcessDocuments(DocumentManager dm)
        {
            documentManager = dm;
        }

        public static void Start(DocumentManager dm)
        {
            // ParameterizedThreadStart;
            // public delegate void ParameterizedThreadStart(object obj);
            // 直接这样启动一个线程了。
            new Thread( new ProcessDocuments(dm).Run ).Start();
       
  • 6
    点赞
  • 31
    收藏
    觉得还不错? 一键收藏
  • 3
    评论
评论 3
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值