【Java】Map雜談,hashcode()、equals()、HashMap、TreeMap、LinkedHashMap、ConcurrentHashMap


參考的優秀文章:

《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;
    }
    
        

}
View Code

 

> 常用的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);
    }

}
View Code

 

日志,注意看使用“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}
View Code

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM