Bagikan melalui


Metode System.Object.GetHashCode

Artikel ini menyediakan keterangan tambahan untuk dokumentasi referensi untuk API ini.

Metode ini GetHashCode menyediakan kode hash untuk algoritma yang membutuhkan pemeriksaan cepat kesetaraan objek. Kode hash adalah nilai numerik yang digunakan untuk menyisipkan dan mengidentifikasi objek dalam koleksi berbasis hash, seperti Dictionary<TKey,TValue> kelas, Hashtable kelas, atau jenis yang berasal dari DictionaryBase kelas.

Catatan

Untuk informasi tentang bagaimana kode hash digunakan dalam tabel hash dan untuk beberapa algoritma kode hash tambahan, lihat entri Fungsi Hash di Wikipedia.

Dua objek yang sama mengembalikan kode hash yang sama. Namun, kebalikannya tidak benar: kode hash yang sama tidak menyiratkan kesetaraan objek, karena objek yang berbeda (tidak sama) dapat memiliki kode hash yang identik. Selain itu, .NET tidak menjamin implementasi GetHashCode default metode , dan nilai yang dikembalikan metode ini mungkin berbeda antara implementasi .NET, seperti versi yang berbeda dari .NET Framework dan .NET Core, dan platform, seperti platform 32-bit dan 64-bit. Untuk alasan ini, jangan gunakan implementasi default metode ini sebagai pengidentifikasi objek unik untuk tujuan hashing. Dua konsekuensi mengikuti dari ini:

  • Anda tidak boleh berasumsi bahwa kode hash yang sama menyiratkan kesetaraan objek.
  • Anda tidak boleh bertahan atau menggunakan kode hash di luar domain aplikasi tempat kode dibuat, karena objek yang sama dapat melakukan hash di seluruh domain, proses, dan platform aplikasi.

Peringatan

Kode hash ditujukan untuk penyisipan dan pencarian yang efisien dalam koleksi yang didasarkan pada tabel hash. Kode hash bukan nilai permanen. Untuk alasan ini:

  • Jangan membuat serialisasi nilai kode hash atau menyimpannya dalam database.
  • Jangan gunakan kode hash sebagai kunci untuk mengambil objek dari koleksi bertanda kunci.
  • Jangan mengirim kode hash di seluruh domain atau proses aplikasi. Dalam beberapa kasus, kode hash dapat dihitung berdasarkan domain per proses atau per aplikasi.
  • Jangan gunakan kode hash alih-alih nilai yang dikembalikan oleh fungsi hashing kriptografi jika Anda memerlukan hash yang kuat secara kriptografis. Untuk hash kriptografi, gunakan kelas yang berasal dari System.Security.Cryptography.HashAlgorithm kelas atau System.Security.Cryptography.KeyedHashAlgorithm .
  • Jangan menguji kesetaraan kode hash untuk menentukan apakah dua objek sama. (Objek yang tidak sama dapat memiliki kode hash yang identik.) Untuk menguji kesetaraan, panggil ReferenceEquals metode atau Equals .

Metode GetHashCode ini dapat ditimpa oleh jenis turunan. Jika GetHashCode tidak ditimpa, kode hash untuk jenis referensi dihitung dengan memanggil Object.GetHashCode metode kelas dasar, yang menghitung kode hash berdasarkan referensi objek; untuk informasi selengkapnya, lihat RuntimeHelpers.GetHashCode. Dengan kata lain, dua objek yang dikembalikan ReferenceEqualstrue metode memiliki kode hash yang identik. Jika jenis nilai tidak mengambil alih GetHashCode, ValueType.GetHashCode metode kelas dasar menggunakan refleksi untuk menghitung kode hash berdasarkan nilai bidang jenis. Dengan kata lain, jenis nilai yang bidangnya memiliki nilai yang sama memiliki kode hash yang sama. Untuk informasi selengkapnya tentang penimpaan GetHashCode, lihat bagian "Catatan untuk Pewaris".

Peringatan

Jika Anda mengambil GetHashCode alih metode , Anda juga harus mengambil alih Equals, dan sebaliknya. Jika metode penimpaan Equals Anda kembali true saat dua objek diuji untuk kesetaraan, metode yang ditimpa GetHashCode harus mengembalikan nilai yang sama untuk dua objek.

Jika objek yang digunakan sebagai kunci dalam tabel hash tidak memberikan implementasi yang berguna dari GetHashCode, Anda dapat menentukan penyedia kode hash dengan menyediakan IEqualityComparer implementasi ke salah satu kelebihan beban Hashtable konstruktor kelas.

