C#中有什么替代字典且允许重复键的方法?

36

我有一个方法,可以返回曾在某些项目上工作的技术人员组,例如:

project 1 | John
project 1 | Tim
project 2 | John
project 2 | Dave

我原本尝试创建一个字典,而这通常是我使用的键值对集合,但在此情况下我无法使用它,因为我不能有重复的键(即项目)。有没有其他可以使用的替代方法?

我唯一想到的是创建一个Dictionary<Project, List<Technicians>>,但是否有更简单的方法呢?


1
字典确保其中不会有重复的键,问题出在哪里? - Vsevolod Goloviznin
15
Dictionary<Project, List<Technicians>> 看起来已经很不错了。但是如果你不需要将 Project 作为键(即你不需要获取某个项目的所有技术人员),那么你可以尝试使用 List<Tuple<Project, Technician>> - Andrey Korneyev
@AndyKorneyev 我需要每个项目的所有技术人员,但如果可能的话,我想避免二维类型访问,但我认为你的第一个选项将是前进的方式。 - AdamMc331
5
这种数据结构被称为multimap,不幸的是,在.NET类库中并没有这种数据结构。 - Jörg W Mittag
3
也许你已经明白了,但是你所描述的更常被认为是每个键拥有多个值。从技术上讲,我想它是同样的事情,但至少在我的听觉中,谈论“重复的键”听起来很奇怪,更像是一个错误条件或无效状态。 - hyde
显示剩余6条评论
5个回答

49
在您的情况下,同一键与多个值相关联,因此标准字典不适用于这种情况,您可以像这样声明:Dictionary<Key, List<Values>>
但是,您也可以使用: Lookup 类,它表示一个由每个键映射到一个或多个值的集合。
您需要 3.5 及更高版本的框架才能使用它。

1
@Groo:不过,用 ToLookup 扩展方法将序列或字典转换很容易。 - Dennis
14
@Dennis: 我怀疑原帖作者不会觉得这个有用。Lookup是只读集合,只能从现有数据中使用LINQ创建。你不能自己实例化或改变它。 - vgru
6
更改 Project 类是首选,因为明显需要将一个项目与技术人员列表链接起来。如果不能更改 Project 类的源代码,可以考虑使用 Lookup 类或 Dictionary,但是如果可以更改源代码,则有利于创建两个类之间的__清晰连接__。 - Greg Burghardt
@GregBurghardt 我完全理解你的意思。我会与项目的首席开发人员讨论这个问题。否则,我很可能会使用 Lookup - AdamMc331
@McAdam331:不行。在List<Tuple>的情况下,您无法像访问键一样访问它。 - Tigran
显示剩余5条评论

13
您需要的是一个项目与一个或多个技术人员之间的关系:
public class Project
{
    public ICollection<Technician> Technicians { get; set; }
}

var project = new Project();
project.Technicians = new List<Technician>()
{
    new Technician(),
    new Technician()
};

你的对象应该反映现实生活中的关系。

顺便提一句,你可能有兴趣阅读有关领域驱动设计的内容。

public void LoadTechnicians(Project project)
{
    List<Technician> techs = new List<Technician>();

    // query the database and map Technician objects

    // Set the "Technicians" property
    project.Technicians = techs;
}

项目中技术人员的快速搜索怎么样? - Dennis
这已经通过“项目”拥有技术人员列表的事实得到处理。 - Greg Burghardt
1
@Dennis 如果你有一个 Project 并且你需要所有的 Technicians,那么在这个例子中,你只需要调用 Project 的一个属性,而不是将其输入到字典中并处理输出。这样做会更容易和更快。 - Servy

13

2
只是一条注释,但它不再是MultiDictionary。它已经被重命名为MultiValueDictionary,以更好地阐明其功能:http://blogs.msdn.com/b/dotnet/archive/2014/08/05/multidictionary-becomes-multivaluedictionary.aspx - Greg

5
我认为你的解决方案没有问题。毕竟,你可以通过项目轻松地访问所有团队成员。但另一种选择是尝试使用List<KeyValuePair<Project, Technician>>。你可以保持键值关系,但不受不重复键的限制。这比你现在拥有的要简单得多吗?这取决于具体情况。 此外,你可以在自定义集合实现中隐藏此结构。

看起来他们不想两次使用“project 1 | John”。也许可以用HashSet<KeyValuePair<Project, Technician>> - Arturo Torres Sánchez
@ArturoTorresSánchez 不,我不想要重复的键值对。项目1不能有重复的技术人员。 - AdamMc331
@McAdam331,...我本来想说KeyValuePair覆盖了Equals,但事实上似乎并没有。那么我猜Tuple可能是一个更好的选择(仍然使用HashSet)。 - Arturo Torres Sánchez

0

我从this post中复制粘贴了自己的答案。

编写一个允许“重复键”条目的字典版本非常容易。这里是一个简单的实现。您可能希望考虑添加对基本上大多数(如果不是全部)IDictionary<T>的支持。

public class MultiMap<TKey,TValue>
{
    private readonly Dictionary<TKey,IList<TValue>> storage;

    public MultiMap()
    {
        storage = new Dictionary<TKey,IList<TValue>>();
    }

    public void Add(TKey key, TValue value)
    {
        if (!storage.ContainsKey(key)) storage.Add(key, new List<TValue>());
        storage[key].Add(value);
    }

    public IEnumerable<TKey> Keys
    {
        get { return storage.Keys; }
    }

    public bool ContainsKey(TKey key)
    {
        return storage.ContainsKey(key);
    }

    public IList<TValue> this[TKey key]
    {
        get
        {
            if (!storage.ContainsKey(key))
                throw new KeyNotFoundException(
                    string.Format(
                        "The given key {0} was not found in the collection.", key));
            return storage[key];
        }
    }
}

一个使用它的快速示例:

const string key = "supported_encodings";
var map = new MultiMap<string,Encoding>();
map.Add(key, Encoding.ASCII);
map.Add(key, Encoding.UTF8);
map.Add(key, Encoding.Unicode);

foreach (var existingKey in map.Keys)
{
    var values = map[existingKey];
    Console.WriteLine(string.Join(",", values));
}

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接