根据键对字典进行排序


73

我有一个像C#的字典

Dictionary<Person, int>

我想那种字典到位相对于键(在Person类的字段)。我该怎么做?互联网上所有可用的帮助都是列表的帮助,没有字典到位排序的特定示例。任何帮助将不胜感激!


我不确定我是否理解您的问题,因为字典没有以任何顺序枚举?您可以遍历键或值,可以轻松地对其进行排序...
Rowland Shaw 2010年

这可能不是您真正想要的。数组可以就地排序,因为对数组进行排序的结果是一个数组。但是字典不是数组,因此不能用对其条目进行排序的结果来代替。对条目进行排序,然后将结果保存在数组或列表中。接受的答案可能不是您想要的...从SortedList或SortedDictionary输入和访问条目比从Dictionary慢得多。
吉姆·巴尔特

Answers:


147

您无法排序Dictionary<TKey, TValue>-本质上是无序的。(或者,检索条目的顺序是特定于实现的。您不应该依赖于版本之间以相同的方式工作,因为顺序并不是其设计功能的一部分。)

可以使用SortedList<TKey, TValue>SortedDictionary<TKey, TValue>,这两种方式均按键排序(如果将传递IEqualityComparer<T>给构造函数,则以可配置的方式)-可能对您有用吗?

很少注意名称中的“列表”一词 SortedList-它仍然是字典,因为它将键映射到值。它的实现使用列表内,有效地-因此,而不是通过哈希码往上看,它确实二进制搜索。SortedDictionary类似地基于二进制搜索,但是通过树而不是列表。


2
不过,请务必谨慎使用SortedList<K,V>:如果您建立了一个很大的列表(假设这些项目未预先排序),它将非常慢。通常,您应该改SortedDictionary<K,V>而使用,或者使用第三方BDictionary<K,V>来获得类似的性能,SortedDictionary而又不会失去通过索引或“查找最近的键”来访问项目的能力。
Qwertie

二进制搜索而不是在已排序键旁边维护哈希的优点是什么?如果我几乎不仅仅执行有序的枚举,为什么还要关心元素是有序的?如果我更关心单元素查找,但是第二次要基于键顺序进行枚举,我是否会按照史蒂夫的答案来管理单独的键列表?如果这是我的希望的用途,SortedListSortedDictionary实际上都是错误的工具,对不对?也就是说,如果我真的只想对字典排序,该怎么办?
鲁芬

1
@ruffin:优点是避免了复杂性。是的,据我所知,没有内置的“排序和散列”集合。请注意,这样的集合更难于使用和构建-必须同时提供相等性比较和哈希码功能以及排序功能。当然,保留两个集合也要消耗内存。您需要针对实际数据来衡量二进制搜索和哈希查找的成本-您可能会发现,除非您的数据库庞大,否则它实际上并不重要。
乔恩·斯基特

@ruffin“二进制搜索而不是在已排序的键旁边保留哈希的优点是什么?” -哈希表的内存以及添加或删除条目时的维护时间。“为什么我不在乎元素是有序的” –如果您不在乎元素是有序的,则只需使用字典(我通常建议在SortedList或SortedDictionary上使用它)“但其次要基于键顺序进行枚举“-在偶尔需要枚举的KeyValuePair时对其进行排序很简单:dict.OrderBy(kv => kv.Key)
Jim Balter

1
@ruffin“如果我真的只想按原样排序字典键怎么办?” -没有这样的东西-这是一个类别错误。数组可以在适当的位置排序……没有什么不是数组就可以。(好吧,除了带有可变节点的链表之外,对这种东西进行分类将是非常困难且非常昂贵的。)但是,您当然可以做类似的事情var array = dict.Keys.ToArray(); array.Sort();
Jim Balter


12

已经说明了正确的答案(只需使用SortedDictionary)。

