參考的優秀文章:
《Java編程思想》第四版
《Effective Java》第二版
Map接口是映射表的結構,維護鍵對象與值對象的對應關系,稱鍵值對。
> hashcode()和equals()
hashcode()和equals()即用於識別對象的身份。
在HashMap或類似的實現中,查找一個對象,是通過hashcode()返回的散列值映射到一個范圍內的下標,在通過equals()比較此下標連接的鏈表是否存在相同的對象。
簡單來說,hashcode()用於參考、快速定位(縮減范圍),真正是否等於是依賴equals()。
默認的hashcode()和equals()
如何對象沒有覆蓋這兩個方法,那么就是繼承Object對象的。
在Object中,hashcode()是使用對象的地址計算散列值;equals()只比較對象的地址。
必要的時候,我們需要覆蓋這兩個方法。
覆蓋這兩個方法有什么原則呢?
equals()的覆蓋,主要是基於此對象的業務。
而hashcode()的覆蓋原則,詳情可參見《Effective Java》的“覆蓋equals時總要覆蓋hashcode”一節。
有幾個比較重要的原則:
1、兩個equals相等的對象,其hashcode是相等的。
2、兩個equals不等的對象,其hashcode有可能是相等的。
3、好的hashcode()應產生分布均勻的散列碼。
基於第3點,《Effective Java》有具體的建議。
1、定義變量result為非零的數。
2、用公式result = 31 * result + c,其中c是類中各個域的散列值。
用Eclipse生成的hashcode()與此原則類似,我們可以看看:

public class User { private Integer id; private String name; private boolean flag = false; private long phoneNumber; @Override public int hashCode() { final int prime = 31; int result = 1; result = prime * result + (flag ? 1231 : 1237); result = prime * result + ((id == null) ? 0 : id.hashCode()); result = prime * result + ((name == null) ? 0 : name.hashCode()); result = prime * result + (int) (phoneNumber ^ (phoneNumber >>> 32)); return result; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null) return false; if (getClass() != obj.getClass()) return false; User other = (User) obj; if (flag != other.flag) return false; if (id == null) { if (other.id != null) return false; } else if (!id.equals(other.id)) return false; if (name == null) { if (other.name != null) return false; } else if (!name.equals(other.name)) return false; if (phoneNumber != other.phoneNumber) return false; return true; } }
> 常用的Map實現
Map接口有幾個常用的實現類,HashMap、TreeMap、LinkedHashMap、ConcurrentHashMap。其中HashMap最常用。
HashMap,基於散列表實現,查找速度快(依賴hashcode()和equals()),存放元素無序。
TreeMap,基於紅黑樹實現,存放有序(依賴Compareable)。
LinkedHashMap,基於散列表、雙向鏈表實現。如HashMap的查找速度,遍歷時有序(默認為插入順序,可通過構造方法設置“最近最少使用(Least Recently Used)順序”)。
ConcurrentHashMap,線程安全的HashMap,用於替代HashTable(線程安全,但基於整個對象的鎖實現的,效率不高),而ConcurrentHashMap是采用分段加鎖的方式實現,提升了效率。
代碼演示LinkedHashMap的兩種排序:

import java.util.LinkedHashMap; import java.util.Map; public class LinkedHashMapTester { public static void main(String[] args) { System.out.println("LinkedHashMap 根據插入順序排列:"); Map<String, String> map = new LinkedHashMap<String, String>(); for (Integer i = 0; i < 5; i++) { map.put("k" + i.toString(), "v" + i.toString()); } for (Integer i = 10; i > 5; i--) { map.put("k" + i.toString(), "v" + i.toString()); } System.out.println(map); map.get("k10"); System.out.println(map); System.out.println("LinkedHashMap 根據最近最少使用(Least Recently Used)順序排列:"); map = new LinkedHashMap<String, String>(16, 0.75f, true); // 沒有了其他構造方法設置其排序為true了。初始容量、加載因子使用默認的16和0.75。 for (Integer i = 0; i < 5; i++) { map.put("k" + i.toString(), "v" + i.toString()); } for (Integer i = 10; i > 5; i--) { map.put("k" + i.toString(), "v" + i.toString()); } System.out.println(map); map.get("k10"); System.out.println(map); } }
日志,注意看使用“K10”后“K10”的位置:

LinkedHashMap 根據插入順序排列: {k0=v0, k1=v1, k2=v2, k3=v3, k4=v4, k10=v10, k9=v9, k8=v8, k7=v7, k6=v6} {k0=v0, k1=v1, k2=v2, k3=v3, k4=v4, k10=v10, k9=v9, k8=v8, k7=v7, k6=v6} LinkedHashMap 根據最近最少使用(Least Recently Used)順序排列: {k0=v0, k1=v1, k2=v2, k3=v3, k4=v4, k10=v10, k9=v9, k8=v8, k7=v7, k6=v6} {k0=v0, k1=v1, k2=v2, k3=v3, k4=v4, k9=v9, k8=v8, k7=v7, k6=v6, k10=v10}