【Java必修課】HashMap性能很好?問過我EnumMap沒

来源:https://www.cnblogs.com/larrydpk/archive/2019/11/07/11815978.html

1 簡介 我們知道 只是一個介面,它有多種實現,Java中最常用的是 了。而本文想講述的是另一個實現: 。它是枚舉類型的 ,要求它的Key值都必須是枚舉型的。 2 創建你的EnumMap 既然是關於枚舉類型的Map,我們先創建一個枚舉,以便後續使用: 2.1 創建EnumMap的三種方法 JDK提供 ...


1 簡介

我們知道Map只是一個介面,它有多種實現,Java中最常用的是HashMap了。而本文想講述的是另一個實現:EnumMap。它是枚舉類型的Map,要求它的Key值都必須是枚舉型的。

2 創建你的EnumMap

既然是關於枚舉類型的Map,我們先創建一個枚舉,以便後續使用:

public enum Directions {
    NORTH, SOUTH, EAST, WEST
}

2.1 創建EnumMap的三種方法

JDK提供的創建EnumMap的方法有三種,代碼如下:

//new EnumMap
EnumMap<Direction, String> enumMap = new EnumMap<>(Direction.class);
enumMap.put(Direction.EAST, "東");
enumMap.put(Direction.SOUTH, "南");
//從EnumMap複製
EnumMap<Direction, String> enumMapCopyEnumMap = new EnumMap<>(enumMap);
assertEquals(enumMap, enumMapCopyEnumMap);
//從Map複製
Map<Direction, String> hashMap = Maps.newHashMap();
hashMap.put(Direction.EAST, "東");
hashMap.put(Direction.SOUTH, "南");
EnumMap<Direction, String> enumMapCopyHashMap = new EnumMap<>(hashMap);
assertEquals(enumMap, enumMapCopyHashMap);
  • (1) 使用new EnumMap()方法時,與HashMap不同,它必須傳入一個枚舉的類型才能創建對象;

  • (2) 從EnumMap複製,這時傳入的參數為EnumMap

  • (3) 從Map複製,傳入的參數為Map,但要求Key的類型必須是枚舉型。

2.2 聰明的Guava

其實可以綜合上面三種情況,實際就是兩種方法:

  • (1) 使用new EnumMap(Class<K> keyType)

  • (2) 使用new EnumMap(Map<K, ? extends V> m)

聰明的Guava就只提供了這兩種方法,如下:

//使用Guava創建
EnumMap<Direction, String> enumMapGuava = Maps.newEnumMap(Direction.class);
enumMapGuava.put(Direction.SOUTH, "南");
assertEquals(1, enumMapGuava.size());
enumMapGuava = Maps.newEnumMap(enumMap);
assertEquals(enumMap, enumMapGuava);

3 基本操作

提供的方法與Map當然是一樣的,操作十分方便,代碼如下:

@Test
public void operations() {
  EnumMap<Direction, String> map = Maps.newEnumMap(Direction.class);
  //增加
  map.put(Direction.EAST, "東");
  map.put(Direction.SOUTH, "南");
  map.put(Direction.WEST, "西");
  //查詢
  assertTrue(map.containsKey(Direction.EAST));
  assertFalse(map.containsKey(Direction.NORTH));
  //刪除
  map.remove(Direction.EAST);
  assertFalse(map.containsKey(Direction.EAST));
  assertFalse(map.remove(Direction.WEST, "北"));
  assertTrue(map.remove(Direction.WEST, "西"));
  //清空
  map.clear();
  assertEquals(0, map.size());
}

需要特別指出的是刪除方法,可以傳入Key和Value兩個參數,map.remove(Direction.WEST, "西")當鍵值對匹配時,則可以刪除成功;map.remove(Direction.WEST, "北")匹配失敗,則不會刪除。

4 集合視圖

4.1 有序性

與Map介面提供的功能一樣,EnumMap也能返回它的所有Values、Keys和Entry等。但與HashMap不同的是,EnumMap返回的視圖是有序的,這個順序不是插入的順序,而是枚舉定義的順序。代碼如下:

EnumMap<Direction, String> map = Maps.newEnumMap(Direction.class);
map.put(Direction.EAST, "東");
map.put(Direction.SOUTH, "南");
map.put(Direction.WEST, "西");
map.put(Direction.NORTH, "北");
//返回所有Value
Collection<String> values = map.values();
values.forEach(System.out::println);
//返回所有Key
Set<Direction> keySet = map.keySet();
keySet.forEach(System.out::println);
//返回所有<Key,Value>
Set<Map.Entry<Direction, String>> entrySet = map.entrySet();
entrySet.forEach(entry -> {
  System.out.println(entry.getKey() + ":" + entry.getValue());
});

