如何在字典中插入元素到第一个索引位置?

22
有没有一种方法或技术可以让你将一个元素插入到 Dictionary<TKey, TValue> 中,并保证该项在该字典的 KeyCollection 的第一个索引中。
例如:
Dictionary<String, String> dic = foo.GetOutput(); 

// `dic` is something like:

// {"foo", "baa"},
// {"a", "b"}

我需要类似于:

dic.Add("key", "value", 0);
// where `0` is the index that `key` to be inserted.

foreach(KeyValuePair<String, String> key in dic) 
{
     Console.WriteLine("{0} = {1}", key.Key, key.Value);
}

输出:

key = value
foo = baa
a = b

2
你使用字典的需求是什么?字典集合中的项目顺序旨在具有灵活性(您可以重新排序等),索引对用户隐藏。根据您的需求,您可能希望使用不同类型的集合更加合适。 - Guy Starbuck
9个回答

29

不使用字典。

Dictionary<TKey, TValue> 是使用哈希表实现的。键在字典内部的位置取决于哈希码,以及将该哈希码进一步缩减为索引的方式,以及插入顺序,在完全依赖于具体实现的方式下进行排序。

这不是实现字典的唯一方法。SortedDictionary<TKey, TValue> 内部使用树结构,因此始终按顺序保留键。在这种情况下,我们仍然无法在开头插入东西,而是插入某个元素后它会被放在适当的位置。

如果你最关心的是排序,那么你根本就不需要一个纯字典。相反,你需要List<KeyValuePair<TKey, TValue>>,或者你需要一个既具有列表功能又具有字典功能的结构,这由OrderedDictionary提供。这并非通用类型,但你可以轻松地创建一个通用包装器(虽然它不像内部使用泛型那样具有性能优势,但在使用中可以提供类型安全性)。


那么,像OrderBy这样的方法的目的是什么呢?(Linq扩展)。枚举将按照什么顺序访问条目? - Keren
@Keren 目的是按特定标准接收元素,枚举它时将按该顺序访问元素(源将以任何顺序访问,排序发生在完成后)。我不确定这与什么相关。 - Jon Hanna
我想说的是,字典确实有某种不依赖于“键”的顺序。因此,我希望有一种方法来控制这个顺序。我知道现在还没有这种方法。 - Keren
2
@Keren,不,字典没有这样的顺序依赖于“键”(Key)。首先,可以使用不可比较的类型作为“键”,如果字典需要排序,则这是不可能的。 - Jon Hanna

16

我知道这是一个三年前的问题。但找到了解决这个问题的方法。它可能会对某些人有所帮助。

Dictionary<String, String> dic = foo.GetOutput();

dic = (new Dictionary<string, string> {{"key","value"}}).Concat(dic).ToDictionary(k => k.Key, v => v.Value);

这将在字典的开头插入该元素 :)


1
这是一个实现细节,即使字典调整其内部数组大小,您也不能保证第一个项目仍然是第一个项目。 - Scott Chamberlain

9

字典是无序的; 元素是通过其哈希指向值位置的键来检索的。

你可能需要的是一个List <KeyValuePair>,它的元素可以插入到特定的索引中。

List<KeyValuePair<string, string>> list = dic.ToList();
list.Insert(0, new KeyValuePair<string, string>("a", "b"));

foreach(KeyValuePair<string, string> pair in list)
    Console.WriteLine("{0} = {1}", pair.Key, pair.Value);

3
这在使用Dictionary<TKey, TValue>时是不可能的,因为当枚举时它以无序的方式呈现其值。虽然有SortedDictionary<TKey, TValue>提供排序,但它是通过直接使用IComparer<TKey>来针对键值进行排序的。在这里,您需要将键设置为一个String,并基于int进行排序。这两种类型都无法实现。
我认为您需要实现一个新类型,具有这些非常特定的语义。例如。
class OrderedMap<TKey, TValue> {
  private readonly Dictionary<TKey, TValue> _map = new Dictionary<TKey, TValue>();
  private readonly List<TKey> _list = new List<TKey>();

  public void Add(TKey key, TValue value) {
    if (!_map.ContainsKey(key)) {
      _list.Add(key);
    }
    _map[key] = value;
  }

  public void Add(TKey key, TValue value, int index) {
    if (_map.ContainsKey(key)) {
      _list.Remove(key);
    }
    _map[key] = value;
    _list.Insert(index, key);
  }

  public TValue GetValue(TKey key) {
    return _map[key];
  }

  public IEnumerabe<KeyValuePair<TKey, TValue>> GetItems() {
    foreach (var key in _list) { 
      var value = _map[key];
      yield return new KeyValuePair<TKey, TValue>(key, value);
    }
  }
}

请注意,与传统的Dictionary<TKey, TValue>相比,这会带来一些非常显著的性能差异。例如AddRemove的速度较慢。


2

Dictionary<TKey, TValue>本质上是无序的(或者说,排序是不可预测的,不能依赖它)。如果您想要某种排序方式,需要使用不同类型。如果不了解您的需求,很难推荐任何特定类型。


2

2

无法对 Dictionary<TKey, TValue> 进行排序。

您可以尝试使用 SortedDictionary<TKey, TValue>,但它是根据键而不是单独的索引排序的。


2
这是我的解决方案,可能不是最好的,但它有效。 =)
public static ComboBox FillDropDownList(Dictionary<String, String> dictionary, ComboBox dropDown, String selecione)
{
    var d = new SortedDictionary<String, String>();

    d.Add("0", selecione);

    foreach (KeyValuePair<string, string> pair in dictionary)
    {
        d.Add(pair.Key, pair.Value);
    }

    dropDown.DataSource = new BindingSource(d, null);
    dropDown.DisplayMember = "Value";
    dropDown.ValueMember = "Key";

    dropDown.SelectedIndex = 0;

    return dropDown;
}

1

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接