【问题标题】:Is it possible to have multiple Values per Key without having to look up the Key first?是否可以在不必先查找密钥的情况下为每个密钥设置多个值?
【发布时间】:2014-01-14 14:14:32
【问题描述】:

我正在编写一个专注于性能的 2D 无限地形编辑器。每次用户绘画时,他都会创建Nodes,此时将其分配给“图层”类中的字典。由于用户可以在不同图层上绘制现有节点,因此我想找到这些节点并销毁它们。根据我目前的设置,这将涉及遍历所有层并从字典中一个一个获取必要的节点。

显然,如果每帧执行数万次,这太昂贵了。所以现在我想我可以只创建一个包含列表中所有节点的字典。然而,事实证明这几乎与之前的设置一样使用了性能,因为现在我必须在实例化新节点时检查 Key 是否已经存在:

之前:

public Node(){
  dic.Add(key,this);
}

现在:

public Node(){
  List<Node> nodes;
  if(dic.TryGetValue(key,out nodes))
    nodes.Add(this);
  else{
    list = new List<Node>();
    dic.Add(key,list);
  }
}

如您所见,虽然当前设置在检查节点时节省了一些性能,但它通过将实例化时间扩大到字典查找时间而完全破坏了效果。

所以现在我正在寻找某种方法来获得与 Dictionary&lt;Key,List&lt;Node&gt;&gt; 相同的效果,但无需在字典中查找列表的成本。是否有一种不同类型的字典可以让每个键堆叠无限(或如果必要的话)数量的值?

或者,是否有可能以某种方式对字典中的值进行排序,以便我可以使用普通的Dictionary&lt;key, Node&gt;,然后查找第一个键并从那里循环遍历所有值,直到我点击某个不再适合的节点搜索条件?

在伪代码中,我的实例化函数如下所示:

public Node(){
  dic.Add(key+LayerIndex,this);
}

我的查找函数看起来像这样

public LookUpAll(key, out list){
  var o = dic[key]; // looking up just one node
  list.Add(o);
  var keyPosition = o.keyPosition;
  while(o.position == keyPosition){ // then looping through all other nodes at that position until one comes that has a different position.
    o = dic.NextValue();
    list.Add(o);
  }
}

现在,这在未排序的字典中不起作用,但因为我希望这里的某个人可以从中获得一些灵感并制定一个可行的解决方案。

如何在我的场景中减少查找次数,或者在不先查找键的情况下为每个键添加多个值?

【问题讨论】:

  • 你确定字典查找是这个过程的瓶颈吗?
  • 我现在已经运行了一些基准测试,如果我实例化 500^2 个节点,那么检查的性能成本增加在 1/2 和 1 % 之间。老实说,我希望它会更大,因为它曾经是过去的主要瓶颈,直到我覆盖了哈希函数,但即便如此,摆脱它还是不错的,特别是因为我希望进一步提高性能而且我不希望这件事在未来成为真正的瓶颈。
  • 对于您的替代解决方案,请考虑使用 SortedDictionary。然后,您将传递您自己的 IComparer 实现作为键,以便对字典进行正确排序以满足您的查找要求。我不能说这会或多或少的表现。
  • @aaron 谢谢,我会试一试的,我已经辞职重新坐在板凳上写一些 n-tree 解决方案,但是正确排序的字典会容易得多。跨度>

标签: c# .net dictionary


【解决方案1】:

因为用户可以在不同的节点上绘制现有节点 层,

我将假设这样做是为了您最终不会保留不需要的数据。但是如果用户不打算删除该层上的数据怎么办?他只是想“看看”某物的样子。然后他撤消了他的更改和 blammo,您没有关于他刚刚所做的更改的数据,因此您无法撤消它。

为什么不保留所有节点,除非用户明确试图删除它们?最终用户始终可以选择在其最终结果中剔除的截锥体。

您也可以尝试使用树状结构。

【讨论】:

  • 你提出了一个很好的观点,我还没有想到可逆性。然而,所有这些微优化的一个主要原因是脚本也需要在游戏过程中运行。我希望它能够实时生成地形,而不必依赖多线程来隐藏性能成本。
  • 说你不想依赖多线程来“隐藏性能成本”让我感到困惑,因为多线程是一种可以使用的工具。无论哪种方式,字典查找都非常快。正如有人提到的,你确定字典是瓶颈吗?此外,您甚至对此进行了基准测试还是只是过早的优化(邪恶)?
  • 是的,我很邪恶……但我仍然想尽可能地节省性能。那么你能详细说明你的树状结构吗?您的意思是像 Bas Brekelmans 发布的内容吗?
【解决方案2】:

如果您只关心与某个键相关的最顶层节点,并且有时需要遍历所有节点,您可以从您的节点构造一个链表。这允许您遍历节点并按键检索相关节点。但是,它会增加节点的内存消耗。您可以通过创建一个拥有Next 属性的Node 的子类来最小化这种情况,同时保留普通节点而没有底层节点的可能性。不过,这可能会减慢迭代速度。

public Node Next { get; set; }

public Node()
{
    Node next;
    if (dic.TryGetValue(key, out next))
        this.Next = next;
    dic[key] = this;
}

【讨论】:

    猜你喜欢
    • 2019-08-29
    • 2021-10-24
    • 1970-01-01
    • 2022-01-14
    • 2021-08-20
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多