2013-08-06 9 views
9

Używam HashSet i Dictionary w języku C# do implementacji struktury Graph. Mam problem z wyjątkowością elementów hashset, gdy klucz hashset jest niestandardową klasą. Tutaj mam:C# - definiowanie hashset z niestandardowym kluczem

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; 
} 

Problemem jest to, że kiedy mam same wierzchołki i chcę, aby dodać je do wykresu, dostają duplikowane. jak mogę zdefiniować sposób, w jaki HashSet zrozumie wyjątkowość moich vertexów?

+0

Z definicji hasz będzie zawsze taki sam, jeśli ta sama wartość zostanie przekazana jako dane wejściowe. Jeśli masz dwa wierzchołki o dokładnie takiej samej wartości, będą one miały dokładnie taki sam skrót. Czy na pewno chcesz użyć HashSet? EDYCJA: W drugim czytaniu brzmi to tak, jak chcesz uniknąć powielania wierzchołków, które są takie same. Jeśli tak, jeśli mają te same punkty początkowe i końcowe, może istnieć inna zmienna, która jest inna. Czy próbowałeś użyć czegoś podobnego do Tuple zamówionych par reprezentujących punkty początkowe i końcowe Vertex? – IllusiveBrian

+1

@Namfuak To nie jest poprawne. Utwórz dwa obiekty Vertex o tej samej wartości punktowej i porównaj GetHashCode. – Paparazzi

Odpowiedz

23

Opcje:

  • Zastąp Equals i GetHashCode w Vertex (i prawdopodobnie Point dla uproszczenia), najprawdopodobniej zaimplementuj [IEquatable][1]<T> po uruchomieniu
  • Utwórz własną implementację IEqualityComparer<Vertex> i przekaż to konstruktor HashSet<Vertex>

Pierwsza opcja to prawdopodobnie najprostszy, ale chciałbym silnie zaleca się zrobić Point niezmienne pierwsze: zmienne typów (lub typy zawierające zmienne typów) nie naprawia hash klawiatura. pewnie bym zrobić to struct też:

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 
} 

... wtedy przesłonić GetHashCode i Equals i wdrożenie IEquatable<> w Vertex też, na przykład

// 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); 
    } 
}  
+5

Dla tych, którzy zastanawiają się, dlaczego mnoży 'x' i' y' przez stałe, ma to pomóc w większym rozpowszechnianiu kodu skrótu. To sprawia, że ​​punkt z 'X = 12' i' Y = 13' ma inny kod skrótu niż punkt z 'X = 13' i' Y = 12'. –

+2

Myślę, że brakowało słowa kluczowego 'override' na' public virtual bool Equals (object obj) ' – Romoku

+0

@Romoku: Tak, zrobione. –

3

Zastąpienie GetHashCode() i Equals() metod klasy Vertex.

Poniżej jest przykład, ale należy użyć nieco lepszy algorytm haszowania niż kopalnia :)

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

    public Point VertexLabel { get; private set; } 

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

    public override bool Equals(object obj) 
    { 
     //your logic for comparing Vertex 
    } 
} 
+1

+1 na przykład.Ten hashcode jest prawdopodobnie przykładem, który oferowałbym zbyt haha. –

4

Jak mówili inni, nadpisać GetHashCode() klasy Vertex.

Zastępuje również metodę .Equals. Słownik użyje zarówno GetHashCode, jak i Equals do określenia równości.

To dlatego Dictionary nie zastępuje wierzchołków. Wierzchołki o tych samych współrzędnych nadal są zasadniczo różne w przypadku modelu Dictionary.

Nie będę zanieczyszczał twojego pytania innym przykładem kodu źródłowego, ponieważ Jon i gzaxx zaoferowali już 2 bardzo dobre przykłady.