关于c#:使用多个相同的键轻松查找值的最佳集合类型是什么?

What is the best collection type to easily lookup values with multiple, identical keys?

我有如下文本文档,其中包含单个和多个变量:

1
2
3
4
5
6
7
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

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

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

但在上面的示例中,这对多个相同的键(如"note"和"todo")不起作用,因为键在字典中必须是唯一的。

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

1
2
3
4
5
string variableValue ="";
if (VariableNamesAndValues.TryGetValue("title", out variableValue))
    return variableValue;
else
    return"";

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

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

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

它不是世界上最有效的收藏品。尤其是因为它是一个非泛型类,使用了大量的虚拟方法调用,而getValues方法将为其返回值分配数组。但是,除非您需要性能最好的集合,否则这无疑是最方便的集合,可以满足您的要求。


您可以创建一个键:字符串和值:字符串列表的字典

Dictionary>

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

1
2
3
4
5
6
7
8
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;

上述示例的简短说明:

  • 从字符串数组中的文件中获取所有行
  • 定义一些拆分选项(以保持示例可读)
  • 对于行数组中的每一行,将其拆分到":"
  • 将拆分结果分组到第一个拆分部分(例如标题、说明、注释等)
  • 将分组的项存储在items变量中

LINQ查询的结果是一个IQueryable>>。结果中的每个项都有一个Key属性,其中包含行的键(标题、说明、注释等)。可以枚举包含所有值的每个项。


您可以使用PowerCollections,它是一个开放源代码项目,具有解决问题的多部门数据结构。

这是一个如何使用它的示例。

注:乔恩·斯基特在回答这个问题时曾提出过这个建议。


您可以使用Lookup

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

请注意,此集合是只读的


我不是首席专家,但我认为Dictionary>

或者某种类型的HashMap>可能有效。例如(Java伪代码):阿基瓦尔其他价值

1
2
3
4
5
6
7
8
if(map.get(aKey) == null)
{
   map.put(aKey, new ArrayList(){{add(aValue);}});
}
else
{
   map.put(aKey, map.get(aKey).add(anotherValue));
}

或者类似的东西。(或者,最短路径:

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

我以前用Dictionary>来获取多个值。我很想知道是否还有更好的。

下面是如何模拟只获取一个值。

1
2
3
4
5
6
7
8
9
10
11
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;
}