C#-使用自定义键定义哈希集

C#-使用自定义键定义哈希集,c#,.net,dictionary,hashset,C#,.net,Dictionary,Hashset,我正在使用C#中的HashSet和Dictionary实现一个图形结构。当HashSet键是自定义类时,我对HashSet元素的唯一性有一个问题。我有: public class Point { public int x { get; set; } public int y { get; set; } } public class Vertex { public Vertex(Point point) { VertexLabel = point;

我正在使用C#中的
HashSet
Dictionary
实现一个图形结构。当
HashSet
键是自定义类时,我对
HashSet
元素的唯一性有一个问题。我有:

public class Point
{
    public int x { get; set; }
    public int y { get; set; }
}

public class Vertex
{
    public Vertex(Point point)
    {
        VertexLabel = point;
    }

    public Point VertexLabel { get; private set; }
}

public class Edge
{
    public Edge(Vertex to, Vertex from, double weight)
    {
        FromVertex = from;
        ToVertex = to;
        Weight = weight;
    }

    public Vertex FromVertex { get; private set; }
    public Vertex ToVertex { get; private set; }
    public double Weight { get; private set; }
}

public class Graph
{
    public Graph()
    {
        _Vertexes = new HashSet<Vertex>();
        _VertexEdgeMapping = new Dictionary<Vertex, LinkedList<Edge>>();
    }
    private HashSet<Vertex> _Vertexes;
    private Dictionary<Vertex, LinkedList<Edge>> _VertexEdgeMapping;
}
公共类点
{
公共整数x{get;set;}
公共整数y{get;set;}
}
公共类顶点
{
公共顶点(点)
{
顶点标签=点;
}
公共点顶点标签{get;private set;}
}
公共阶级边缘
{
公共边(顶点到、顶点从、双重权重)
{
FromVertex=from;
ToVertex=to;
重量=重量;
}
公共顶点FromVertex{get;private set;}
公共顶点到顶点{get;私有集;}
公共双权重{get;私有集;}
}
公共类图
{
公共图()
{
_顶点=新的HashSet();
_VertexEdgeMapping=新字典();
}
私有哈希集_顶点;
私有字典_顶点映射;
}
问题是,当我有相同的顶点,并且我想将它们添加到图形中时,它们会被复制。我如何定义一种方法,使
HashSet
能够理解顶点的唯一性?

覆盖
GetHashCode()
Equals()
顶点类的方法

下面是一个示例,但您应该使用比我的哈希算法更好的算法:)

选项:

  • 覆盖
    顶点
    中的
    Equals
    GetHashCode
    (为了简单起见,可能是
    ),很有可能在执行时实现
  • 创建自己的
    IEqualityComparer
    实现,并将其传递给
    HashSet
第一个选项可能是最简单的,但我强烈建议您首先设置
不可变:可变类型(或包含可变类型的类型)不能生成好的散列键。我可能也会把它变成一个
struct

public struct Point : IEquatable<Point>
{
    private readonly int x, y;

    public int X { get { return x; } }
    public int Y { get { return y; } }

    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }

    public override int GetHashCode()
    {
        return 31 * x + 17 * y; // Or something like that
    }

    public override bool Equals(object obj)
    {
        return obj is Point && Equals((Point) obj);
    }

    public bool Equals(Point p)
    {
        return x == p.x && y == p.y;
    }

    // TODO: Consider overloading the == and != operators
}

正如其他人所说,重写
Vertex
类的
GetHashCode()

还重写
.Equals
方法。字典将同时使用
GetHashCode
Equals
来确定相等性

这就是为什么
Dictionary
没有替换顶点。就
字典而言,具有相同坐标的顶点仍然是根本不同的


我不会用另一个源代码示例来污染您的问题,因为Jon和gzaxx已经提供了两个非常好的示例。

根据定义,如果将相同的值作为输入传递,则哈希值总是相同的。如果有两个顶点具有完全相同的值,则它们将具有完全相同的哈希。您确定要使用哈希集吗?编辑:在二读时,听起来您希望避免重复相同的顶点。如果是这样,如果它们有相同的起点和终点,那么可能还有其他一些不同的变量。您是否尝试过使用类似于表示顶点起点和终点的有序对的元组之类的东西?@Namfuak这是不正确的。请创建两个具有相同点值的顶点对象,并比较GetHashCode.+1例如。hashcode可能是我也会提供的例子哈哈。对于那些想知道为什么他将
x
y
乘以常量的人来说,这有助于更多地分发hash代码。这使得具有
X=12
Y=13
的点的哈希代码与具有
X=13
Y=12
的点的哈希代码不同。我认为您错过了
公共虚拟bool Equals(object obj)
public struct Point : IEquatable<Point>
{
    private readonly int x, y;

    public int X { get { return x; } }
    public int Y { get { return y; } }

    public Point(int x, int y)
    {
        this.x = x;
        this.y = y;
    }

    public override int GetHashCode()
    {
        return 31 * x + 17 * y; // Or something like that
    }

    public override bool Equals(object obj)
    {
        return obj is Point && Equals((Point) obj);
    }

    public bool Equals(Point p)
    {
        return x == p.x && y == p.y;
    }

    // TODO: Consider overloading the == and != operators
}
// Note: sealed to avoid oddities around equality and inheritance
public sealed class Vertex : IEquatable<Vertex>
{
    public Vertex(Point point)
    {
        VertexLabel = point;
    }

    public Point VertexLabel { get; private set; }

    public override int GetHashCode()
    {
        return VertexLabel.GetHashCode();
    }

    public override bool Equals(object obj)
    { 
        return Equals(obj as Vertex);
    }

    public bool Equals(Vertex vertex)
    {
        return vertex != null && vertex.VertexLabel.Equals(VertexLabel);
    }
}