【问题标题】:HashSet of Guid vs Tuple<Guid, Guid> as Dictionary Key: Order of Guid Shouldn't matterGuid 的 HashSet 与 Tuple<Guid, Guid> 作为字典键:Guid 的顺序无关紧要
【发布时间】:2021-10-24 11:11:09
【问题描述】:

我需要字典,哪个键需要是两个 guid 的 guid 顺序无关紧要。我有下面提到的两个解决方案,我想知道哪个更好,如果将来我需要添加第三个 guid 以及密钥的一部分,我应该去哪个。性能确实很重要,但如果差异不大,那么应该没问题。

第一个解决方案

 var guids = guids
     .OrderBy(g => g.ToString("N")) // Ordering so key always come as sorted 
     .ToList();

dict[Tuple.Create(guids[0], guids[1]) = "RandomObj";

第二个解决方案

public class GuidsModel
    {
        public GuidsModel(Guid guid1, Guid guid2)
        {
            Guid1= guid1;
            Guid2= guid2;
        }

        public Guid Guid1 { get; private set; }

        public Guid Guid2 { get; private set; }
    }

 public class GuidsModelComparer : IEqualityComparer<GuidsModel>
    {
        public static GuidsModelComparer Default = new GuidsModelComparer();

        private static IEqualityComparer<HashSet<Guid>> _hashSetComparer = HashSet<Guid>.CreateSetComparer();

        public bool Equals(GuidsModel x, GuidsModel y)
        {
            return _hashSetComparer.Equals(new HashSet<Guid> { x.Guid1, x.Guid2}, new HashSet<Guid> { y.Guid1, y.Guid2});
        }

        public int GetHashCode(GuidsModel obj)
        {
            if (obj == null)
                return 0;

            return _hashSetComparer.GetHashCode(new HashSet<Guid> { obj.Guid1, obj.Guid2});
        }

// usage
var dict = new Dictionary<GuidsModel, object>(GuidsModelComparer.Default);
dict[new GuidsModel(guids[0], guids[1])] = "RandomObj";

【问题讨论】:

  • 字典是使用哈希表实现的,即在基于给定键检索对象时非常快。那是你的用例吗?如果是这样,请在列表上使用字典。
  • HashSet 更有意义。你没有价值,所以没有必要使用字典。创建具有两个 Guid 属性的自定义类型(或 Tuple / ValueTuple)。有一个构造函数来接受它们。第一个属性越小,第二个属性越大。
  • @41686d6564 Example
  • 另一种选择,是只使用通信散列并将其存储为密钥
  • @imsmn 不,我正在使用字典,但我应该使用 Tuple 作为键或 GuidModel 作为键的字典。 tuple 的问题是 Tuple 不等于 Tuple 因此,在添加字典之前,我总是必须对 guid 进行排序。

标签: c# .net dictionary


【解决方案1】:

这在很大程度上取决于上下文,但我想说,如果您想在多个地方访问字典,将 guid“排序”逻辑封装在一个地方会更有意义。我个人也只会在模型本身中实现EqualsGetHashcode,而不是创建自定义比较器。

public class GuidsModel
{
    public GuidsModel(Guid guid1, Guid guid2)
    {
        if (guid1.CompareTo(guid2) > 0) // when there will be more than 2 guids can switch to ordering collection i.e. new [] {guid1, guid2, guid3}.OrderBy()...
        {
            Guid1 = guid1;
            Guid2 = guid2;
        }
        else
        {
            Guid1 = guid2;
            Guid2 = guid1;
        }
    }

    public Guid Guid1 { get; private set; }
    public Guid Guid2 { get; private set; }

    private bool Equals(GuidsModel other)
    {
        return Guid1.Equals(other.Guid1) && Guid2.Equals(other.Guid2);
    }

    public override bool Equals(object obj)
    {
        if (ReferenceEquals(null, obj)) return false;
        if (ReferenceEquals(this, obj)) return true;
        if (obj.GetType() != this.GetType()) return false;
        return Equals((GuidsModel)obj);
    }

    public override int GetHashCode()
    {
        return HashCode.Combine(Guid1, Guid2);
    }
}

Fiddle

【讨论】:

  • 谢谢@Guru Stron 我会尝试上面的解决方案似乎比比较器简单得多
  • 我会稍微改变 Equal 如下 public override bool Equals(object obj) { if (ReferenceEquals(null, obj)) return false; if (ReferenceEquals(this, obj)) 返回真; if (!(obj is GuidModel source)) return false;返回等于((GuidsModel)obj); }
  • @MurtazaTahirAli return obj is GuidsModel model &amp;&amp; Equals(model); 可以更短=)
【解决方案2】:

据我了解,您需要一个HashSet&lt;Something&gt;,并且这个东西包含两个相同类型的值,如果顺序不同,HashSet 也应该匹配。

得到这个忽略顺序是相当复杂的。但是如果你使用工厂方法来创建实例并且这个工厂方法负责排序,那么它会变得更简单:

// Some class that holds two Guids, but it respects ordering
public record GuidPair(Guid First, Guid Second);

public static class Create
{
    public static GuidPair GuidPair(Guid first, Guid second)
    {
        // Order guids somehow in factory method to ensure
        // correct equality comparison
        if (first.CompareTo(second) > 0)
            return new GuidPair(second, first);
        else
            return new GuidPair(first, second);
    }
}

public static class Program
{
    public static void Main(string[] args)
    {
        // Some source for Guid values.
        var values = Enumerable.Range(0, 100)
            .Select(_ => Guid.NewGuid());

        // Create a HashSet with arbitrary values.
        var hashes = values.Zip(values)
            .Select(pair => Create.GuidPair(pair.First, pair.Second))
            .ToHashSet();

        // Pick some "random" element
        var someEntry = hashes.Skip(17).First();
        // Create explicitly a new item with values swapped (with factory method).
        var toCompare = Create.GuidPair(someEntry.Second, someEntry.First);

        // Check if default HashSet comparer works.
        var isAvailable = hashes.Contains(toCompare);

        // Prints "Found { first = Guid, second = Guid }: true"
        Console.WriteLine($"Found {toCompare}: {isAvailable}");

        Console.ReadKey();
    }
}

【讨论】:

  • 嘿@Oliver,你理解正确。您的解决方案也与 Guru Stron 提出的非常相似。
  • 你是对的。策略是一样的。唯一真正不同的是,我使用新的 record 类,而 Guru Stron 使用它自己的 Equals()GetHashCode() 实现。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-04-28
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多