Catatan untuk Windows Runtime

Ketika Anda memanggil GetHashCode metode pada kelas di Windows Runtime, metode ini menyediakan perilaku default untuk kelas yang tidak mengambil alih GetHashCode. Ini adalah bagian dari dukungan yang disediakan .NET untuk Windows Runtime (lihat Dukungan .NET untuk Aplikasi Windows Store dan Runtime Windows). Kelas di Windows Runtime tidak mewarisi Object, dan saat ini tidak menerapkan GetHashCode. Namun, mereka tampaknya memiliki ToStringmetode , Equals(Object), dan GetHashCode ketika Anda menggunakannya dalam kode C# atau Visual Basic Anda, dan .NET Framework menyediakan perilaku default untuk metode ini.

Catatan

Kelas Windows Runtime yang ditulis dalam C# atau Visual Basic dapat mengambil GetHashCode alih metode .

Contoh

Salah satu cara paling sederhana untuk menghitung kode hash untuk nilai numerik yang memiliki rentang yang sama atau lebih kecil daripada jenisnya adalah dengan hanya mengembalikan nilai tersebut Int32 . Contoh berikut menunjukkan implementasi seperti itu untuk Number struktur.

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 Example1
{
   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 Example1
    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

Seringkali, jenis memiliki beberapa bidang data yang dapat berpartisipasi dalam menghasilkan kode hash. Salah satu cara untuk menghasilkan kode hash adalah dengan menggabungkan bidang ini menggunakan operasi, seperti yang XOR (eXclusive OR) ditunjukkan dalam contoh berikut.

using System;

// A type that represents a 2-D point.
public struct Point2
{
    private int x;
    private int y;

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

    public override bool Equals(Object obj)
    {
       if (! (obj is Point2)) return false;

       Point2 p = (Point2) obj;
       return x == p.x & y == p.y;
    }

    public override int GetHashCode()
    {
        return x ^ y;
    }
}

public class Example3
{
   public static void Main()
   {
      Point2 pt = new Point2(5, 8);
      Console.WriteLine(pt.GetHashCode());

      pt = new Point2(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 Point3
    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 Point3 Then Return False

        Dim p As Point3 = CType(obj, Point3)
        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 Example3
    Public Sub Main()
        Dim pt As New Point3(5, 8)
        Console.WriteLine(pt.GetHashCode())

        pt = New Point3(8, 5)
        Console.WriteLine(pt.GetHashCode())
    End Sub
End Module

Contoh sebelumnya mengembalikan kode hash yang sama untuk (n1, n2) dan (n2, n1), sehingga dapat menghasilkan lebih banyak tabrakan daripada yang diinginkan. Sejumlah solusi tersedia sehingga kode hash dalam kasus ini tidak identik. Salah satunya adalah mengembalikan kode Tuple hash objek yang mencerminkan urutan setiap bidang. Contoh berikut menunjukkan kemungkinan implementasi yang menggunakan Tuple<T1,T2> kelas . Namun, perhatikan bahwa overhead performa dari membuat Tuple objek dapat secara signifikan berdampak pada performa keseluruhan aplikasi yang menyimpan sejumlah besar objek dalam tabel hash.

using System;

public struct Point3
{
    private int x;
    private int y;

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

    public override bool Equals(Object obj)
    {
        if (obj is Point3)
        {
            Point3 p = (Point3) 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()
   {
        Point3 pt = new Point3(5, 8);
        Console.WriteLine(pt.GetHashCode());

        pt = new Point3(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

Solusi alternatif kedua melibatkan pembobotan kode hash individu dengan menggeser kiri kode hash bidang berturut-turut oleh dua bit atau lebih. Secara optimal, bit yang bergeser di luar bit 31 harus dibungkus daripada dibuang. Karena bit dibuang oleh operator shift kiri di C# dan Visual Basic, ini memerlukan pembuatan metode shift-and-wrap kiri seperti berikut:

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

Contoh berikut kemudian menggunakan metode shift-and-wrap ini untuk menghitung kode hash struktur yang Point digunakan dalam contoh sebelumnya.

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 Example2
{
   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 Point5
    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 Point5 Then Return False

        Dim p As Point5 = CType(obj, Point5)
        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 Example2
    Public Sub Main()
        Dim pt As New Point5(5, 8)
        Console.WriteLine(pt.GetHashCode())

        pt = New Point5(8, 5)
        Console.WriteLine(pt.GetHashCode())
    End Sub
End Module
' The example displays the following output:
'       28
'       37