但是,如果偶然需要将您的收藏集保留为Dictionary,则可以按有序的方式访问Dictionary键,例如,通过排序列表中的键,然后使用此列表来访问Dictionary。一个例子...

Dictionary<string, int> dupcheck = new Dictionary<string, int>();

...一些代码填写“ dupcheck”,然后...

if (dupcheck.Count > 0) {
  Console.WriteLine("\ndupcheck (count: {0})\n----", dupcheck.Count);
  var keys_sorted = dupcheck.Keys.ToList();
    keys_sorted.Sort();
  foreach (var k in keys_sorted) {
    Console.WriteLine("{0} = {1}", k, dupcheck[k]);
  }
}

不要忘记using System.Linq;这一点。


“如果偶然,您有必要将您的馆藏保留为Dictionary” –保留它的明显原因是它的插入,删除和查找速度要快得多。至于var keys_sorted = dupcheck.Keys.ToList(); keys_sorted.Sort();-简单的是keys_sorted = dupcheck.Keys.OrderBy(k => k).ToList();...如果你要使用的值,然后就foreach (var ent in dupcheck.OrderBy(kv => kv.Key)) Console.WriteLine($"{ent.Key} = {ent.Value}");
吉姆·巴尔特

这不是对Dictionary对象进行排序,而只是对显示进行排序,并且是一种解决方法:(
Ambuj,

7

根据设计,字典是不可排序的。如果您在字典中需要此功能,请查看SortedDictionary。


4

看一看SortedDictionary,甚至有构造函数重载,因此您可以传递自己的IComparable进行比较。


3

当Dictionary被实现为哈希表时,SortedDictionary被实现为红黑树。

如果您没有利用算法中的顺序,而只需要在输出之前对数据进行排序,那么使用SortedDictionary会对性能产生负面影响

您可以像这样对字典进行“排序”:

Dictionary<string, int> dictionary = new Dictionary<string, int>();
// algorithm
return new SortedDictionary<string, int>(dictionary);

如果您想按键对条目进行排序,请使用dictionary.OrderBy(kv => kv.Key)
Jim Balter,

2

由于这个答案,搜索排名很高,我认为值得展示LINQ OrderBy解决方案:

class Person
{
    public Person(string firstname, string lastname)
    {
        FirstName = firstname;
        LastName = lastname;
    }
    public string FirstName { get; set; }
    public string LastName { get; set; }
}

static void Main(string[] args)
{
    Dictionary<Person, int> People = new Dictionary<Person, int>();

    People.Add(new Person("John", "Doe"), 1);
    People.Add(new Person("Mary", "Poe"), 2);
    People.Add(new Person("Richard", "Roe"), 3);
    People.Add(new Person("Anne", "Roe"), 4);
    People.Add(new Person("Mark", "Moe"), 5);
    People.Add(new Person("Larry", "Loe"), 6);
    People.Add(new Person("Jane", "Doe"), 7);

    foreach (KeyValuePair<Person, int> person in People.OrderBy(i => i.Key.LastName))
    {
        Debug.WriteLine(person.Key.LastName + ", " + person.Key.FirstName + " - Id: " + person.Value.ToString());
    }
}

输出:

Doe, John - Id: 1
Doe, Jane - Id: 7
Loe, Larry - Id: 6
Moe, Mark - Id: 5
Poe, Mary - Id: 2
Roe, Richard - Id: 3
Roe, Anne - Id: 4

在此示例中,还可以对名字使用ThenBy

foreach (KeyValuePair<Person, int> person in People.OrderBy(i => i.Key.LastName).ThenBy(i => i.Key.FirstName))

然后输出是:

Doe, Jane - Id: 7
Doe, John - Id: 1
Loe, Larry - Id: 6
Moe, Mark - Id: 5
Poe, Mary - Id: 2
Roe, Anne - Id: 4
Roe, Richard - Id: 3

LINQ还为需要它的人提供了OrderByDescendingThenByDescending

By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.