摘要:相關的文章網上很多了寫這個主要是按自己的思路進行記錄是什么中的實現是一個本地方法生成一個表征當前對象實例的特征值具體的實現根據的實現可能會不同中實際計算的函數的實現如下為時是直接使用的內存地址但默認使用的是的隨
hashcode相關的文章網上很多了, 寫這個主要是按自己的思路進行記錄
hashCode是什么Object中的hashCode實現是一個本地方法, 生成一個表征當前對象實例的特征值.
public native int hashCode();
具體的實現根據jvm的實現可能會不同. JDK1.8中實際計算hashcode的get_next_hash函數的實現如下(src/share/vm/runtime/synchronizer.cpp)
static inline intptr_t get_next_hash(Thread * Self, oop obj) { intptr_t value = 0 ; if (hashCode == 0) { // This form uses an unguarded global Park-Miller RNG, // so it"s possible for two threads to race and generate the same RNG. // On MP system we"ll have lots of RW access to a global, so the // mechanism induces lots of coherency traffic. value = os::random() ; } else if (hashCode == 1) { // This variation has the property of being stable (idempotent) // between STW operations. This can be useful in some of the 1-0 // synchronization schemes. intptr_t addrBits = intptr_t(obj) >> 3 ; value = addrBits ^ (addrBits >> 5) ^ GVars.stwRandom ; } else if (hashCode == 2) { value = 1 ; // for sensitivity testing } else if (hashCode == 3) { value = ++GVars.hcSequence ; } else if (hashCode == 4) { value = intptr_t(obj) ; } else { // Marsaglia"s xor-shift scheme with thread-specific state // This is probably the best overall implementation -- we"ll // likely make this the default in future releases. unsigned t = Self->_hashStateX ; t ^= (t << 11) ; Self->_hashStateX = Self->_hashStateY ; Self->_hashStateY = Self->_hashStateZ ; Self->_hashStateZ = Self->_hashStateW ; unsigned v = Self->_hashStateW ; v = (v ^ (v >> 19)) ^ (t ^ (t >> 8)) ; Self->_hashStateW = v ; value = v ; } value &= markOopDesc::hash_mask; if (value == 0) value = 0xBAD ; assert (value != markOopDesc::no_hash, "invariant") ; TEVENT (hashCode: GENERATE) ; return value; }
hashcode為4時是直接使用的內存地址, 但默認使用的是hashcode>=5的隨機算法. 可以用JVM parameter -XX:hashCode來調整.
查了下, xor-shift scheme是弗羅里達州立大學一位叫做George Marsaglia的老師發明的使用位移和異或運算生成隨機數的方法, 所以在計算機上運算速度非常快(移位指令需要的機器周期更少).有興趣的可以去深入了解.
hashCode是由散列方法得來的, 所以不同對象按hashCode方法計算的散列值, 是可能相同的.
類似于存在兩個不同串擁有相同的MD5值, 并且可能存在未知的其它串MD5值相同.
所以hashCode相同的對象, 并不一定是相等的, 需要通過equals方法比較.
如果兩個對象的equals為true, 那么這兩個對象的HashCode一定相同.
兩個對象的hashCode值相同, 對于其作為hashmap的key沒有影響, 即使映射到同一個槽中, 也可以通過對比key本身來進行區分.
equals需要滿足數個性質:
自反性, 對稱性, 傳遞性, 一致性.
對稱性: 如果x.equals(y)返回是true,那么y.equals(x)也應該返回是true
自反性: x.equals(x)返回是true
傳遞性: 如果x.equals(y)返回是true,而且y.equals(z)返回是true,那么z.equals(x)也應該返回是true
一致性: 如果x.equals(y)返回是true,只要x和y內容一直不變, 那么x.equals(y)始終都返回都是true
這個其實也很好理解, 簡單點思考可看作是兩個數字在比較, 那么滿足這些性質便是理所當然.
判斷對象相等需要重寫equals方法, 否則會調用Object的equals方法.
為什么重寫equals方法, 通常需要重寫hashCode方法?
假設現在有一個類Apple, 有兩個屬性color和weight. 比較兩個Apple類的實例A和B是否相等(當然其中一個可能不是其實例), 實際等價于判斷兩者的兩個屬性是否都相等; 如果不重寫hashCode方法, 當new一個新的Apple實例C, 將其color和weight屬性設置成與B相同, 這就導致B.equals(C)時兩者的hashCode不一致, 會產生理解上的混淆.
重寫hashCode的經典方式是使用17和31散列碼的實現:
public class Apple { private String color; private int weight; @Override public boolean equals(Object o) { if (o == this) return true; if (!(o instanceof Apple)) { return false; } Apple apple = (Apple) o; return apple.color.equals(color) && apple.weight == weight; } @Override public int hashCode() { int result = 17; result = 31 * result + color.hashCode(); result = 31 * result + weight; return result; } }
除此之外, 可以使用java.util.Objects去重寫equals和hashCode, 也可以使用Apache Commons Lang的LangEqualsBuilder和HashCodeBuilder方法. 這兩種方式也是對于17和31散列碼思想的封裝實現.
集合類的hashCodeAbstractSet中的實現, 對所有元素的hashCode對應的int值進行累加求和. 這樣的話, 兩個都包括"a", "b", "c"三個元素的HashSet, 不論添加次序, 其hashCode是一樣的.
public int hashCode() { int h = 0; Iteratori = iterator(); while (i.hasNext()) { E obj = i.next(); if (obj != null) h += obj.hashCode(); } return h; }
AbstractList中的實現, 使用了31讓結果更分散.
public int hashCode() { int hashCode = 1; for (E e : this) hashCode = 31*hashCode + (e==null ? 0 : e.hashCode()); return hashCode; }
AbstractMap是遍歷將每個entry的hashCode累加. 等等.
如果兩個集合對象的hashCode相等, 完全無法說明這兩個對象相等, 但如果不等, 說明這兩個對象肯定是不等的. 可作為一個快速判斷不等的方案.
緩存對象的hashCodehashCode每次都是實時計算的, 雖然其是一個本地方法, 速度非常快, 如果有大量重復使用的場景, 可以考慮像Integer內部緩存int值為-128到127的對象一樣進行緩存.
文章版權歸作者所有,未經允許請勿轉載,若此文章存在違規行為,您可以聯系管理員刪除。
轉載請注明本文地址:http://specialneedsforspecialkids.com/yun/69178.html
摘要:如果根據方法得到兩個對象不相同,那么兩個對象的方法的結果不一定不相同,我們可以利用這一點來提高散列表的性能。最后回到文章開頭的問題,如何判斷兩個對象或值是否相同這個問題其實有兩方面的含義,一方面是判斷的方法,另一方面是判斷的效率。 Java中有很多場景需要判斷兩個對象或者兩個值,那么 判斷是否相同的依據是什么? 如何判斷是否相同呢? 為了解釋這個問題,我們從Java語言的根說起,那...
摘要:中,任何未處理的受檢查異常強制在子句中聲明。運行時多態是面向對象最精髓的東西,要實現運行時多態需要方法重寫子類繼承父類并重寫父類中已 1、簡述Java程序編譯和運行的過程:答:① Java編譯程序將Java源程序翻譯為JVM可執行代碼--字節碼,創建完源文件之后,程序會先被編譯成 .class 文件。② 在編譯好的java程序得到.class文件后,使用命令java 運行這個 .c...
摘要:中,任何未處理的受檢查異常強制在子句中聲明。運行時多態是面向對象最精髓的東西,要實現運行時多態需要方法重寫子類繼承父類并重寫父類中已 1、簡述Java程序編譯和運行的過程:答:① Java編譯程序將Java源程序翻譯為JVM可執行代碼--字節碼,創建完源文件之后,程序會先被編譯成 .class 文件。② 在編譯好的java程序得到.class文件后,使用命令java 運行這個 .c...
摘要:我們使用調試器卻發現在中已經存儲了這個對象。中和有一個契約如果兩個對象相等的話,它們的必須相等但如果兩個對象的相等的話,這兩個對象不一定相等。的結構能夠快速找到一個對象,而不是進行較慢的線性查找。可以看作是數組的數組。 java.lang.Object類中有兩個非常重要的方法: public boolean equals(Object obj) public int hashCode...
摘要:中的使用散列來高效的查找和存儲值。理解中的方法是頂層對象中的方法因此中所有的對象都會帶有方法。中給出了覆蓋方法的最佳實踐把某個非零的常數值比如保存在一個名為的類型中。 Java中的HashMap使用散列來高效的查找和存儲值。HashMap內部使用Map.Entry的形式來保存key和value,使用put(key,value)方法存儲值,使用get(key)方法查找值。 理解hashC...
閱讀 4376·2021-11-24 10:24
閱讀 1412·2021-11-22 15:22
閱讀 2042·2021-11-17 09:33
閱讀 2448·2021-09-22 15:29
閱讀 520·2019-08-30 15:55
閱讀 1660·2019-08-29 18:42
閱讀 2737·2019-08-29 12:55
閱讀 1777·2019-08-26 13:55