【问题标题】:Fastest way to retrieve record from dictionary contains 1 million records [closed]从字典中检索记录的最快方法包含 100 万条记录 [关闭]
【发布时间】:2019-02-13 08:56:05
【问题描述】:

我有一本包含一百万条记录的字典;键是数字,其值为string。我想使用它的值从集合中检索键。

我的应用程序在多线程环境中运行。

那么,最快的方法是什么?

【问题讨论】:

  • 欢迎来到 SO。您要求最快,但没有显示您尝试过的任何内容。所以不会为您编写代码,没有任何显示的问题往往会被关闭。如果你有代码,codereview 可能是更好的地方来问这个
  • 字典中是否有重复值?您只想检索一个密钥还是需要检索多个?
  • 太多不清楚的方面。你的收藏会被不同的线程访问吗?你的收藏会发生变异吗?如果是,该集合的预期读取和写入比率是多少?
  • 注意:“在多线程环境中运行”是模棱两可的;那是“读写的多线程”吗? “多个线程读取,只有一个线程写入”? “多线程读取,数据一经构建永不改变”等; 真的,真的很重要

标签: c# multithreading winforms collections


【解决方案1】:

您的问题给人的印象是您的字典在键和值之间具有一对一的映射。如果是这种情况,and 如果字典不经常更改,and 如果您需要多次检索某个值的键,那么最快的方法是构建一个反向字典,其中原始字典中的值是键,键是值。这是一些前期工作,但之后会更快:

var revDict = new Dictionary<string, int>();
foreach (var kvp in yourDict) revDict[kvp.Value] = kvp.Key;

编辑:或者也许使用 LINQ:

var revDict = yourDict.ToDictionary(kvp => kvp.Value, kvp => kvp.Key);

【讨论】:

  • 不知道为什么这被否决了;这是一个很好的答案,尤其是它解释了注意事项(一对一,以及所有 等)。而且一旦支付了构建开销,这确实是从值中查找键的最快方法。
  • 旁注:如果是我,我会使用.Add(kvp.Value, kvp.Key) 而不是索引器;这将强制执行数据是一对一的假设,而不是默默地处理重复值。
  • @MarcGravell - 我尊重你的意见,但你不认为因为问题缺乏细节 - 特别是在多线程方面 - 那么任何答案都为时过早,可能会误导未来的读者。你能帮助我理解吗?
  • @Enigmativity 相反;正是因为我们的上下文有限,IMO 这是一个很好的答案;你是对的,它没有明确讨论问题没有指定的一些场景(例如,如果它在运行时发生突变,如何同步) - 但是......这有点像问答的性质。如果修改问题以显示更细微的上下文,那么可以肯定:答案可能也需要修改。
【解决方案2】:

如果我可以假设您具有与键和值的双向一对一映射,并且您将从多个线程访问和更新字典,那么我建议您应该创建一个线程安全的双向字典。

public class Map<T1, T2>
{
    private object _gate = new object();
    private Dictionary<T1, T2> _forward = new Dictionary<T1, T2>();
    private Dictionary<T2, T1> _reverse = new Dictionary<T2, T1>();

    public Map()
    {
        this.Forward = new Indexer<T1, T2>(_gate, _forward);
        this.Reverse = new Indexer<T2, T1>(_gate, _reverse);
    }

    public class Indexer<T3, T4>
    {
        private object _gate;
        private Dictionary<T3, T4> _dictionary;
        public Indexer(object gate, Dictionary<T3, T4> dictionary)
        {
            _dictionary = dictionary;
            _gate = gate;
        }
        public T4 this[T3 index]
        {
            get { lock (_gate) { return _dictionary[index]; } }
            set { lock (_gate) { _dictionary[index] = value; } }
        }
    }

    public void Add(T1 t1, T2 t2)
    {
        lock (_gate)
        {
            _forward.Add(t1, t2);
            _reverse.Add(t2, t1);
        }
    }

    public Indexer<T1, T2> Forward { get; private set; }
    public Indexer<T2, T1> Reverse { get; private set; }
}

你会这样使用它:

var map = new Map<int, string>();

map.Add(42, "Life");

Console.WriteLine(map.Forward[42]);
Console.WriteLine(map.Reverse["Life"]);

输出:

生活 42

【讨论】:

  • 您可能需要考虑的轻微边缘情况:在Add 中,您应该检查_reverse 是否已经 有一个以t2 键入的条目之前 你_forward.Add,因为如果第一个Add 成功并且第二个Add 抛出,你已经破坏了预期的行为(你不需要检查_forward/t1,虽然 - 让随心所欲地扔)
  • @MarcGravell - 是的,这是真的。这是一个相当杂乱无章的课程。它确实需要更多的工作才能使其健壮。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-11-19
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多