輸出的結果如下:

北
南
東
西
NORTH
SOUTH
EAST
WEST
NORTH:北
SOUTH:南
EAST:東
WEST:西

這個順序與我們定義枚舉的順序確實是一樣的,而與添加的順序無關。

4.2 聯動性

除了有序性之外,EnumMap返回的集合視圖還有一點不同就是聯動性,即牽一發而動全身。改變其中一個,另外的也跟著變了。看代碼一下就明白了:

//Values、keySet、entrySet改變會影響其它
values.remove("東");
assertEquals(3, map.size());
assertEquals(3, keySet.size());
assertEquals(3, entrySet.size());

keySet.remove(Direction.WEST);
assertEquals(2, map.size());
assertEquals(2, values.size());
assertEquals(2, entrySet.size());

entrySet.removeIf(entry -> Objects.equals(entry.getValue(), "北"));
assertEquals(1, map.size());
assertEquals(1, keySet.size());
assertEquals(1, values.size());

//Map的改變會影響其它視圖
map.clear();
assertEquals(0, values.size());
assertEquals(0, keySet.size());
assertEquals(0, entrySet.size());

5 性能

性能是我們選擇EnumMap的主要原因之一,那為何它性能會比優秀的HashMap還要好呢?通過看源碼可以得知:

(1)底層是通過兩個數組來存放數據的,一個放Keys,一個放Values;

(2)因為Key值是枚舉類型,即一開始就確定了元素個數,所以在創建一個EnumMap的時候,存放數據的數組就已經確定了大小,不用考慮後續擴容帶來的性能問題。

(3)枚舉本身就是固定順序的,可以通過Enum.ordinal()方法獲得順序,這個便可以作為查詢與插入的索引,而不用計算HashCode,性能也會比較快。這個順序也就是數組下標。這也是EnumMap的集合視圖都是有序的原因。

(4)因為大小固定,則不用考慮載入因數,也不會有哈希衝突的問題,空間複雜度小。

6 結論

本文介紹了EnumMap作為一個Map的特殊實現的創建、使用、集合視圖和性能分析,發現它的確是有過人之處的。當我們的Key值是枚舉時,不妨可以試一試EnumMap,性能會更好哦。


歡迎關註公眾號<南瓜慢說>,將持續為你更新...

多讀書,多分享;多寫作,多整理。


您的分享是我們最大的動力!

更多相關文章
  • day one 演算法是充分利用解題環境所提供的基本操作,對輸入數據進行 逐步加工、變換和處理,從而達到解決問題的目的。 電腦的基本功能操作包括以下四個方面: 邏輯運算:與、或、非; 算術運算:加、減、乘、除; 數據比較:大於、小於、等於、不等於、大於等於、小於等於; 數據傳送:輸入、輸出、賦值。 ...
  • 1.首先是對vs2017這款軟體的使用 1.VS中的scanf()這個函數的使用問題 直到這次寫代碼我才知道VS中用scanf是會被警告的,VS中正規的類似於scanf()函數的輸入函數是scanf_s()只有使用這個函數你才不會報錯,它有三個參分別是數據類型,地址,最大存儲量, 還有兩種方法 第一 ...
  • 寶塔官方建議是純凈的系統,我使用docker運行一個ubuntu容器,模擬一個純凈的系統,這樣也不會影響到我的其他服務。 docker run --name baota -id -p 8888:8888 ubuntu docker exec -it baota bashapt-get updatea ...
  • 一個程式就是一個世界,有很多對象(變數) Golang 語言面向對象編程說明 1) Golang 也支持面向對象編程(OOP),但是和傳統的面向對象編程有區別,並不是純粹的面向對 象語言。所以我們說 Golang 支持面向對象編程特性是比較準確的。 2) Golang 沒有類(class),Go 語 ...
  • 雙11臨近的我發現自己真的很窮很窮很窮(重要的問題說三遍)…… 貧窮催人上進。於是我就尋思著在空閑時間自己搗鼓一下錢生錢的游戲是怎麼玩的,畢竟就算註定做韭菜也要做一根有知識有理想的韭菜。 第一個要玩的模型就是股票交易中的均線黃金交叉。 作為一個基礎的韭菜一定聽說過均線黃金交叉原則,也就是說當短期移動 ...
  • Python繪圖庫Turtle Turtle介紹 Turtle是Python內嵌的繪製線、圓以及其他形狀(包括文本)的圖形模塊。 一個Turtle實際上是一個對象,在導入Turtle模塊時,就創建了對象,然後,可以調用Turtle對象的各種方法完成不同的操作。 當創建一個Turtle對象時,它的位置 ...
  • 寫在前面的話 適用讀者:有一定經驗的,本文不適合初學者,因為可能不能理解我在說什麼 文章思路:不會一開始就像別的博客文章那樣,Bean 的生命周期,源碼解讀(給你貼一大堆的源碼)。個人覺得應該由問題驅動,為什麼為出現 BeanFactory ,為什麼會有生命周期。 正文 一開始我們使用 bean 都 ...
  • 1、Eureka服務端集群開發 1、先創建一個父工程 若是不是普通demo,還有別的配置時,需要註意若是服務開不起來可能就是父類依賴中可能會需要<dependencyManagement>這個標簽。 2、再創建兩個子工程 1、導入依賴 <dependency> <groupId>org.spring ...
