Map API

查找:

  • 根據key(關鍵字)查找相應的value(對應值)

Map API

  • Map屬於Java的集合API

  • Map是面向查找而設計的API,查找表。

  • Map API的查找性能非常好

  • Map API提供了根據key查找value的方法

Map接口

  • Map接口定義了根據key查找value的功能,其全部實現類都提供了根據key查找value的功能

  • Map中的key是不可以重複的,value可以重複

  • 每個key對應唯一value

  • 根據key查找唯一value

  • Map常見實現類有兩個:

    -HashMap 利用散列表算法實現的Map
    -TreeMap 利用二叉排序樹算法實現Map

HashMap

  • 是Map接口的最常用實現類

  • 其內部採用了散列表算法

  • HashMap是計算機中查找速度最快的算法

構造器

Map map = new HashMap();
  • 向map集合中添加數據,數據用於查找功能

    val = map.put(key,value);

eg:
val = map.put("濟南","天氣熱");//第一次添加成功,返回null
val = map.put("濟南","歷城區");//第二次是替換,返回原有的value"天氣熱"
從map中查找數據
語法一:
Map<String,String> map = new HashMap();
public class MapDome1 {
    public  static void main(String[] a){
        /*
        ==創建一個HashMap
        向map中添加數據
        從map查找數據**==**
        */
        //創建map集合
        Map map = new HashMap();
        List<String> JN = new ArrayList<>();
        JN.add("好熱");
        JN.add("好久不下雨");
        JN.add("豔陽高照,受不了");
        //向map中添加key-value數據
        Object val = map.put("JN","天氣好熱");
        //第一次添加數據,返回null
        System.out.println(val);
        //第二次替換數據
        val = map.put("JN",JN);
        System.out.println(val);
        System.out.println(map.toString());
        map.put("HF","涼快");
        map.put("BJ","熱死");
        val = map.get("JN");
        System.out.println(val);
        System.out.println(map.toString());

    }

}
語法二:
Map<String,List> map = new HashMap();
public class MapDome {
    public static void main(String[] agr){
        Map<String,List> map = new HashMap<>();
        List<String> TJ = new LinkedList<>();
        TJ.add("這裏是個好地方");
        TJ.add("微風不燥");
        TJ.add("時光剛好");
        TJ.add("流連忘返");
        //System.out.println(TJ);
        map.put("TJ",TJ);
        List<String> BJ = new LinkedList<>();
        BJ.add("國之重地");
        BJ.add("北方太平");
        BJ.add("霧霾嚴重");
        BJ.add("肅然起敬");
        map.put("BJ",BJ);
        Scanner cin = new Scanner(System.in);
        System.out.println("請輸入想要查看的城市");
        String city = cin.nextLine();
        List<String> see = map.get(city);
        System.out.println(see);
        int num = map.size();
        System.out.println(num);
    }
}

如果希望一個key對應多個Value,則可以利用List作爲value存儲多個值。

性能測試

  • 相對於 LinkedList HashMap具有極高的查詢性能
public class MapTest {
    public static void main(String[] arg){
        //縱向比較 HashMap 不同數據量時候的查詢性能
        test(1000000);
        test(10000000);
        //橫向比較 HashMap 和 LinkedList的查詢性能
        test(10000000);
        Linkedtest(10000000);
        //橫向比較 HashMap 和 ArrayList的查詢性能
        test(10000000);
        ArrayListtest(10000000);
    }
    public static void test(int n){//寫成方法,方便代碼複用
        Map<Integer,String> map = new HashMap<>();
        for (int i = 0; i <n; i++) {
            map.put(i,"n"+i);
        }
        long t1 = System.nanoTime();
        String val = map.get(n-1);
        long t2 = System.nanoTime();
        System.out.println("Val:"+val+" , time:"+(t2-t1)+"ns");
    }
    public static void Linkedtest(int n){
        List<String> list = new LinkedList<>();
        for (int i = 0; i <n; i++) {
            list.add("n"+i);
        }
        long t1 = System.nanoTime();
        String str = list.get(n/2);
        long t2 = System.nanoTime();
        System.out.println("LStr:"+str+" , time:"+(t2-t1)+"ns");
    }
    public static void ArrayListtest(int n){
        List<String> list = new ArrayList<>();
        for (int i = 0; i <n; i++) {
            list.add("n"+i);
        }
        long t1 = System.nanoTime();
        String str = list.get(n-1);
        long t2 = System.nanoTime();
        System.out.println("RStr:"+str+" , time:"+(t2-t1)+"ns");
    }
}

