Modify dictionary values without allocating
我需要修改字典中的所有值。通常,在枚举字典时修改它会引发异常。有各种方法可以解决这个问题,但我看到的所有答案都涉及到分配临时存储。有关示例,请参见在foreach循环中编辑字典值。
我希望在不分配任何内存的情况下修改所有值。为忽略字典版本的值编写自定义结构枚举器是可以的,但是由于字典的所有重要成员都是私有的,所以这似乎是不可能的。
- 你不想使用ConcurrentDictionary的原因是什么?您的问题真的是并发访问或内存分配的异常吗?
- 您提供的链接似乎表明在字典上调用keys.to list()并在该列表上迭代应该可以工作…不?
- @虽然问题很不清楚。我相信OP引用的异常是在迭代集合时修改集合的异常。这不是并发访问的问题。
- 我想调用keys.tolist()可能算作分配内存?如果是这样,那么最好的解决方案可能是在自定义类上实现IDictionary。
- 我可以考虑一些可能的方法,但它们都有性能和行为上的权衡。你明白为什么字典在迭代时被修改会抛出异常吗?如果有人试图同时迭代和修改它,您是否可以分享您希望它的行为方式?为什么你不愿意分配更多的内存?并发是一个问题,还是这是单线程的?你想要实现的具体用例是什么?
- @我没有使用ConcurrentDictionary,因为在程序的其他地方,我锁定了对该字典的访问。此外,我不知道ConcurrentDictionary如何允许我在不分配的情况下修改字典中的所有值。
- @克里斯伯格是的,那很管用…但是会分配内存,这对于我的性能约束来说是不够的。
- @脱衣武士是的,我理解为什么它是这样实现的。但是,在不需要调整大小的情况下,我认为修改现有键的版本增量是为了与添加+调整大小的情况保持一致。对这本字典的访问受读写器LockSlim的保护,因此就我而言,它是单线程的。
- @Chrisberger同样,仅供参考,如果您对IDictionary C的任何实现进行迭代,将强制对枚举器进行装箱,从而导致不需要的分配(尽管公认的是,这比键的列表副本的分配少得多)。不过,我同意你实现类似IDictionary的一般想法。
- 我不知道为什么投反对票。有人能提出一个能使问题更清楚的编辑建议吗?
- 使用for循环的dictionary[key] = new_value;应该可以做到这一点。如果键不存在,索引器将创建新的项,否则它将更新从项引用的值。
- @卡兹马卡里:你得举个例子。你的for循环到底是什么样子的?如果不先为键分配一个单独的列表或数组,我看不出这是如何工作的。
- 如果这些值是类,那么您可以在根本不修改字典的情况下将它们修改为您的核心内容。
- @扎卡里伯恩斯:我没有投反对票,但我认为如果你要添加一个小的代码示例来展示你在迭代过程中试图做的修改,它会帮助很多人。
- @Jeroemostert:我猜如果他不愿意为所有字典键分配空间,那么他在添加这样的对象层时可能会遇到类似的内存开销问题。
- @脱衣武士:我的评论是一个微妙的点头以外的拳击思考。(对不起,无法抗拒。)
在这里,您肯定正在进行一些基本的性能优化。
根据您在注释中提供的附加信息,听起来最好的方法(除了升级内存以便处理更多的分配)可能是采用字典源代码并为此专门创建一个新类,如果只是更改,则不会增加version字段。一个值。
- 我害怕那个结论。您可能应该更新链接以指向.NET核心实现,而不是出于许可原因而引用源。如果您遵循一些规则,MIT许可证将允许修改,而MS-RSL许可证只允许"引用"源代码。
- 完成。说得对。
- 最后,这条路走得很好。我最终得到了一个字典,您可以在迭代时迭代和替换值,甚至可以在迭代时删除键。
- 我很高兴它成功了。不过,我不建议在迭代过程中删除键,因为如果项目恰好与您删除的项目放在同一个bucket中,那么您可能会跳过这些项目。
- 在这种情况下,可以在迭代时删除键。新类只允许您通过keyValuePair替换值或删除键,确保您正在删除当前所属的项。引用源字典迭代的工作方式是,它不在bucket上迭代,而是简单地迭代entries数组。当删除一个项目时,它会从该数组中清除一个条目,然后从bucket中修复链接列表,因此这不会成为问题。