一周排行
  • 1. 雲停車 (開篇)2. 雲停車 (硬體篇)3. 雲停車 (業務篇)4. 雲停車 (平臺篇)5. 雲停車 (源碼篇) 網址:http://42.194.142.223:8091/Home 賬戶:admin 密碼:[email protected] 在場記錄 出入記錄 月租車繳費記錄 臨時車繳費記錄 車場 ...
  • 1.RabbitMQ簡介 因為RabbitMQ是基於開源的AMQP協議來實現的,所以在瞭解MQ時候,首先我們來瞭解下AMQP協議。AMQP,即Advanced Message Queuing Protocol,一個提供統一消息服務的應用層標準高級消息隊列協議,是應用層協議的一個開放標準,為面向消息的 ...
  • 1.AMQP Messaging中的基本概念 Broker:接收和分發消息的應用,RabbitMQ Server就是Message Broker。Virtual Host:出於多租戶和安全因素設計的,把AMQP的基本組件劃分到一個虛擬的分組中,類似於網路中的Namespace概念。當多個不同的用戶使 ...
  • 在顯示或者隱藏視窗的時候,可以利用Windows API中的AnimateWindow函數實現一些特殊的效果。主要的動畫類型有四種:滾動、幻燈片、摺疊或展開和alpha混合漸變。 ##視窗動畫效果 首先定義動畫工具類,引入AnimateWindow函數。 public class WindowsEf ...
  • 泥水佬大佬的地址已經不能下載,所以分享下。 用法很簡單,輸入地址即可。 鏈接:https://pan.baidu.com/s/1OUeybjqY9uGWmxe_ywgwgQ 提取碼:nls9 ...
  • //前提需要 //需要一個 serialPort 工具 可在vs自帶的工具欄中獲得 //源代碼加串口工具地址: //鏈接:https://pan.baidu.com/s/1YbfvdXEmfsJX87D-Jxljyg 提取碼:d32x //記錄用戶打開的串口號 可改為泛型模式 string seri ...
  • 從業務視角還原問題、業務視角抽象問題、二次抽象發現技術問題,作為思考問題的三個還原點。 提煉單點問題解決能力、思考單點問題解決能力,複製、層層遞進思考問題,從深度和廣度出發, 深度以數據作指標,廣度以領域做方向。 腳踏實地的代碼量成長沒有捷徑。 ...
  • Java電子書分類 Java、Spring、SpringBoot、SpringCloud、mybatis、Tomcat、多線程、Git相關、Redis、設計模式、Nginx、Linux、演算法、資料庫、大數據、架構 電子書大概有30G左右 部分電子書截圖 領取方式 加我的微信(s2001sssss)免 ...
  • 一.node啟動js公鑰加密 //需要導入模塊npm install node-forge var arguments = process.argv.splice(2); // console.log('所傳遞的參數是:', arguments); var e = arguments[1]; var ...
  • LeetCode–最長公共首碼 博客說明 文章所涉及的資料來自互聯網整理和個人總結,意在於個人學習和經驗彙總,如有什麼地方侵權,請聯繫本人刪除,謝謝! 說明 leetcode題,14題 最長公共首碼 題目 編寫一個函數來查找字元串數組中的最長公共首碼。 如果不存在公共首碼,返回空字元串 ""。 示例 ...