前言
大家好啊,我是湯圓,今天給大家帶來的是《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對應的值,如果沒有,則返回nulldefault 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以后是尾部插入),形成鏈表結構;
整體結構 如下圖所示
這里只是簡單介紹,以后再深入了解
下面用代碼示范一下
// 鍵值對集合,鍵不可以重復
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會是多少呢?
答案是多少都不是,因為程序運行到這一行就出錯了,報空指針異常
不應該返回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);
}
}
}
輸出如下所示:很亂
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);
}
}
}
輸出如下所示:
細心的你們,應該會發現上面的輸出有點別致
那是因為這里的鍵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));
}
}
}
輸出如下所示:
總結
Map一般用到的有HashMap,TreeMap,LinkedHashMap,當然還有並發相關的,這里入門級別的先不涉及(比如ConcurrentHashMap)
-
HashMap的插入和訪問都很快,但是內部是無序排列
-
TreeMap的插入和訪問都很慢,但是內部是有序排列,默認按key升序排列
-
LinkedHashMap擁有HashMap的大部分優點,而且還可以按照元素插入的順序來訪問元素,但是性能會比HashMap差
后記
最后,感謝大家的觀看,謝謝