c#之字典学习笔记

来源:互联网 发布:台州房销售数据下载 编辑:程序博客网 时间:2024/05/21 15:51

字典表示一种非常复杂的数据结构,这种数据结构允许按照某个键来访问元素。字典也称为映射或散列表。字典的主要特性是能根据键快速查找值。也可以自由添加和删除元素,这有点像List类,但没有在内存中移动元素的性能开销。字典的性能取决于GetHashCode()方法的实现代码。
(1).键值
用作字典中键的类型必须重写Object类的GetHashCode()方法。只要字典类需要确定元素的位置,它就要调用GetHashCode()方法。GetHashCode()方法返回的int由字典用于计算在对应位置放置元素的索引。
GetHashCode()方法的实现代码必须满足以下要求:
①相同的对象应总是返回相同的值
②不同的对象可以返回相同的值。
③它应执行得比较快,计算的开销不大
④它不能抛出异常
⑤它应至少使用一个实例字段
⑥散列代码值应平均分布在int可以存储的整个数字范围上
⑦散列代码最好在对象的生存期中不发生变化。
其中要使散列代码值平均分布在整数的取值范围内,因为如果两个键返回的散列代码值会得到相同的索引,字典类就必须寻找最近的可用空闲位置来存储第二个数据项,这需要进行一定的搜索。这会降低性能,如果在排序的时候许多键都有相同的索引,这类冲突就更可能出现。根据Microsoft的部分算法的工作方式,当计算出来的散列代码值评价分布在int.MinValue和int.Max.Value之间时,这种风险会降低到最小。
除了实现GetHashCode()方法之外,键类型还必须实现IEquatable.Equals()方法,或重写Object类的Equals()方法。因为不同的键对象可能返回相同的散列代码,所以字典使用Equals()方法来比较键。字典检查两个键A和B是否相等,并调用A.Equals(B)方法。这表示必须确保下述条件总是成立:如果A.Equals(B)方法返回true,则A.GetHashCode()和B.GetHashCode()方法必须总是返回相同的散列代码。
当为Equals()方法提供了重写版本,但没有提供GetHashCode()方法的重写版本,C#编译器会显示一个编译警告。
如果需要使用的键类型没有实现IEquatable接口,并根据存储在字典中的键值重载GetHashCode()方法,就可以创建一个实现IEqualityComparer接口的比较器。IEqualityComparer接口定义了GetHashCode()和Equals()方法,并将传递的对象作为参数,因此可以提供与对象类型不同的实现方式。Dictionary

using System;using System.Collections.Generic;[Serializable]public class EmployeeIdException : Exception{    public EmployeeIdException(string message) : base(message) { }}[Serializable]public struct EmployeeId : IEquatable<EmployeeId>{    private readonly char prefix;    private readonly int number;    public EmployeeId(string id)    {        if (id == null) throw new ArgumentNullException("id");        prefix = (id.ToUpper())[0];        int numLength = id.Length-1;        try        {            number = int.Parse(id.Substring(1, numLength > 6 ? 6 : numLength));        }        catch (FormatException)        {            throw new EmployeeIdException("Invalid EmployeeId format");        }    }    public override string ToString()    {        return prefix.ToString() + string.Format("{0:000000}", number);    }    public override int GetHashCode()    {        //由于数字是可变的,因此员工可以取1~190000的一个值。这并没有填满整数取值范围。        //可以将数字向左移动16位,再与原来的数字进行异或操作,最后将结果乘以十六进制数15051505        //以上处理可以使散列代码在整数取值区域上的分布相当均匀。        //不理解16跟0x15051505怎么得到的。        return (number ^ number << 16) * 0x15051505;    }    public bool Equals(EmployeeId other)    {        if (other == null) return false;        return (prefix == other.prefix && number == other.number);    }    public override bool Equals(object obj)    {        return Equals((EmployeeId)obj);    }    public static bool operator ==(EmployeeId left, EmployeeId right)    {        return left.Equals(right);    }    public static bool operator !=(EmployeeId left, EmployeeId right)    {        return !(left == right);    }}[Serializable]public class Employee{    private string name;    private decimal salary;    private readonly EmployeeId id;    public Employee(EmployeeId id, string name, decimal salary)    {        this.id = id;        this.name = name;        this.salary = salary;    }    public override string ToString()    {        return String.Format("{0}:{1,-20} {2:c}", id.ToString(), name, salary);    }    static void Main()    {        //构造函数指定了31个元素的容量。注意容量一般是素数。如果指定了一个不是素数的值。        //Dictionary<TKey,TValue>类会使用传递给构造函数的整数后面紧接着的一个素数,来指定容量。        var employees = new Dictionary<EmployeeId, Employee>(31);        var idKyle = new EmployeeId("T3755");        var kyle = new Employee(idKyle, "Kyle Bush", 5443890.00m);        employees.Add(idKyle, kyle);        Console.WriteLine(kyle.ToString());        var idCarl = new EmployeeId("F3547");        var carl = new Employee(idCarl, "Carl Edwards", 5597120.00m);        employees.Add(idCarl, carl);        Console.WriteLine(carl.ToString());        var idJimmie = new EmployeeId("C3386");        var jimmie = new Employee(idJimmie, "Jimmie Johnson", 5024710.00m);        employees.Add(idJimmie, jimmie);        Console.WriteLine(jimmie.ToString());        var idDale = new EmployeeId("C3323");        var dale = new Employee(idDale, "Dale Earnhardt Jr.", 3522740.00m);        employees[idDale] = dale;        Console.WriteLine(dale.ToString());        var idJeff = new EmployeeId("C3235");        var jeff = new Employee(idJeff, "Jeff Burton", 3879540.00m);        employees[idJeff] = jeff;        Console.WriteLine(jeff.ToString());        while (true)        {            Console.Write("Enter employee id (X to exit) > ");            var userInput = Console.ReadLine();            userInput = userInput.ToUpper();            if (userInput == "X") break;            EmployeeId id;            try            {                id = new EmployeeId(userInput);                Employee employee;                if (!employees.TryGetValue(id, out employee))                {                    Console.WriteLine("Employee with id {0} does not exist", id);                }                else                {                    Console.WriteLine(employee);                }            }            catch (EmployeeIdException ex)            {                Console.WriteLine(ex.Message);            }        }    }}
using System;using System.Collections.Generic;namespace csharpTest{    class Program    {        public static int GetNumber(int number)        {            return (number ^ number << 16) * 0x15051505;        }        static void Main(string[] args)        {            List<int> data = new List<int>() { 0, int.MinValue, 0, int.MaxValue };            for (int i = 1; i <= 190000;++i)            {                int num = GetNumber(i);                if(data[1] < num)                {                    data[0] = i;                    data[1] = num;                }                if (data[3] > num)                {                    data[2] = i;                    data[3] = num;                }            }            Console.WriteLine("{0} {1} {2} {3}",data[0],data[1],data[2],data[3]);            Console.WriteLine("{0} {1}", int.MinValue, int.MaxValue);        }    }}

(2) Lookup
Dictionary < TKey,TValue > 类支持每个键关联一个值。Lookup < TKey,TElement > 非常类似于Dictionary < TKey,TValue > 类,但它把键映射到一个值集上。这个类在程序集System.Core中实现,用System.Linq名称空间定义。Lookup < TKey,TElement > 类不能像一般的字典那样创建,而必须调用ToLookup()方法,该方法返回一个Lookup < TKey,TElement > 对象。ToLookup()方法是一个扩展方法,它可以用于实现了IEnumerable接口的所有类。

using System;using System.Linq;using System.Collections.Generic;[Serializable]public class Racer{    public string FirstName{get;set;}    public string LastName{get;set;}    public string Country{get;set;}    public int Wins{get;set;}    public Racer(string firstName,string lastName,string country = null,int wins = 0)    {        this.FirstName = firstName;        this.LastName = lastName;        this.Country = country;        this.Wins = wins;    }    public override string ToString()    {        return String.Format("{0} {1}", FirstName, LastName);    }}public class Test{    static void Main()    {        var racers = new List<Racer>();        racers.Add(new Racer("Jacques", "Villeneuve", "Canada", 11));        racers.Add(new Racer("Alan", "Jones", "Australia", 12));        racers.Add(new Racer("Jackie", "Stewart", "United Kingdom", 10));        racers.Add(new Racer("James", "Hunt", "United Kingdom", 10));        racers.Add(new Racer("Jack", "Brabham", "Australia", 14));        var lookupRacers = racers.ToLookup(r => r.Country);        foreach(Racer r in lookupRacers["Australia"])        {            Console.WriteLine(r);        }    }}

(3)有序字典
SortedDictionary < TKey,TValue > 类是一个二叉搜索树,其中的元素根据键来排序。该键类型必须实现IComparable接口。如果键的类型不能排序,可以创建一个实现了IComparer接口的比较器,将比较器用作有序字典的构造函数的一个参数。
SortedDictionary < TKey,TValue > 类和SortedList < TKey,TValue > 类的功能类似。但因为SortedList < TKey,TValue > 实现为一个基于数组的列表,而SortedDictionary < TKey,TValue > 类实现为一个字典,所以它们有不同的特征。
SortedList < TKey,TValue > 类使用的内存比SortedDictionary < TKey,TValue > 类少
SortedDictionary < TKey,TValue > 类的元素插入和删除速度比较快。
在用已排好序的数据填充集合时,若不需要修改容量,SortedList < TKey,TValue > 类比较快。

0 0
原创粉丝点击