【问题标题】:IBiLookup and Bilookup: key and value "dictionary" lookupIBiLookup 和 Bilookup:键和值“字典”查找
【发布时间】:2013-03-05 17:13:09
【问题描述】:

我需要一个更健壮的类字典结构来获得:

  • 通过提供键值(默认字典行为);
  • 通过提供一个值来创建键(不是那么简单)。

另外,我想扩展这个类字典结构的功能,以便一个键可以与多个值相关联。

通过this 讨论,this 回答和this other one 提供了实现该目标的工具。我决定从 BiDictionary(Jon Skeet 的回答)中删除“按索引访问”,因为歧义会比我想要的更频繁地发生(例如,将字符串映射到字符串时)。我想出的类似字典的“结构”是:

using System.Collections.Generic;

public interface IBiLookup<TLeft, TRight>
{
    IDictionary<TLeft, ICollection<TRight>> LeftToRight { get; }
    IDictionary<TRight, ICollection<TLeft>> RightToLeft { get; }

    bool TryGetByLeft(TLeft left, out ICollection<TRight> rights);
    bool TryGetByRight(TRight right, out ICollection<TLeft> lefts);

    void Add(TLeft left, TRight right);
}
public class BiLookup<TLeft, TRight> : IBiLookup<TLeft, TRight>
{
    public IDictionary<TLeft, ICollection<TRight>> LeftToRight
    {
        get { return this.leftToRight; }
    }
    public IDictionary<TRight, ICollection<TLeft>> RightToLeft
    {
        get { return this.rightToLeft; }
    }

    public bool TryGetByLeft(TLeft left, out ICollection<TRight> rights)
    {
        return LeftToRight.TryGetValue(left, out rights);
    }
    public bool TryGetByRight(TRight right, out ICollection<TLeft> lefts)
    {
        return RightToLeft.TryGetValue(right, out lefts);
    }

    public void Add(TLeft left, TRight right)
    {
        AddLeftToRight(left, right);
        AddRightToLeft(right, left);
    }

    private void AddLeftToRight(TLeft left, TRight right)
    {
        ICollection<TRight> rights;

        // 1) Is there an entry associated with the "left" value?
        // 2) If so, is the "right" value already associated?
        if (!TryGetByLeft(left, out rights))
        {
            // Then we have to add an entry in the leftToRight dictionary.
            rights = new List<TRight> { right };                
        }
        else
        {
            // So there are entries associated with the "left" value.
            // We must verify if the "right" value itself is not there.
            if (((List<TRight>)rights).FindIndex(element => element.Equals(right)) < 0)
            {
                // We don't have that association yet.
                rights.Add(right);
            }
            else
            {
                // The value is already in the list: do nothing.
                return;
            }
        }

        LeftToRight[left] = rights;
    }
    private void AddRightToLeft(TRight right, TLeft left)
    {
        ICollection<TLeft> lefts;

        // 1) Is there an entry associated with the "right" value?
        // 2) If so, is the "left" value already associated?
        if (!TryGetByRight(right, out lefts))
        {
            // Then we have to add an entry in the leftToRight dictionary.
            lefts = new List<TLeft> { left };
        }
        else
        {
            // So there are entries associated with the "right" value.
            // We must verify if the "right" value itself is not there.
            if (((List<TLeft>)lefts).FindIndex(element => element.Equals(left)) < 0)
            {
                // We don't have that association yet.
                lefts.Add(left);
            }
            else
            {
                // The value is already in the list: do nothing.
                return;
            }
        }

        RightToLeft[right] = lefts;
    }

    #region Fields

    private IDictionary<TLeft, ICollection<TRight>> leftToRight = new Dictionary<TLeft, ICollection<TRight>>();
    private IDictionary<TRight, ICollection<TLeft>> rightToLeft = new Dictionary<TRight, ICollection<TLeft>>();

    #endregion
}

我担心将 Add(...) 方法拆分为两个更具体的方法是否是一个好的实现,因为 BiLookup 类将被广泛使用,并且需要牢记性能。此外,此线程的目的是讨论接口和类实现是否尽可能好,或者可以改进它们。

如果我将接口和“默认”实现都保存到类库项目中,设计是否足以重复使用?

【问题讨论】:

  • 如果您想讨论性能,最好指定要求。看起来您并不关心添加性能,但需要 O(1) 进行两次查找。
  • 你是对的,但由于要求有些“松散”,我只想确保不要为简单(且过度使用)的 Add(...) 操作增加不必要的开销。

标签: c# .net dictionary


【解决方案1】:

如果不实际使用它,就无法判断“设计是否足够好,可以重复使用”。您需要尝试以几种不同的方式使用您的代码(所有单元测试都算作一个),看看它是否有意义并鼓励编写可读的代码。

如果您没有任何特定要求(性能或其他),而只是确保代码的单元测试覆盖率良好,所有测试均已通过,并且您的预期使用场景已被测试覆盖。

代码注释:强制转换(即((List&lt;TLeft&gt;)lefts)看起来是不必要的。 FindIndex 调用(使用 O(number_of_items) 进行线性搜索)在性能敏感代码中并不看好。

【讨论】:

  • 我没有任何自动化测试经验(也许是单元测试?),但就我的需求而言,是的,现在的代码看起来已经足够了。也许我在这里问的太多了,因为正如您已经说过的,只有通过使用才能确定代码是否好。我只是想确保整体设计没有太大的缺陷,并且也讨论一下,因为偶尔有人要求这种解决方案。关于不必要的演员表,您会建议什么作为迭代列表的另一种方法?
  • 演员:Any 将具有相同的性能。此外,当您尝试使用代码时,请查看您是否真的需要 ICollection&lt;TXXXX&gt; - 很有可能 IEnumerable&lt;TXXXX&gt; 就足够了(可以通过允许对两个索引使用 Dictionary 来简化实现)。
  • 谢谢,IEnumerable 在这里更有意义。使用扩展方法 Any (System.Linq) 可以部分解决该问题,因为在将元素添加到 IEnumerable 之前仍需要强制转换。
  • 好吧,刚刚发现在执行 Add 之前强制转换为 ICollection&lt;T&gt; 比限制可能使用这两个类的对象数量要好得多(在辩论 IEnumerable&lt;T&gt;ICollection&lt;T&gt; 时)。感谢阿列克谢的帮助,我现在会接受你的回答:)
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2022-06-15
  • 1970-01-01
  • 2022-11-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多