HashMap工作原理

  • LinkedList 在查詢元素時,採用順序查找,當被查詢元素在頭尾時查詢速度快。但是當被查詢元素處於鏈表中部時,則會出現順序查找,此時性能非常差。
  • HashMap 利用散列算法可以直接定位元素位置,所以查找性能優異,因爲不是順序查找,所以散列查找不收數據量的影響。

散列算法的工作原理

  • 在HashMap內部有一個數組用於存儲key-Value數據
  • 在添加數據時,根據Key的hashCode()返回值計算在數組的存儲位置,將Key-Value數據存儲到數組的對應位置
  • 根據Key查找時,根據Key的hashCode()計算數組下標位置,直接定位到數組中Key-Value數據,並返回value值。
HashMap中查找的特點:
  • 不進行順序查找,直接利用算法定位數組中的下標位置。因爲避免了順序查找,所以查詢性能好!
    這裏寫圖片描述

hashCode()方法與散列表

  • Java爲了在最底層支持散列表(HashMap 哈希表)算法在Object類上定義了hashCode()方法,用於支持散列表算法。

  • hashCode()方法和equals()是一對方法,需要一同重寫!!!!
    ——如果不成對重寫這兩個方法,會造成HashMap工作故障

  • 如果重寫hashCode時要遵守如下規則

    1.當兩個對象equals比較相等時,兩個對象必須有一樣的hashCode值。

    2.當兩個對象equals比較不相等時,兩個對象的hashCode儘可能不同。

  • Eclipse及其他的開發工具提供了成對生成equals和hashCode方法,利用工具生成即可。

    ——一般按照核心關鍵屬性比較兩個對象是否相等。

  • Java的API,包括String、Integer都很好的成對的重寫了equals和hashCode。

對象的默認hashCode值不是對象的地址值

HashMap元素添加過程(put)

  • HashMap中有一個數組用於存儲key-value數據對

    1.根據key的hashCode值,利用散列算法計算出數組下標位置。

    2.找到數組下標位置

    如果位置爲null,則直接將key-value存儲到數組中

    如果位置上已有數據,則調用key的equals方法比較數組中存在key

      >如果key相等,則替換對應的value值
    
      >如果key不等,則找鏈表中後續的元素並且判斷是否相等
      
      >如果key相等,則替換value
      
      >如果不相等則將key-value追加到鏈表後部
    

概括:根據key的hashCode計算數組下標位置,再用equals比較ke是否相等,相等就替換,不等就插入。

HashMap元素的查找過程(get)

1.根據key的hashCode值,利用散列算法計算出數組下標位置。

2.找到數組下標位置
>如果位置爲null,則直接返回null

 >如果位置上已有數據,則調用key的equals方法比較數組中存在key

    >如果key相等,則返回value數據

    >如果key不等,則找鏈表中後續的元素並且判斷是否相等
    
    >如果key相等,則返回value
    
    >如果不相等則返回null

概述:先根據hashCode計算數組下標位置,在根據key的equals比較key,如果找到則返回value,否則返回null。

HashMap相關術語

  • HashMap中保存key-value的數組稱爲 散列表

HashMap中的數據按照散列值存儲和添加順序無關。但不是一個隨機順序

  • 根據key的hashCode計算數組下標位置的算法稱爲 散列算法經過散列算法得到的數組下標稱爲散列值

