【问题标题】:Data structure for two way mapping [duplicate]双向映射的数据结构[重复]
【发布时间】:2023-04-09 11:32:01
【问题描述】:

现在,在你骂我之前,我知道有一个非常类似的问题:

Best data structure for two way mapping?

事实上,我正在寻找一种能够完成同样事情的数据结构。具体来说,我有一个字符串应该映射到另一个字符串,而另一个字符串也应该映射到原始字符串。

例如:

".jpg" -> "image/jpeg"
"image/jpeg" -> ".jpg"

链接的问题建议使用某种哈希图或Dictionary<string,string> 来完成此操作。

一个自定义数据类型将包含两个字典,每个字典是一种映射方式。这将提供 O(1),但我发现它根本无法扩展

考虑到我有一个字典,其中包含从 200 个 MIME 类型到相关文件扩展名的所有映射,我需要创建一个类似的字典,其内容相同但相反。这很容易出现拼写错误或缺少键,并且是大量重复代码。

虽然链接的问题是针对 Java 中的解决方案,但我正在寻找 C# 中的解决方案。

是否有支持对象之间这种双向映射的 .NET 数据结构?

如果没有,我如何在不复制代码的情况下完成此操作(如在两个字典解决方案中)?

【问题讨论】:

  • 理论上你想要dictionary.Add(".jpg", "image/jpg")之类的东西,并且能够将它用作var mime = dictionary[".jpg"]var ext = dictionary["image/jpg"]
  • 似乎毫无意义,因为您仍然会明确映射这些关系......
  • @BrettCaswell 我手动创建地图没有问题。但是,我不想创建两次地图
  • 有时简单更好;假设查找的内容是运行时静态的,使用代码生成获取源并创建正常/反向字典。
  • 我不知道你的代码会有多抽象,但这种模式对我来说是不可取的:我将提供一种 linq 方法。

标签: c# data-structures types mapping


【解决方案1】:

为什么不能使用带有两个字典的自定义类型?虽然它会使用双倍的内存,但它允许 O(1) 查找并且应该可以按照您的意愿工作。

但是,当涉及到泛型参数时,它可能会有点麻烦。如果您指定相同的类型,则没有问题,但是如果您指定不同的类型,则索引器会中断,因为您只能通过一种方式获取值。如果您重载索引器并有两个,即:

public K this[T value]
public T this[K value]

如果您有相同的参数,它会中断,因为它无法解决。在这种情况下,我建议有两个不同的类:

public class TwoWayDictionary<T>
{
    private Dictionary<T, T> _first;
    private Dictionary<T, T> _second;

    public TwoWayDictionary()
    {
        _first = new Dictionary<T, T>();
        _second = new Dictionary<T, T>();
    }

    public void Add(T first, T second)
    {
        _first.Add(first, second);
        _second.Add(second, first);
    }

    public T this[T value]
    {
        get
        {
            if(_first.ContainsKey(value))
            {
                return _first[value];
            }
            if(_second.ContainsKey(value))
            {
                return _second[value];
            }

            throw new ArgumentException(nameof(value));
        }
    }
}

public class TwoWayDictionary<T, K>
{
    private readonly Dictionary<T, K> _first;
    private readonly Dictionary<K, T> _second;

    public TwoWayDictionary()
    {
        _first = new Dictionary<T, K>();
        _second = new Dictionary<K, T>();
    }

    public void Add(T first, K second)
    {
        _first.Add(first, second);
        _second.Add(second, first);
    }

    public K this[T value]
    {
        get
        {
            if (_first.ContainsKey(value))
            {
                return _first[value];
            }

            throw new ArgumentException(nameof(value));
        }
    }

    public T this[K value]
    {
        get
        {
            if (_second.ContainsKey(value))
            {
                return _second[value];
            }

            throw new ArgumentException(nameof(value));
        }
    }
}    

这将允许您像 cmets 中提到的那样使用它:

var dict = new TwoWayDictionary<string>();
dict.Add(".jpg", "image/jpg");
var mime = dict[".jpg"];
var ext = dict["image/jpg"];

如果需要,还可以指定 2 种不同的类型:

var dict = new TwoWayDictionary<string, int>();
dict.Add(".jpg", 100);
var number = dict[".jpg"];
var ext = dict[100];

【讨论】:

  • 我赞成。我唯一不喜欢的是索引器的二元性。我宁愿公开 2 个单独的方法,例如 string GetFirst(string second)string GetSecond(string first)
  • @IvanStoev 公开两个独立的索引器几乎会完全违背课程的目的。如果这样做,您将不得不在类之外进行包含/空检查。在那种情况下,为什么还要有这个类 - 为什么不只拥有 2 个字典并检查每个字典中是否存在。
  • 我不这么认为。我看到的这个类的主要好处是封装。还假设您引入了像 TwoWayDictionary 这样的通用参数。那么你的索引器的签名是什么?顺便说一句,我赞成你的回答,所以我们站在同一边:-)
  • @IvanStoev 我同意你关于泛型类型和索引器的看法。这会让人毛骨悚然。
猜你喜欢
  • 2014-03-21
  • 2012-11-26
  • 2015-04-22
  • 2021-03-03
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-03-04
相关资源
最近更新 更多