【问题标题】:What is the best collection type to easily lookup values with multiple, identical keys?使用多个相同键轻松查找值的最佳集合类型是什么?
【发布时间】:2025-12-02 17:30:01
【问题描述】:

我有 text 文档,如下所示,其中包含 singlemultiple 变量:

title:: Report #3
description:: This is the description.
note:: more information is available from marketing
note:: time limit for this project is 18 hours
todo:: expand the outline
todo:: work on the introduction
todo:: lookup footnotes

我需要遍历这个文本文档的行并用这些变量填充一个集合,目前我正在使用一个字典

public Dictionary<string, string> VariableNamesAndValues { get; set; }

但这不适用于多个相同键,例如上面示例中的“note”和“todo”,因为键必须是唯一的 在字典中。

什么是最好的集合,这样我不仅可以得到这样的单一值:

string variableValue = "";
if (VariableNamesAndValues.TryGetValue("title", out variableValue))
    return variableValue;
else
    return "";

但我也可以像这样得到多个值:

//PSEUDO-CODE:
List<string> variableValues = new List<string>();
if (VariableNamesAndValues.TryGetValues("note", out variableValues))
    return variableValues;
else
    return null;

【问题讨论】:

    标签: c# collections dictionary


    【解决方案1】:

    您可以使用PowerCollections,这是一个开源项目,它有一个多字典数据结构,可以解决您的问题。

    这是sample of how to use it

    注意:Jon Skeet 之前在他对 this question. 的回答中提出了这个建议

    【讨论】:

    • +1,我喜欢 PowerCollections。但它是在 .NET 2.0 左右设计的,从那以后就没有真正被触及过。考虑到 .NET 3.5 或 4.0,它可能会使用重写。
    【解决方案2】:

    您可以制作key:字符串和value:字符串列表

    的字典

    Dictionary&lt;string,List&lt;string&gt;&gt;

    编辑 1 和 2:
    如果您可以使用 .NET 3.0 或更高版本,我已经想到了一个更好的解决方案。
    这是一个 LINQ 示例(我在没有 Visual Studio 的情况下键入它,所以我希望它能够编译;)):

    string[] lines = File.ReadAllLines("content.txt");
    string[] separator = {":: "};
    var splitOptions = StringSplitOptions.RemoveEmptyEntries;
    
    var items = from line in lines
                let parts = line.Split(separator, splitOptions)
                group parts by parts[0] into partGroups
                select partGroups;
    

    上面例子的简短解释:

    • 从字符串数组中获取文件中的所有行
    • 定义一些拆分选项(以保持示例可读性)
    • 对于lines数组中的每一行,将其拆分为“::”
    • 对第一个拆分部分的拆分结果进行分组(例如标题、描述、注释...)
    • 将分组的项目存储在 items 变量中

    LINQ 查询的结果是IQueryable&lt;IGrouping&lt;string, IEnumberable&lt;string&gt;&gt;&gt;
    结果中的每个项目都有一个 Key 属性,其中包含行的键(标题、描述、注释...)。
    可以枚举包含所有值的每个项目。

    【讨论】:

    • 这绝对是一个解决方案,但我自己会做很多收藏管理工作,例如逻辑,例如 if-key-doesn't-exist-yet create-new-entry 否则 add-to-List-of-existing-entry,我认为这在一些核心的编程中是一个足够普遍的需求.NET 集合类型将存在以固有地处理它,所以我可以说 .Add(key, value) 并且单个或多个键都存储在内部。
    • @Edward:不抱歉,我不知道。你可以做的是使用 LINQ 来处理你的字符串行并使用 ToDictionary 方法来创建你的字典。
    • @Edward:我已经考虑了更多,看看新的例子:)
    【解决方案3】:

    您可以使用Lookup&lt;TKey, TElement&gt;

    ILookup<string, string> lookup = lines.Select(line => line.Split(new string[] { ":: " })
                                          .ToLookup(arr => arr[0], arr => arr[1]);
    IEnumerable<string> notes = lookup["note"];
    

    注意这个集合是只读的

    【讨论】:

    • 啊,这也是我一直在寻找的,但我不记得了。我最近在 Jon Skeet 的推文上看到了它。
    【解决方案4】:

    如果您的键和值是字符串,则使用NameValueCollection。它支持给定键的多个值。

    这不是世界上最有效的集合。特别是因为它是一个非泛型类,使用了大量的虚方法调用,而且 GetValues 方法会为其返回值分配数组。但除非您需要最好的性能系列,否则这肯定是最方便满足您要求的系列。

    【讨论】:

    • 但是您必须在检索值时手动拆分它们。我建议将其包装到它自己的小类中。
    • 不,那不是真的。有一个 GetValues 方法。
    • 你不喜欢那种尴尬的感觉吗?
    • 可以理解。 NameValueCollection 在很多方面都很奇怪。例如,它没有实现 IDictionary。
    • 我敢打赌,这个集合的性能问题被您必须在其他解决方案中使用的变通方法所抵消。
    【解决方案5】:

    我过去曾使用Dictionary&lt;string, HashSet&lt;string&gt;&gt; 获取多个值。不过我很想知道是否有更好的东西。

    这是您如何模拟只获得一个值的方法。

    public static bool TryGetValue(this Dictionary<string, HashSet<string>> map, string key, out string result)
    {
        var set = default(HashSet<string>);
        if (map.TryGetValue(key, out set))
        {
            result = set.FirstOrDefault();
            return result == default(string);
        }
        result = default(string);
        return false;
    }
    

    【讨论】:

    • @ChaosPandion +1:HashSet 是一个很好的建议,尽管它并不比小列表的 List 快得多。而且您失去了通过索引从集合中获取特定项目的能力。我经常将 HashSets 用于复杂对象或大型值类型列表。
    • @Zyphrax - 同意,我只记得字典中的每个条目都必须具有一组唯一值的情况。 LINQ 使使用 HashSet 更加方便,如果不是更慢的话。
    【解决方案6】:

    我不是 c# 专家,但我认为 Dictionary&lt;string, List&lt;string&gt;&gt;

    或某种HashMap&lt;string, List&lt;string&gt;&gt; 可能会起作用。 例如(Java 伪代码): aKey aValue aKey anotherValue

    if(map.get(aKey) == null)
    {
       map.put(aKey, new ArrayList(){{add(aValue);}});
    } 
    else 
    {
       map.put(aKey, map.get(aKey).add(anotherValue));
    }
    

    或类似的东西。 (或者,最短的方式:

    map.put(aKey, map.get(aKey) != null ? map.get(aKey).add(value) : new ArrayList(){{add(value);}});
    

    【讨论】:

      最近更新 更多