Java中的對映Map - 入門篇

語言: CN / TW / HK

前言

大家好啊,我是湯圓,今天給大家帶來的是《Java中的對映Map - 入門篇》,希望對大家有幫助,謝謝

簡介

前面介紹了集合List,這裡開始簡單介紹下對映Map,相關類如下圖所示

集合

正文

Map是一種儲存鍵值對的資料集合,鍵以雜湊或者樹形結構進行儲存;

為什麼會設計Map介面呢?

假設我們有一個員工類,裡面有Id屬性和姓名等其他資訊,現在我們把所有員工都存到List裡,然後要找出Id為001的員工,你會發現,你需要從List中遍歷每個物件,然後取出Id進行比較;

你會發現這種查詢法效率很低,有點殺雞用牛刀的感覺;

這時如果有一個集合類,可以以鍵值對對映的方式的儲存員工資訊(Id作為鍵,員工資訊作為值),那麼就可以只遍歷鍵列表,然後進行比較;

你會發現這種查詢法效率提高了很多,因為物盡其用了(比較的是Id,也只是取了Id,沒有浪費);

這就是Map介面的作用,可以根據某個鍵去查詢對應的資訊,有點類似於資料庫的設計。

Map的種類

Map主要有三種類型:HashMap(常用)、TreeMap(樹形結構)、LinkedHashMap(前兩者的結合)

我們先來看一下Map介面主要的幾個方法:

  • V put(K key, V value):往Map中新增鍵值對,其中key為鍵,value為值;如果key存在,則覆蓋原有的值;如果不存在,則新建鍵值對。
  • V get(Object key):從Map中查詢鍵key對應的值,如果沒有,則返回null
  • default V getOrDefault(Object key, V defaultValue):從Map中查詢鍵key對應的值,如果沒有,則返回第二個引數(設定的預設值);這裡的修飾符default是用在介面方法中,表示這個方法在介面中已經實現了,子類可以不實現(Java8開始支援)
  • Set<K> keySet():返回Map中Key的集合;之所以返回Set,是因為Map中的key不能有重複,所以用Set最適合了
  • Collection<V> values():返回Map中Values的集合

下面我們簡單看下三者的區別

HashMap TreeMap LinkedHashMap
訪問速度 適中
元素是否有序 無序 有序,預設按key排序 有序,預設按插入的順序
適用場景 普通的插入,查詢(用的最多) 需要對key進行排序的場景(比如員工按年齡排序等) 需要保證查詢和插入順序一致的場景(類似佇列)

接下來我們以HashMap為例,來介紹Map介面

HashMap

HashMap內部是陣列+連結串列的結構;

因為在新增鍵值對的時候,Key做了hash處理,然後按照hash值進行排列;

  • 如果hash值沒有重複,就按照陣列的方式依次排列;
  • 如果hash值有重複的,就新增到已有的鍵值對後面(Java8以後是尾部插入),形成連結串列結構;

整體結構 如下圖所示

HashMap結構

這裡只是簡單介紹,以後再深入瞭解

下面用程式碼示範一下

// 鍵值對集合,鍵不可以重複
Map<String, Integer> map = new HashMap<>();
// 新增:首先會檢查對應的key是否存在,如果不存在,則新建鍵值對,然後填充;如果存在,則覆蓋已有的值
map.put("a", 1); // 這裡的1會自動裝箱為Intege型別
// 查詢
int value1 = map.get("a");
int value2 = map.get("b"); 
System.out.println(map);

這裡有個很有意思的現象,你覺得value2會是多少呢?

答案是多少都不是,因為程式執行到這一行就出錯了,報空指標異常

HashMap空指標異常

不應該返回null嗎?怎麼會出錯?

這裡涉及到拆箱和裝箱的問題,上面我們在新增put的時候,int 1自動裝箱為Integer;

然後在獲取get的時候,對應的也是要進行拆箱的,將Integer轉為int;

但是由於獲取的value = null,所以就相當於對null進行拆箱,結果就報錯了。

解決辦法就是嚴格按照Map的型別資訊進行新增和獲取;

將上面的程式碼加以修改,如下所示

// 鍵值對集合,鍵不可以重複
Map<String, Integer> map = new HashMap<>();
// 新增:首先會檢查對應的key是否存在,如果不存在,則新建鍵值對,然後填充;如果存在,則覆蓋已有的值
map.put("a", 1); // 這裡的1會自動裝箱為Intege型別
// 查詢
Integer value1 = map.get("a");
Integer value2 = map.get("b");
System.out.println(map);

此時value2就等於null了。

關於自動裝箱和拆箱,網上資源很多,這裡就不再細說了

TreeMap

TreeMap在插入的時候,可以按照鍵的順序進行排序

它適合用在排序比較多的場景,效能會比HashMap差一些

LinkedHashMap

LinkedHashMap擁有HashMap的大部分優點,且保證了插入的順序,使得在查詢的時候,可以按照插入的順序依次讀取

三者的排序比較

下面用程式碼演示一下,依次插入100個數,看看他們分別是怎麼排序的

HashMapDemo.java

public class HashMapDemo {
    public static void main(String[] args) {
        // 鍵值對集合,鍵不可以重複
        Map<String, Integer> map = new HashMap<>();
        // 倒序插入100個數
        int i =100;
        while (i-->0){
            map.put(""+i, i);
        }
        // 查詢
        for (String str :
                map.keySet()) {
            // 這裡會亂序輸出
            System.out.println(str);
        }
    }
}

輸出如下所示:很亂

HashMap輸出亂序

TreeMapDemo.java

public class MapDemo {
    public static void main(String[] args) {
       
        // TreeMap
        Map<String, Integer> map1 = new TreeMap<>();
        // 連續倒序插入100個數
        int k =100;
        while (k-->0){
            map1.put(""+k, k);
        }
        // 查詢
        for (String str :
                map1.keySet()) {
            // 這裡會正序輸出
            System.out.println(str);
        }
    }
}

輸出如下所示:

TreeMap輸出正序

細心的你們,應該會發現上面的輸出有點別緻

那是因為這裡的鍵key(0~99)其實不是整型,而是字串型別,所以排序按照字串的升序來排,才會出現如圖所示的結果

(建議實際場景不要這樣搞,容易出事,字串儘量不要用純數字,而是要跟字母做拼接;)

正確的做法是key=“a”+i,這種方式

LinkedHashMapDemo.java

public class MapDemo {
    public static void main(String[] args) {
        // LinkedHashMap
        Map<String, Integer> map2 = new LinkedHashMap<>();
        // 倒序插入100個數
        int j =100;
        while (j-->0){
            map2.put("a"+j, j);
        }
        for (String str :
                map2.keySet()) {
            // 這裡按照插入的順序依次輸出
            System.out.println(map2.get(str));
        }
    }
}

輸出如下所示:

LinkedHashMap按插入順序輸出

總結

Map一般用到的有HashMap,TreeMap,LinkedHashMap,當然還有併發相關的,這裡入門級別的先不涉及(比如ConcurrentHashMap)

  • HashMap的插入和訪問都很快,但是內部是無序排列

  • TreeMap的插入和訪問都很慢,但是內部是有序排列,預設按key升序排列

  • LinkedHashMap擁有HashMap的大部分優點,而且還可以按照元素插入的順序來訪問元素,但是效能會比HashMap差

後記

最後,感謝大家的觀看,謝謝

分享到: