在java中有三大主要的容器類:list,set和map。他們都是interface,在這三大容器類下面分別有AbstractList,AbstractSet和AbstractMap這三個抽象類去實現他們,他們幾乎實現了對應接口中的所有方法(有少數方法沒有實現,仍然是abstract的)。其中他們實現的主要的方法就是hashCode和equals方法(toString方法不一定在這些類中實現的,因爲他們有的不是直接實現對應接口的,而是通過繼承AbstractCollection這個抽象類來實現接口的,toString方法有的是在抽象類中實現的,比如AbstractList就是繼承了AbstractCollection的抽象類)。下面我們就來分別的講解一下他們內部是如何實現hashCode和equals方法的:
首先我們需要說明的是他們的實現都是有效的,即他們的實現都能夠保證下面的推導成立: a.equals(b) =》 a.hashCode() = b.hashCode()
上面這個是一個充分非必要條件(AbstractSet對於equals和hashCode的實現就是一個例子),即a.hashcode() = b.hashcode() =》 a.equals(b)是不對的。
所以有了上面的保證,我們以後就可以放心大膽的使用容器類(凡是繼承了AbstractList,AbstractSet或者AbstractMap的容器類),而不用擔心他們的equals和hashcode重新的問題了,而且java的設計者也是不希望我們去重新他們辛辛苦苦設計好的equals和hashcode方法的,我們自己貿貿然的重新可能會導致不必要的錯誤,所謂費力不討好。比如最常用的就是將一個Set(比如HashSet)作爲元素放在另一個Set(比如HashSet)中去,我們就可以直接使用,而不用擔心因爲hashcode和equals重寫的問題造成的邏輯錯誤。
下面我們就來看看java的設計者是如何重新這些方法的:
|
equals() (若a.equals(b)爲true) |
hashCode() (hashCode()的算法) |
AbstractList |
1.a和b都是List的子類 2.a.size()和b.size()相等 3. a中和b中元素必須對應相等,即a中和b中下標相等的元素必須相等(即調用元素本身的equals方法返回true) |
將list中每個元素的hashCode利用特殊的hash算法加起來,詳細代碼可以參考java源碼。 |
AbstractSet |
1. a和b都是Set的子類 2. a.size()和b.size()相等 3. a中包含b中所有元素,由於Set是不考慮位置順序的,所以我們不要求位置一樣。由於有了第二條的限制,所以這條就保證了a和b一定含有相同的元素,當然比較a和b中的元素是否相等我們調用的也是元素本身的equals方法,返回true就表示兩個元素相等。 |
將所有非null元素的hash code加起來,當然元素的hash code是通過調用元素的hashCode方法獲得的。 |
AbstractMap |
1. a和b都是Map的子類 2. a.size()和b.size()相等 3. a中包含的mapping和b中包含的mapping是相同的,即 a.entrySet().equals(b.entrySet()),具體說來就是a中的key set和b中的key set是相等的,並且相同key對應的值也是相等的。值的相等都是通過equals方法判斷的。 |
Map中所有Entry對象的hash code的和(Entry對象的hash code通過調用他們自己的hashCode方法獲得)。而Entry類的定義是Entry<K,V>在Map中起到存放鍵值對的作用,Entry<K, V>的hash code的算法是: return (key == null ? 0 : key.hashCode()) ^ (value == null ? 0 : value.hashCode()); |
結語:愉快的享受java工程師給我們帶來的便利吧。