从 HashSet 中获取原始值

Get original value from HashSet

更新: 从 .Net 4.7.2 开始,HashSet.TryGetValue - docs 可用。
HashSet.TryGetValue - SO post


我对 HashSet 有疑问,因为它没有提供任何类似于 Dictionary 已知的 TryGetValue 的方法。我需要这样的方法——传递元素以在集合中查找,并从其集合中设置返回元素(找到时)。

旁注 -- "why do you need element from the set, you already have that element?"。不,我不知道,平等和身份是两个不同的东西。

HashSet 不是密封的,但它的所有字段都是私有的,因此从中派生是没有意义的。我不能改用 Dictionary 因为我需要 SetEquals 方法。我正在考虑获取 HashSet 的源代码并添加所需的方法,但许可证并不是真正的开源(我可以看,但我不能 distribute/modify)。我可以使用反射,但 HashSet 中的数组不是 readonly,这意味着我不能在每个实例生命周期中绑定到这些字段一次。

而且我不想只为单个 class 使用完整的库。

到目前为止,我一直在使用 LINQ SingleOrDefault。所以问题是如何解决这个问题——让 HashSetTryGetValue?

我同意这是基本上缺失的东西。虽然它仅在极少数情况下有用,但我认为它们是非常罕见的情况 - 最值得注意的是密钥规范化。

暂时只能想到一个建议,实在是犯规。

您可以在创建 HashSet<T> 时指定您自己的 IEqualityComparer<T> - 因此创建一个记住它执行的最后一个正(即返回真值)Equals 比较的参数.然后您可以调用 Contains,并查看要求比较器比较的内容。

注意事项:

  • 这会不必要地保留引用,因此最终可能会阻止对象被垃圾回收
  • 您可能希望在每个线程的基础上执行此操作(如果您有一个集合在初始化后未 修改 ,但随后 通过多线程读取,例如)
  • 假设 HashSet<T> 不使用任何优化,例如 "if the references are equal, don't bother consulting the equality comparer"
  • 从根本上说这是一种可怕的滥用

我一直在尝试寻找交叉点方面的其他替代方案,但我还没有找到任何地方...

如评论中所述,值得尽可能封装它 - 我怀疑您只需要一组非常有限的操作,所以我将 HashSet<T> 包装在您自己的 class 并且只公开你真正需要的操作——这样你就可以在每次操作后清除 "cache",消除我上面的​​第一个反对意见。

对我来说这仍然是一种可怕的虐待,但是...

正如其他人所建议的,另一种方法是使用 Dictionary<TKey, TValue> 并自己实施 SetEquals。这很容易做到——同样,您希望将其封装在您自己的类型中。无论哪种方式,您应该首先设计类型本身,然后 然后 使用 HashSet<>Dictionary<,> 作为实现细节来实现它。

听起来你用错了工具。的确,您可以使用 HashSet 节省一些内存,但在我看来,您正在尝试实现一个不同的目标:获取刚好等于表示的实际元素。 所以实际上它们是两个不同的元素。只是 memento(唯一表示)是相等的。

因此,您最好使用字典,将元素添加为 Key and Value.所以你可以找回它(相同的)但是你错过了你的 SetEquals..

我想 SetEquals 在它的实现中与按桶顺序比较两个 HashSet 并在第一个不相等时失败没有什么不同。

因此,使用简单的 SequenceEqual() (LINQ) 比较两个 Keys 集合也同样有效。

所以这个扩展方法可以做到

public static SetEqual<T,G>(this IDictionary<T,G> d, IDictionary<T,G> e)
{
    return d.Keys.SequenceEqual(e.Keys);
}

这应该可行,因为 Dictionary 基本上是一个具有关联值的 HashSet。更适合你的问题。 (好吧,为了正确起见,代码应该使用 Dictionary<> 而不是 IDictionary<> 因为键顺序很重要)

如果第二个参数需要 IEnumerable<>,请尝试排序以获得定义的顺序(效率不高)。

也许你应该从 HashSet 切换到 SortedSet

对于SortedSet有一个简单的TryGetValue():

public bool TryGetValue(ref T element)
{
    var foundSet = sortedSet.GetViewBetween(element, element);
    if(foundSet.Count == 1)
    {
        element = foundSet.First();
        return true;
    }
    return false;       
}

调用时,该元素只需要设置比较器中使用的所有属性。它 returns 在集合中找到的元素。

希望不是瞎子,但我在任何地方都没有看到这个答案。如果你想要字典的TryGetValue,你可以偷它。

theHashset.ToDictionary(item => item.ID).TryGetValue(key, out value)

您只需要一个用于确定唯一键的快速 lambda。

最终在 .NET 4.7.2 中添加:

HashSet.TryGetValue(T, T) Method

An SO post with more details