【问题标题】:What is an alternative to Dictionaries in C# that allows for duplicate keys?C# 中允许重复键的字典的替代方法是什么?
【发布时间】:2015-02-13 15:35:07
【问题描述】:

我有一个方法可以返回从事某些项目的技术人员组,例如:

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

我最初尝试创建一个字典,它通常是我的键值对的首选集合,但在这种情况下,我无法使用它,因为我不能有重复的键(项目)。我可以使用什么替代方案?

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

【问题讨论】:

  • 字典确保你不会有重复的键,有什么问题?
  • @VsevolodGoloviznin 问题是我想要一个重复的键,但仍然有唯一的键值对(如果我可以限制那部分)。
  • Dictionary<Project, List<Technicians>> 看起来不错。但是,如果您真的不需要 Project 作为密钥(即您不需要让某个项目的所有技术人员),那么您可以尝试 List<Tuple<Project, Technician>>
  • 这个数据结构被称为multimap,不幸的是,.NET 类库中没有。
  • 现在您可能已经很清楚了,但是您所描述的通常被认为是每个键具有多个值。我想在技术上是一样的,但至少在我看来,谈论“重复键”听起来倒退了,更像是错误条件或无效状态。

标签: c# dictionary collections


【解决方案1】:

在您的情况下,相同的键与 多个 值相关,因此标准字典不适合。您可以将其声明为Dictionary<Key, List<Values>>

但是,你也可以使用:

Lookup 类,即

表示一组键,每个键映射到一个或多个值。

为此,您需要框架 3.5 及更高版本。

【讨论】:

  • Lookup 有公共构造函数吗?
  • @Groo:不,但是很容易转换带有ToLookup扩展名的序列或字典。
  • 我认为 Lookup 类在这里掩盖了真正的问题。 OP 与应该由 Project 类上的属性建模的类之间存在关系。
  • @Dennis:我怀疑 OP 会觉得这很有用。 Lookup 是只读集合,只能使用 LINQ 从现有数据创建。你不能自己实例化它,也不能改变它。
  • 更改Project首选,因为您显然需要一个链接到技术人员列表的项目。如果您无法更改 Project 类的源代码,我可以看到使用 Lookup 类或 Dictionary,但如果可以,创建 两个类之间的清晰连接很有用。
【解决方案2】:

您需要的是Project 与一名或多名技术人员之间的关系:

public class Project
{
    public ICollection<Technician> Technicians { get; set; }
}

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

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

作为旁注,您可能有兴趣阅读有关 Domain-driven design 的信息。

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;
}

【讨论】:

  • 按项目快速搜索技术人员怎么样?
  • Project 拥有技术人员名单这一事实已经处理完毕。
  • @Dennis 如果你有一个Project 并且你需要所有的Technicians,那么在这个例子中你需要做的就是调用Project 的一个属性,而不是喂将其放入字典并处理输出。这将更容易更快。
【解决方案3】:

an experimental NuGet package from MS that contains MultiValueDictionary

基本上,它就像Dictionary&lt;Project, List&lt;Technicians&gt;&gt;,只是您不必在每次访问时重复所有逻辑来管理Lists。

【讨论】:

【解决方案4】:

我认为您的解决方案没有任何问题。毕竟,您可以按项目轻松访问所有团队成员。但是您也可以尝试List&lt;KeyValuePair&lt;Project, Technician&gt;&gt;。您保持键值关系,但不受不重复键的限制。它比你现在拥有的简单得多吗?取决于用例。

或者,您可以将此结构隐藏在自定义集合实现之后。

【讨论】:

  • 看来他们不想要“项目 1 |约翰”两次。也许是HashSet&lt;KeyValuePair&lt;Project, Technician&gt;&gt;
  • @ArturoTorresSánchez 不,我不想重复键值对。项目 1 不能有重复的技术人员。
  • @McAdam331, ... 我本来想说KeyValuePair 会覆盖Equals,但它似乎实际上并没有。我想Tuple 可能是更好的选择(仍然使用HashSet)。
【解决方案5】:

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

“滚动您自己的”版本的字典非常容易,它允许“重复键”条目。这是一个粗略的简单实现。您可能需要考虑在IDictionary&lt;T&gt; 上添加对基本上大多数(如果不是全部)的支持。

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));
}

【讨论】:

    猜你喜欢
    • 2017-02-16
    • 2010-10-07
    • 1970-01-01
    • 1970-01-01
    • 2018-10-09
    • 2015-04-12
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多