【问题标题】:Is there a way to keep track of the ordering of items in a dictionary?有没有办法跟踪字典中项目的顺序?
【发布时间】:2023-08-28 13:01:01
【问题描述】:

我有一个Dictionary<Guid, ElementViewModel>。 (ElementViewModel 是我们自己的复杂类型。) 我用股票标准items.Add(Guid.NewGuid, new ElementViewModel() { /*setters go here*/ });将项目添加到字典中,

稍后我会删除部分或全部这些项目。

我的 ElementViewModel 的简单视图是这样的:

class ElementViewModel
{
    Guid Id { get; set; }
    string Name { get; set; }
    int SequenceNo { get; set; }
}

值得一提的是,SequenceNos 在添加后会在集合中被压缩,以防发生移动和复制等其他操作。 {1, 5, 6} -> {1, 2, 3}

我的删除操作的简单视图是:

public void RemoveElementViewModel(IEnumerable<ElementViewModel> elementsToDelete)
{
    foreach (var elementViewModel in elementsToDelete)
        items.Remove(elementViewModel.Id);

    CompactSequenceNumbers();
}

我会用一个例子来说明问题:

我在字典中添加了 3 个项目:

var newGuid = Guid.NewGuid();
items.Add(newGuid, new MineLayoutElementViewModel { Id = newGuid, SequenceNo = 1, Name = "Element 1" });
newGuid = Guid.NewGuid();
items.Add(newGuid, new MineLayoutElementViewModel { Id = newGuid, SequenceNo = 2, Name = "Element 2" });
newGuid = Guid.NewGuid();
items.Add(newGuid, new MineLayoutElementViewModel { Id = newGuid, SequenceNo = 3, Name = "Element 3" });

我删除了 2 个项目

RemoveElementViewModel(new List<ElementViewModel> { item2, item3 }); //imagine I had them cached somewhere.

现在我想添加另外 2 项:

newGuid = Guid.NewGuid();
items.Add(newGuid, new MineLayoutElementViewModel { Id = newGuid, SequenceNo = 2, Name = "Element 2, Part 2" });
newGuid = Guid.NewGuid();
items.Add(newGuid, new MineLayoutElementViewModel { Id = newGuid, SequenceNo = 3, Name = "Element 3, Part 2" });

在此时评估字典时,我预计项目的顺序是 “元素 1”、“元素 2,第 2 部分”、“元素 3,第 2 部分”

但实际上是按以下顺序排列的: “元素 1”,“元素 3,第 2 部分”,“元素 2,第 2 部分”


我依靠这些项目的顺序来确定方式。为什么不符合预期,我该怎么办?

【问题讨论】:

    标签: c# .net dictionary hashtable


    【解决方案1】:

    .Net 字典在设计上是无序的。

    您应该改用KeyedCollection&lt;TKey, TValue&gt;;它将保留项目添加到集合中的顺序,并且还将使用哈希表进行快速查找。

    例如:

    class ElementViewModelCollection : KeyedCollection<Guid, ElementViewModel> {
        protected override Guid GetKeyForItem(ElementViewModel item) { return item.Id; }
    }
    
    items.Add(new MineLayoutElementViewModel { Id = Guid.NewGuid(), SequenceNo = 3, Name = "Element 3" });
    

    请注意,如果在将项目添加到集合后更改 Id 属性,则需要在集合上调用 ChangeItemKey 方法。我强烈建议您将Id 属性设为只读。

    【讨论】:

      【解决方案2】:

      您不使用 System.Collections.Generic.SortedDictionary 的任何原因似乎都是您正在寻找的原因

      【讨论】:

        【解决方案3】:

        不幸的是,对于我们必须在其中存储的大量数据,SortedDictionary 的速度不够快,而且 KeyedCollection 违背了手动压缩元素的 SequenceNo 属性的目的。

        严格来说,我们应该重写排序发生的方式,因为我的解决方案不是最漂亮的:

        每次删除项目时,都会新建字典并将未删除的项目重新添加到新字典中,以保持默认顺序。 --> 可怕的做法,我承认。等我压力小了就计划换吧。

        【讨论】:

          最近更新 更多