Object.GetHashCode Metoda
Definicja
Ważne
Niektóre informacje odnoszą się do produktu w wersji wstępnej, który może zostać znacząco zmodyfikowany przed wydaniem. Firma Microsoft nie udziela żadnych gwarancji, jawnych lub domniemanych, w odniesieniu do informacji podanych w tym miejscu.
Służy jako domyślna funkcja skrótu.
public:
virtual int GetHashCode();
public virtual int GetHashCode ();
abstract member GetHashCode : unit -> int
override this.GetHashCode : unit -> int
Public Overridable Function GetHashCode () As Integer
Zwraca
Wartość skrótu dla bieżącego obiektu.
Przykłady
Jednym z najprostszych sposobów obliczania kodu skrótu dla wartości liczbowej, która ma ten sam lub mniejszy zakres niż Int32 typ, jest po prostu zwrócenie tej wartości. W poniższym przykładzie pokazano taką implementację Number
struktury.
using System;
public struct Number
{
private int n;
public Number(int value)
{
n = value;
}
public int Value
{
get { return n; }
}
public override bool Equals(Object obj)
{
if (obj == null || ! (obj is Number))
return false;
else
return n == ((Number) obj).n;
}
public override int GetHashCode()
{
return n;
}
public override string ToString()
{
return n.ToString();
}
}
public class Example
{
public static void Main()
{
Random rnd = new Random();
for (int ctr = 0; ctr <= 9; ctr++) {
int randomN = rnd.Next(Int32.MinValue, Int32.MaxValue);
Number n = new Number(randomN);
Console.WriteLine("n = {0,12}, hash code = {1,12}", n, n.GetHashCode());
}
}
}
// The example displays output like the following:
// n = -634398368, hash code = -634398368
// n = 2136747730, hash code = 2136747730
// n = -1973417279, hash code = -1973417279
// n = 1101478715, hash code = 1101478715
// n = 2078057429, hash code = 2078057429
// n = -334489950, hash code = -334489950
// n = -68958230, hash code = -68958230
// n = -379951485, hash code = -379951485
// n = -31553685, hash code = -31553685
// n = 2105429592, hash code = 2105429592
open System
[<Struct; CustomEquality; NoComparison>]
type Number(value: int) =
member _.Value = value
override _.Equals(obj) =
match obj with
| :? Number as n ->
n.Value = value
| _ -> false
override _.GetHashCode() =
value
override _.ToString() =
string value
let rnd = Random()
for _ = 0 to 9 do
let randomN = rnd.Next(Int32.MinValue, Int32.MaxValue)
let n = Number randomN
printfn $"n = {n,12}, hash code = {n.GetHashCode(),12}"
// The example displays output like the following:
// n = -634398368, hash code = -634398368
// n = 2136747730, hash code = 2136747730
// n = -1973417279, hash code = -1973417279
// n = 1101478715, hash code = 1101478715
// n = 2078057429, hash code = 2078057429
// n = -334489950, hash code = -334489950
// n = -68958230, hash code = -68958230
// n = -379951485, hash code = -379951485
// n = -31553685, hash code = -31553685
// n = 2105429592, hash code = 2105429592
Public Structure Number
Private n As Integer
Public Sub New(value As Integer)
n = value
End Sub
Public ReadOnly Property Value As Integer
Get
Return n
End Get
End Property
Public Overrides Function Equals(obj As Object) As Boolean
If obj Is Nothing OrElse Not TypeOf obj Is Number Then
Return False
Else
Return n = CType(obj, Number).n
End If
End Function
Public Overrides Function GetHashCode() As Integer
Return n
End Function
Public Overrides Function ToString() As String
Return n.ToString()
End Function
End Structure
Module Example
Public Sub Main()
Dim rnd As New Random()
For ctr As Integer = 0 To 9
Dim randomN As Integer = rnd.Next(Int32.MinValue, Int32.MaxValue)
Dim n As New Number(randomN)
Console.WriteLine("n = {0,12}, hash code = {1,12}", n, n.GetHashCode())
Next
End Sub
End Module
' The example displays output like the following:
' n = -634398368, hash code = -634398368
' n = 2136747730, hash code = 2136747730
' n = -1973417279, hash code = -1973417279
' n = 1101478715, hash code = 1101478715
' n = 2078057429, hash code = 2078057429
' n = -334489950, hash code = -334489950
' n = -68958230, hash code = -68958230
' n = -379951485, hash code = -379951485
' n = -31553685, hash code = -31553685
' n = 2105429592, hash code = 2105429592
Często typ ma wiele pól danych, które mogą uczestniczyć w generowaniu kodu skrótu. Jednym ze sposobów wygenerowania kodu skrótu jest połączenie tych pól przy użyciu XOR (eXclusive OR)
operacji, jak pokazano w poniższym przykładzie.
using System;
// A type that represents a 2-D point.
public struct Point
{
private int x;
private int y;
public Point(int x, int y)
{
this.x = x;
this.y = y;
}
public override bool Equals(Object obj)
{
if (! (obj is Point)) return false;
Point p = (Point) obj;
return x == p.x & y == p.y;
}
public override int GetHashCode()
{
return x ^ y;
}
}
public class Example
{
public static void Main()
{
Point pt = new Point(5, 8);
Console.WriteLine(pt.GetHashCode());
pt = new Point(8, 5);
Console.WriteLine(pt.GetHashCode());
}
}
// The example displays the following output:
// 13
// 13
// A type that represents a 2-D point.
[<Struct; CustomEquality; NoComparison>]
type Point(x: int, y: int) =
member _.X = x
member _.Y = y
override _.Equals(obj) =
match obj with
| :? Point as p ->
x = p.X && y = p.Y
| _ ->
false
override _.GetHashCode() =
x ^^^ y
let pt = Point(5, 8)
printfn $"{pt.GetHashCode()}"
let pt2 = Point(8, 5)
printfn $"{pt.GetHashCode()}"
// The example displays the following output:
// 13
// 13
' A type that represents a 2-D point.
Public Structure Point
Private x As Integer
Private y As Integer
Public Sub New(x As Integer, y As Integer)
Me.x = x
Me.y = y
End Sub
Public Overrides Function Equals(obj As Object) As Boolean
If Not TypeOf obj Is Point Then Return False
Dim p As Point = CType(obj, Point)
Return x = p.x And y = p.y
End Function
Public Overrides Function GetHashCode() As Integer
Return x Xor y
End Function
End Structure
Public Module Example
Public Sub Main()
Dim pt As New Point(5, 8)
Console.WriteLine(pt.GetHashCode())
pt = New Point(8, 5)
Console.WriteLine(pt.GetHashCode())
End Sub
End Module
Poprzedni przykład zwraca ten sam kod skrótu dla (n1, n2) i (n2, n1), a więc może wygenerować więcej kolizji niż są pożądane. Dostępnych jest wiele rozwiązań, dzięki czemu kody skrótów w tych przypadkach nie są identyczne. Jednym z nich jest zwrócenie kodu skrótu Tuple
obiektu, który odzwierciedla kolejność każdego pola. W poniższym przykładzie przedstawiono możliwą implementację Tuple<T1,T2> , która używa klasy . Należy jednak pamiętać, że obciążenie związane z wydajnością tworzenia wystąpienia Tuple
obiektu może znacząco wpłynąć na ogólną wydajność aplikacji, która przechowuje dużą liczbę obiektów w tabelach skrótów.
using System;
public struct Point
{
private int x;
private int y;
public Point(int x, int y)
{
this.x = x;
this.y = y;
}
public override bool Equals(Object obj)
{
if (obj is Point)
{
Point p = (Point) obj;
return x == p.x & y == p.y;
}
else
{
return false;
}
}
public override int GetHashCode()
{
return Tuple.Create(x, y).GetHashCode();
}
}
public class Example
{
public static void Main()
{
Point pt = new Point(5, 8);
Console.WriteLine(pt.GetHashCode());
pt = new Point(8, 5);
Console.WriteLine(pt.GetHashCode());
}
}
// The example displays the following output:
// 173
// 269
[<Struct; CustomEquality; NoComparison>]
type Point(x: int, y: int) =
member _.X = x
member _.Y = y
override _.Equals(obj) =
match obj with
| :? Point as p ->
x = p.X && y = p.Y
| _ ->
false
override _.GetHashCode() =
(x, y).GetHashCode()
let pt = Point(5, 8)
printfn $"{pt.GetHashCode()}"
let pt2 = Point(8, 5)
printfn $"{pt2.GetHashCode()}"
// The example displays the following output:
// 173
// 269
Public Structure Point
Private x As Integer
Private y As Integer
Public Sub New(x As Integer, y As Integer)
Me.x = x
Me.y = y
End Sub
Public Overrides Function Equals(obj As Object) As Boolean
If Not TypeOf obj Is Point Then Return False
Dim p As Point = CType(obj, Point)
Return x = p.x And y = p.y
End Function
Public Overrides Function GetHashCode() As Integer
Return Tuple.Create(x, y).GetHashCode()
End Function
End Structure
Public Module Example
Public Sub Main()
Dim pt As New Point(5, 8)
Console.WriteLine(pt.GetHashCode())
pt = New Point(8, 5)
Console.WriteLine(pt.GetHashCode())
End Sub
End Module
' The example displays the following output:
' 173
' 269
Drugie rozwiązanie alternatywne polega na ważoniu poszczególnych kodów skrótów przez przesunięcie lewego skrótu kodów kolejnych pól o dwa lub więcej bitów. Optymalnie bity przesunięte poza bit 31 powinny owinąć, a nie zostać odrzucone. Ponieważ bity są odrzucane przez operatory przesunięcia po lewej stronie w języku C# i Visual Basic, wymaga to utworzenia metody shift-and-wrap w lewo, jak pokazano poniżej:
public int ShiftAndWrap(int value, int positions)
{
positions = positions & 0x1F;
// Save the existing bit pattern, but interpret it as an unsigned integer.
uint number = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
// Preserve the bits to be discarded.
uint wrapped = number >> (32 - positions);
// Shift and wrap the discarded bits.
return BitConverter.ToInt32(BitConverter.GetBytes((number << positions) | wrapped), 0);
}
let shiftAndWrap (value: int) positions =
let positions = positions &&& 0x1F
// Save the existing bit pattern, but interpret it as an unsigned integer.
let number = BitConverter.ToUInt32(BitConverter.GetBytes value, 0)
// Preserve the bits to be discarded.
let wrapped = number >>> (32 - positions)
// Shift and wrap the discarded bits.
BitConverter.ToInt32(BitConverter.GetBytes((number <<< positions) ||| wrapped), 0)
Public Function ShiftAndWrap(value As Integer, positions As Integer) As Integer
positions = positions And &h1F
' Save the existing bit pattern, but interpret it as an unsigned integer.
Dim number As UInteger = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0)
' Preserve the bits to be discarded.
Dim wrapped AS UInteger = number >> (32 - positions)
' Shift and wrap the discarded bits.
Return BitConverter.ToInt32(BitConverter.GetBytes((number << positions) Or wrapped), 0)
End Function
W poniższym przykładzie użyto tej metody shift-and-wrap do obliczenia kodu skrótu Point
struktury użytej w poprzednich przykładach.
using System;
public struct Point
{
private int x;
private int y;
public Point(int x, int y)
{
this.x = x;
this.y = y;
}
public override bool Equals(Object obj)
{
if (!(obj is Point)) return false;
Point p = (Point) obj;
return x == p.x & y == p.y;
}
public override int GetHashCode()
{
return ShiftAndWrap(x.GetHashCode(), 2) ^ y.GetHashCode();
}
private int ShiftAndWrap(int value, int positions)
{
positions = positions & 0x1F;
// Save the existing bit pattern, but interpret it as an unsigned integer.
uint number = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0);
// Preserve the bits to be discarded.
uint wrapped = number >> (32 - positions);
// Shift and wrap the discarded bits.
return BitConverter.ToInt32(BitConverter.GetBytes((number << positions) | wrapped), 0);
}
}
public class Example
{
public static void Main()
{
Point pt = new Point(5, 8);
Console.WriteLine(pt.GetHashCode());
pt = new Point(8, 5);
Console.WriteLine(pt.GetHashCode());
}
}
// The example displays the following output:
// 28
// 37
open System
[<Struct; CustomEquality; NoComparison>]
type Point(x: int, y: int) =
member _.X = x
member _.Y = y
override _.Equals(obj) =
match obj with
| :? Point as p ->
x = p.X && y = p.Y
| _ ->
false
override this.GetHashCode() =
this.ShiftAndWrap(x.GetHashCode(), 2) ^^^ y.GetHashCode()
member _.ShiftAndWrap(value, positions) =
let positions = positions &&& 0x1F
// Save the existing bit pattern, but interpret it as an unsigned integer.
let number = BitConverter.ToUInt32(BitConverter.GetBytes value, 0)
// Preserve the bits to be discarded.
let wrapped = number >>> (32 - positions)
// Shift and wrap the discarded bits.
BitConverter.ToInt32(BitConverter.GetBytes((number <<< positions) ||| wrapped), 0)
let pt = Point(5, 8)
printfn $"{pt.GetHashCode()}"
let pt2 = Point(8, 5)
printfn $"{pt2.GetHashCode()}"
// The example displays the following output:
// 28
// 37
Public Structure Point
Private x As Integer
Private y As Integer
Public Sub New(x As Integer, y As Integer)
Me.x = x
Me.y = y
End Sub
Public Overrides Function Equals(obj As Object) As Boolean
If Not TypeOf obj Is Point Then Return False
Dim p As Point = CType(obj, Point)
Return x = p.x And y = p.y
End Function
Public Overrides Function GetHashCode() As Integer
Return ShiftAndWrap(x.GetHashCode(), 2) XOr y.GetHashCode()
End Function
Private Function ShiftAndWrap(value As Integer, positions As Integer) As Integer
positions = positions And &h1F
' Save the existing bit pattern, but interpret it as an unsigned integer.
Dim number As UInteger = BitConverter.ToUInt32(BitConverter.GetBytes(value), 0)
' Preserve the bits to be discarded.
Dim wrapped AS UInteger = number >> (32 - positions)
' Shift and wrap the discarded bits.
Return BitConverter.ToInt32(BitConverter.GetBytes((number << positions) Or wrapped), 0)
End Function
End Structure
Module Example
Public Sub Main()
Dim pt As New Point(5, 8)
Console.WriteLine(pt.GetHashCode())
pt = New Point(8, 5)
Console.WriteLine(pt.GetHashCode())
End Sub
End Module
' The example displays the following output:
' 28
' 37
Uwagi
Kod skrótu jest wartością liczbową używaną do wstawiania i identyfikowania obiektu w kolekcji opartej na skrótach, takiej jak Dictionary<TKey,TValue> klasa, Hashtable klasa lub typ pochodzący z DictionaryBase klasy. Metoda GetHashCode udostępnia ten kod skrótu dla algorytmów, które wymagają szybkiego sprawdzania równości obiektów.
Uwaga
Aby uzyskać informacje na temat sposobu użycia kodów skrótów w tabelach skrótów i dodatkowych algorytmów kodu skrótu, zobacz wpis Funkcja skrótu w wikipedii.
Dwa obiekty, które są równe kody skrótów zwracanych, które są równe. Odwrotnie nie ma jednak wartości true: równe kody skrótów nie oznaczają równości obiektów, ponieważ różne (nierówne) obiekty mogą mieć identyczne kody skrótów. Ponadto platforma .NET nie gwarantuje domyślnej implementacji metody, a wartość zwracana przez GetHashCode tę metodę może się różnić między implementacjami platformy .NET, takimi jak różne wersje .NET Framework i .NET Core, oraz platformy, takie jak platformy 32-bitowe i 64-bitowe. Z tych powodów nie należy używać domyślnej implementacji tej metody jako unikatowego identyfikatora obiektu do celów tworzenia skrótów. Z tego wynikają dwie konsekwencje:
Nie należy zakładać, że równe kody skrótów oznaczają równość obiektów.
Nigdy nie należy utrwalać ani używać kodu skrótu poza domeną aplikacji, w której został utworzony, ponieważ ten sam obiekt może zawierać skrót między domenami aplikacji, procesami i platformami.
Ostrzeżenie
Kod skrótu jest przeznaczony do wydajnego wstawiania i wyszukiwania w kolekcjach opartych na tabeli skrótów. Kod skrótu nie jest wartością stałą. Z tego powodu:
- Nie serializuj wartości kodu skrótu ani nie przechowuj ich w bazach danych.
- Nie należy używać kodu skrótu jako klucza do pobierania obiektu z kolekcji kluczy.
- Nie wysyłaj kodów skrótów między domenami aplikacji ani procesami. W niektórych przypadkach kody skrótów mogą być obliczane dla poszczególnych procesów lub dla domeny aplikacji.
- Nie używaj kodu skrótu zamiast wartości zwracanej przez funkcję skrótu kryptograficznego, jeśli potrzebujesz kryptograficznie silnego skrótu. W przypadku skrótów kryptograficznych użyj klasy pochodnej System.Security.Cryptography.HashAlgorithm z klasy lub System.Security.Cryptography.KeyedHashAlgorithm .
- Nie testuj równości kodów skrótów, aby określić, czy dwa obiekty są równe. (Nierówne obiekty mogą mieć identyczne kody skrótów). Aby przetestować równość, wywołaj metodę ReferenceEquals or Equals .
Metodę GetHashCode można zastąpić typem pochodnym. Jeśli GetHashCode nie jest zastępowany, kody skrótów dla typów referencyjnych są obliczane przez wywołanie Object.GetHashCode metody klasy bazowej, która oblicza kod skrótu na podstawie odwołania do obiektu. Aby uzyskać więcej informacji, zobacz RuntimeHelpers.GetHashCode. Innymi słowy, dwa obiekty, dla których ReferenceEquals metoda zwraca true
, mają identyczne kody skrótów. Jeśli typy wartości nie przesłaniają GetHashCodewartości , ValueType.GetHashCode metoda klasy bazowej używa odbicia do obliczenia kodu skrótu na podstawie wartości pól typu. Innymi słowy, typy wartości, których pola mają równe wartości, mają równe kody skrótów. Aby uzyskać więcej informacji na temat zastępowania GetHashCode, zobacz sekcję "Uwagi do dziedziczy".
Ostrzeżenie
Jeśli zastąpisz metodę GetHashCode , należy również zastąpić Equalsmetodę , i na odwrót. Jeśli metoda przesłonięć Equals zwraca true
wartość, gdy dwa obiekty są testowane pod kątem równości, metoda zastępowana GetHashCode musi zwrócić tę samą wartość dla dwóch obiektów.
Jeśli obiekt, który jest używany jako klucz w tabeli skrótów, nie zapewnia użytecznej implementacji GetHashCode, można określić dostawcę kodu skrótu, podając implementację IEqualityComparer do jednego z przeciążeń konstruktora Hashtable klasy.
Uwagi dotyczące środowisko wykonawcze systemu Windows
Wywołanie GetHashCode metody w klasie w środowisko wykonawcze systemu Windows zapewnia domyślne zachowanie klas, które nie zastępują GetHashCodeklasy . Jest to część wsparcia zapewnianego przez .NET Framework dla środowisko wykonawcze systemu Windows (zobacz .NET Framework pomoc techniczna dla aplikacji ze sklepu Windows i środowisko wykonawcze systemu Windows). Klasy w środowisko wykonawcze systemu Windows nie dziedziczą Object, a obecnie nie implementują klasy GetHashCode. Jednak wydają się ToStringmieć metody , Equals(Object)i GetHashCode podczas ich używania w kodzie C# lub Visual Basic, a .NET Framework zapewnia domyślne zachowanie dla tych metod.
Uwaga
środowisko wykonawcze systemu Windows klasy napisane w języku C# lub Visual Basic mogą zastąpić metodę GetHashCode .
Uwagi dotyczące dziedziczenia
Funkcja skrótu służy do szybkiego generowania liczby (kodu skrótu), która odpowiada wartości obiektu. Funkcje skrótu są zwykle specyficzne dla każdego typu i w celu unikatowości muszą używać co najmniej jednego z pól wystąpienia jako danych wejściowych. Kody skrótów nie powinny być obliczane przy użyciu wartości pól statycznych.
W przypadku klas pochodnych z ObjectGetHashCode
metody metoda może delegować do implementacji klasy GetHashCode() bazowej tylko wtedy, gdy klasa pochodna definiuje równość jako równość odwołania. Domyślna implementacja dla GetHashCode() typów odwołań zwraca kod skrótu, który jest odpowiednikiem tego, który jest zwracany przez metodę GetHashCode(Object) . Można zastąpić GetHashCode() niezmienialne typy odwołań. Ogólnie rzecz biorąc, w przypadku modyfikowalnych typów odwołań należy zastąpić GetHashCode() tylko wtedy, gdy:
— Można obliczyć kod skrótu z pól, które nie są modyfikowalne; Lub
— Można upewnić się, że kod skrótu obiektu modyfikowalnego nie zmienia się, gdy obiekt znajduje się w kolekcji, która opiera się na jego kodzie skrótu.
W przeciwnym razie można pomyśleć, że obiekt modyfikowalny zostanie utracony w tabeli skrótów. Jeśli zdecydujesz się zastąpić GetHashCode() dla modyfikowalnego typu odwołania, dokumentacja powinna wyjaśnić, że użytkownicy typu nie powinni modyfikować wartości obiektów, gdy obiekt jest przechowywany w tabeli skrótów.
W przypadku typów GetHashCode() wartości udostępnia domyślną implementację kodu skrótu, która używa odbicia. Należy rozważyć zastąpienie go w celu uzyskania lepszej wydajności.
Aby uzyskać więcej informacji i przykłady dotyczące kodów skrótów obliczeniowych na różne sposoby, zobacz sekcję Przykłady.
Funkcja skrótu musi mieć następujące właściwości:
Jeśli dwa obiekty są porównywane jako równe, GetHashCode() metoda dla każdego obiektu musi zwrócić tę samą wartość. Jeśli jednak dwa obiekty nie są porównywane jako równe, GetHashCode() metody dla tych dwóch obiektów nie muszą zwracać różnych wartości.
Metoda GetHashCode() obiektu musi stale zwracać ten sam kod skrótu, o ile nie ma żadnych modyfikacji stanu obiektu, który określa wartość zwracaną metody System.Object.Equals obiektu. Należy pamiętać, że dotyczy to tylko bieżącego wykonywania aplikacji i że w przypadku ponownego uruchomienia aplikacji może zostać zwrócony inny kod skrótu.
— Aby uzyskać najlepszą wydajność, funkcja skrótu powinna wygenerować równomierną dystrybucję dla wszystkich danych wejściowych, w tym danych wejściowych, które są silnie klastrowane. Implikacja polega na tym, że niewielkie modyfikacje stanu obiektu powinny spowodować duże modyfikacje wynikowego kodu skrótu w celu uzyskania najlepszej wydajności tabeli skrótów.
Funkcje skrótu powinny być niedrogie do obliczeń.
Metoda GetHashCode() nie powinna zgłaszać wyjątków.
Na przykład implementacja metody dostarczonej GetHashCode() String przez klasę zwraca identyczne kody skrótów dla identycznych wartości ciągów. W związku z tym dwa String obiekty zwracają ten sam kod skrótu, jeśli reprezentują tę samą wartość ciągu. Ponadto metoda używa wszystkich znaków w ciągu do generowania względnie losowo rozproszonych danych wyjściowych, nawet jeśli dane wejściowe są klastrowane w określonych zakresach (na przykład wielu użytkowników może mieć ciągi zawierające tylko mniejsze 128 znaków ASCII, mimo że ciąg może zawierać dowolny z 65 535 znaków Unicode).
Zapewnienie dobrej funkcji skrótu w klasie może znacząco wpłynąć na wydajność dodawania tych obiektów do tabeli skrótów. W tabeli skrótów z kluczami zapewniającymi dobrą implementację funkcji skrótu wyszukiwanie elementu wymaga stałego czasu (na przykład operacji O(1). W tabeli skrótów ze słabą implementacją funkcji skrótu wydajność wyszukiwania zależy od liczby elementów w tabeli skrótów (na przykład operacji O(n
), gdzie n
jest to liczba elementów w tabeli skrótów. Złośliwy użytkownik może wprowadzać dane wejściowe, które zwiększają liczbę kolizji, co może znacznie obniżyć wydajność aplikacji, które zależą od tabel skrótów, w następujących warunkach:
Gdy funkcje skrótu generują częste kolizje.
Gdy duża część obiektów w tabeli skrótów generuje kody skrótów, które są równe lub w przybliżeniu równe sobie.
— Podczas wprowadzania danych wejściowych przez użytkowników, z których jest obliczany kod skrótu.
Klasy pochodne, które GetHashCode() zastępują, muszą również przesłonić Equals(Object) , aby zagwarantować, Hashtable że dwa obiekty uważane za równe mają ten sam kod skrótu. W przeciwnym razie typ może nie działać poprawnie.