HashMap中的散列算法利用二進制運算實現的,性能極好。

  • 散列值相同時,key-value存儲的鏈表結構稱爲散列桶

散列桶中查詢是順序查找,性能不好,原則上要避免散列桶。

  • 當key的hashCode值很分散時候,散列桶會變少。

hashCode的實現規則就是一個分散原則。

  • HashMap++元素和數組容量的比值++稱爲加載因子,當加載因子小於等於75%的情況下,經過“統計”發現很少出現散列桶,即便出現散列桶也很少超過3層。
  • 當向HashMap中添加數據時,其數據和數組容量的比值大於75%時,HashMap會對數組進行擴容,並重新計算元素的散列值,這個過程稱爲重新散列

重新散列會影響散列表添加()性能。
散列表提供了重載構造器,可以預先設置數組的容量,避免重新散列,提高性能。HashMap可以在重載構造器中設置數組容量,用於減少散列。

重載構造器語法:
Map<String,String> map = new HashMap<>(n+n/2);
該方法建議在已知數組容量的時候使用

經典面試題

HashMap如何存儲的?

概括:根據key的hashCode計算數組下標位置,再用equals比較ke是否相等,相等就替換,不等就插入。
概述:先根據hashCode計算數組下標位置,在根據key的equals比較key,如果找到則返回value,否則返回null。

HashMap 用途

  • HashMap的查找性能非常好,在軟件開發中儘量將查找功能交給HashMap完成。
  • 案例, 利用map緩存Http協議頭信息:

1.創建文件 headers.txt 保存http協議頭信息:

我這裏用的是有道雲筆記首頁的Request Headers

這裏寫圖片描述

Host: ursdoccdn.nosdn.127.net
Connection: keep-alive
If-None-Match: e36a35c363d892f3c44cfa821427ce36
If-Modified-Since: Mon, 16 Apr 2018 14:57:44 Asia/Shanghai
User-Agent: Mozilla/5.0 (X11; Linux x86_64) AppleWebKit/537.36 (KHTML, like Gecko) Ubuntu Chromium/66.0.3359.181 Chrome/66.0.3359.181 Safari/537.36
Accept: */*
Referer: https://note.youdao.com/web/
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9

2.讀取文件內容到Map:

        /**
 * 輸出全部頭信息,利用遍歷map可以輸出全部信息
 * map沒有直接提供遍歷接口,map間接提供了遍歷功能
 * map.entrySet().iterator()
 */
public class headersDome {
    public static void main(String[] arg){
        try {
            BufferedReader br 
	            = new BufferedReader(
				     new InputStreamReader(
					     new FileInputStream("./headrs.txt")));
            String line = null;
            Map<String,String> map = new HashMap<>();
            while ((line = br.readLine()) != null){
                //line 代表文件中的每一行
                //爲了邏輯嚴禁,跳過可能出現的空行
                if(line.trim().isEmpty()){
                    continue;
                }
                String[] arr = line.split(":\\s");
                map.put(arr[0],arr[1]);

            }
            System.out.println(map);

3.利用map查詢一個協議頭 信息:

        //查詢頭信息
		String host = map.get("Host");
		System.out.println(host);

4.利用map的變量,顯示全部緩存在map中的信息

        try {
              Set<Map.Entry<String,String>> entries 
		              = map.entrySet();
                for (Map.Entry<String,String> e:entries) {
         //獲取全部Entry的集合,每個entry對象包含兩個屬性,分別是key和value
         //e 代表map集合中的每個key-value對應e.getKey()方法獲取key值
         //e.getValue()方法是獲取value值
                    System.out.println(e.getKey()+":"+e.getValue());
                }
            } catch (Exception e) {
                e.printStackTrace();
            }
            br.close();
        }catch (Exception e){
            System.out.println("文件未找到,請覈對文件位置");

        }
    }
}

5.測試

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章