Masuk ke area abu-abu "on / off topic", tetapi perlu untuk menghilangkan kebingungan terkait saran Oscar Reyes bahwa lebih banyak tabrakan hash adalah hal yang baik karena mengurangi jumlah elemen di HashMap. Saya mungkin salah paham tentang apa yang dikatakan Oscar, tetapi sepertinya saya bukan satu-satunya: kdgregory, delfuego, Nash0, dan saya semua tampaknya memiliki pemahaman (mis) yang sama.
Jika saya mengerti apa yang dikatakan Oscar tentang kelas yang sama dengan kode hash yang sama, dia mengusulkan bahwa hanya satu contoh kelas dengan kode hash yang diberikan akan dimasukkan ke dalam HashMap. Misalnya, jika saya memiliki instance SomeClass dengan hashcode 1 dan instance kedua SomeClass dengan hashcode 1, hanya satu instance SomeClass yang dimasukkan.
Contoh Java pastebin di http://pastebin.com/f20af40b9 tampaknya menunjukkan dengan benar merangkum apa yang Oscar usulkan di atas.
Terlepas dari pemahaman atau kesalahpahaman, apa yang terjadi adalah contoh yang berbeda dari kelas yang sama tidak dimasukkan hanya sekali ke dalam HashMap jika mereka memiliki kode hash yang sama - tidak sampai ditentukan apakah kuncinya sama atau tidak. Kontrak kode hash mengharuskan objek yang sama memiliki kode hash yang sama; Namun, itu tidak mengharuskan objek yang tidak sama memiliki kode hash yang berbeda (meskipun ini mungkin diinginkan karena alasan lain) [1].
Contoh pastebin.com/f20af40b9 (yang dirujuk Oscar setidaknya dua kali) mengikuti, tetapi sedikit dimodifikasi untuk menggunakan pernyataan JUnit daripada printlines. Contoh ini digunakan untuk mendukung proposal bahwa kode hash yang sama menyebabkan benturan dan jika kelasnya sama, hanya satu entri yang dibuat (misalnya, hanya satu String dalam kasus khusus ini):
@Test
public void shouldOverwriteWhenEqualAndHashcodeSame() {
String s = new String("ese");
String ese = new String("ese");
// same hash right?
assertEquals(s.hashCode(), ese.hashCode());
// same class
assertEquals(s.getClass(), ese.getClass());
// AND equal
assertTrue(s.equals(ese));
Map map = new HashMap();
map.put(s, 1);
map.put(ese, 2);
SomeClass some = new SomeClass();
// still same hash right?
assertEquals(s.hashCode(), ese.hashCode());
assertEquals(s.hashCode(), some.hashCode());
map.put(some, 3);
// what would we get?
assertEquals(2, map.size());
assertEquals(2, map.get("ese"));
assertEquals(3, map.get(some));
assertTrue(s.equals(ese) && s.equals("ese"));
}
class SomeClass {
public int hashCode() {
return 100727;
}
}
Namun, kode hash bukanlah cerita lengkapnya. Apa yang pastebin contoh mengabaikan adalah kenyataan bahwa kedua s
dan ese
sama: mereka berdua string "ese". Jadi, memasukkan atau mendapatkan konten peta menggunakan s
atau ese
atau "ese"
sebagai kunci semuanya setara karenas.equals(ese) && s.equals("ese")
.
Tes kedua menunjukkan bahwa adalah salah untuk menyimpulkan bahwa kode hash yang identik pada kelas yang sama adalah alasan kunci -> nilai s -> 1
ditimpa ese -> 2
ketika map.put(ese, 2)
dipanggil dalam tes satu. Dalam pengujian kedua, s
dan ese
masih memiliki kode hash yang sama (sebagaimana diverifikasi oleh assertEquals(s.hashCode(), ese.hashCode());
) DAN mereka adalah kelas yang sama. Namun, s
dan ese
merupakan MyString
instance dalam pengujian ini, bukan String
instance Java - dengan satu-satunya perbedaan yang relevan untuk pengujian ini adalah sama: String s equals String ese
dalam pengujian satu di atas, sedangkan MyStrings s does not equal MyString ese
dalam pengujian dua:
@Test
public void shouldInsertWhenNotEqualAndHashcodeSame() {
MyString s = new MyString("ese");
MyString ese = new MyString("ese");
// same hash right?
assertEquals(s.hashCode(), ese.hashCode());
// same class
assertEquals(s.getClass(), ese.getClass());
// BUT not equal
assertFalse(s.equals(ese));
Map map = new HashMap();
map.put(s, 1);
map.put(ese, 2);
SomeClass some = new SomeClass();
// still same hash right?
assertEquals(s.hashCode(), ese.hashCode());
assertEquals(s.hashCode(), some.hashCode());
map.put(some, 3);
// what would we get?
assertEquals(3, map.size());
assertEquals(1, map.get(s));
assertEquals(2, map.get(ese));
assertEquals(3, map.get(some));
}
/**
* NOTE: equals is not overridden so the default implementation is used
* which means objects are only equal if they're the same instance, whereas
* the actual Java String class compares the value of its contents.
*/
class MyString {
String i;
MyString(String i) {
this.i = i;
}
@Override
public int hashCode() {
return 100727;
}
}
Berdasarkan komentar selanjutnya, Oscar tampaknya membalikkan apa yang dia katakan sebelumnya dan mengakui pentingnya persamaan. Namun, tampaknya gagasan bahwa yang sama adalah yang penting, bukan "kelas yang sama", tidak jelas (penekanan dari saya):
Daftar dibuat hanya jika hashnya sama, tetapi kuncinya berbeda. Misalnya jika String memberikan kode hash 2345 dan dan Integer memberikan kode hash yang sama 2345, maka integer tersebut dimasukkan ke dalam daftar karena String. sama (Integer) salah. Tetapi jika Anda memiliki kelas yang sama (atau setidaknya .equals mengembalikan nilai benar) maka entri yang sama digunakan. Misalnya String baru ("satu") dan `String baru (" satu ") digunakan sebagai kunci, akan menggunakan entri yang sama. Sebenarnya ini adalah poin SELURUH HashMap di tempat pertama! Lihat sendiri: pastebin.com/f20af40b9 - Oscar Reyes "
versus komentar sebelumnya yang secara eksplisit membahas pentingnya kelas identik dan kode hash yang sama, tanpa menyebutkan sama dengan:
"@delfuego: Lihat sendiri: pastebin.com/f20af40b9 Jadi, dalam pertanyaan ini kelas yang sama sedang digunakan (tunggu sebentar, kelas yang sama sedang digunakan kan?) Yang menyiratkan bahwa ketika hash yang sama digunakan, entri yang sama digunakan dan tidak ada "daftar" entri. - Oscar Reyes "
atau
"Sebenarnya ini akan meningkatkan kinerja. Semakin banyak tabrakan eq lebih sedikit entri dalam persamaan hashtable. Lebih sedikit pekerjaan yang harus dilakukan. ciptaan yang kinerjanya merendahkan. - Oscar Reyes "
atau
"@kdgregory: Ya, tetapi hanya jika tabrakan terjadi dengan kelas yang berbeda, untuk kelas yang sama (yang merupakan kasus), entri yang sama digunakan. - Oscar Reyes"
Sekali lagi, saya mungkin salah paham tentang apa yang sebenarnya coba dikatakan Oscar. Namun, komentar aslinya telah menyebabkan kebingungan yang cukup sehingga tampaknya bijaksana untuk menjernihkan semuanya dengan beberapa tes eksplisit sehingga tidak ada keraguan yang tersisa.
[1] - Dari Java yang Efektif, Edisi Kedua oleh Joshua Bloch:
Kapan pun itu dipanggil pada objek yang sama lebih dari sekali selama eksekusi aplikasi, metode hashCode harus secara konsisten mengembalikan bilangan bulat yang sama, asalkan tidak ada informasi yang digunakan dalam perbandingan yang sama pada objek yang dimodifikasi. Integer ini tidak perlu tetap konsisten dari satu eksekusi aplikasi ke eksekusi lain dari aplikasi yang sama.
Jika dua objek sama menurut metode equal s (Obj ect), maka pemanggilan metode hashCode pada masing-masing objek harus menghasilkan hasil integer yang sama.
Tidak diperlukan bahwa jika dua objek tidak sama menurut metode s (Object) yang sama, maka pemanggilan metode hashCode pada masing-masing dari dua objek harus menghasilkan hasil integer yang berbeda. Namun, programmer harus menyadari bahwa menghasilkan hasil integer yang berbeda untuk objek yang tidak sama dapat meningkatkan kinerja tabel hash.