Java 中九種 Map 的遍歷方式,你一般用的是哪種呢?

来源:https://www.cnblogs.com/zi-you/archive/2022/11/23/16920311.html
-Advertisement-
Play Games

日常工作中 Map 絕對是我們 Java 程式員高頻使用的一種數據結構,那 Map 都有哪些遍歷方式呢?這篇文章阿粉就帶大家看一下,看看你經常使用的是哪一種。 通過 entrySet 來遍歷 1、通過 for 和 map.entrySet() 來遍歷 第一種方式是採用 for 和 Map.Entry ...


日常工作中 Map 絕對是我們 Java 程式員高頻使用的一種數據結構,那 Map 都有哪些遍歷方式呢?這篇文章阿粉就帶大家看一下,看看你經常使用的是哪一種。

通過 entrySet 來遍歷

1、通過 formap.entrySet() 來遍歷

第一種方式是採用 forMap.Entry 的形式來遍歷,通過遍歷 map.entrySet() 獲取每個 entrykeyvalue,代碼如下。這種方式一般也是阿粉使用的比較多的一種方式,沒有什麼花里胡哨的用法,就是很朴素的獲取 map 的 keyvalue

public static void testMap1(Map<Integer, Integer> map) {
    long sum = 0;
    for (Map.Entry<Integer, Integer> entry : map.entrySet()) {
      sum += entry.getKey() + entry.getValue();
    }
    System.out.println(sum);
  }

看過 HashMap 源碼的同學應該會發現,這個遍歷方式在源碼中也有使用,如下圖所示,

putMapEntries 方法在我們調用 putAll 方法的時候會用到。

2、通過 forIteratormap.entrySet() 來遍歷

我們第一個方法是直接通過 forentrySet() 來遍歷的,這次我們使用 entrySet() 的迭代器來遍歷,代碼如下。

public static void testMap2(Map<Integer, Integer> map) {
    long sum = 0;
    for (Iterator<Map.Entry<Integer, Integer>> entries = map.entrySet().iterator(); entries.hasNext(); ) {
      Map.Entry<Integer, Integer> entry = entries.next();
      sum += entry.getKey() + entry.getValue();
    }
    System.out.println(sum);
  }

3、通過 whileIteratormap.entrySet() 來遍歷

上面的迭代器是使用 for 來遍歷,那我們自然可以想到還可以用 while 來進行遍歷,所以代碼如下所示。

 public static void testMap3(Map<Integer, Integer> map) {
    Iterator<Map.Entry<Integer, Integer>> it = map.entrySet().iterator();
    long sum = 0;
    while (it.hasNext()) {
      Map.Entry<Integer, Integer> entry = it.next();
      sum += entry.getKey() + entry.getValue();
    }
    System.out.println(sum);
  }

這種方法跟上面的方法類似,只不過迴圈從 for 換成了 while,日常我們在開發的時候,很多場景都可以將 forwhile 進行替換。2 和 3 都使用迭代器 Iterator,通過迭代器的 next(),方法來獲取下一個對象,依次判斷是否有 next

通過 keySet 來遍歷

上面的這三種方式雖然代碼的寫法不同,但是都是通過遍歷 map.entrySet() 來獲取結果的,殊途同歸。接下來我們看另外的一組。

4、通過 for 和 map.keySet() 來遍歷

前面的遍歷是通過 map.entrySet() 來遍歷,這裡我們通過 map.keySet() 來遍歷,顧名思義前者是保存 entry 的集合,後者是保存 key 的集合,遍歷的代碼如下,因為是 key 的集合,所以如果想要獲取 key 對應的 value 的話,還需要通過 map.get(key) 來獲取。

public static void testMap4(Map<Integer, Integer> map) {
    long sum = 0;
    for (Integer key : map.keySet()) {
      sum += key + map.get(key);
    }
    System.out.println(sum);
  }

5、通過 forIteratormap.keySet() 來遍歷

public static void testMap5(Map<Integer, Integer> map) {
    long sum = 0;
    for (Iterator<Integer> key = map.keySet().iterator(); key.hasNext(); ) {
      Integer k = key.next();
      sum += k + map.get(k);
    }
    System.out.println(sum);
  }

6、通過 whileIteratormap.keySet() 來遍歷

public static void testMap6(Map<Integer, Integer> map) {
    Iterator<Integer> it = map.keySet().iterator();
    long sum = 0;
    while (it.hasNext()) {
      Integer key = it.next();
      sum += key + map.get(key);
    }
    System.out.println(sum);
  }

我們可以看到這種方式相對於 map.entrySet() 方式,多了一步 get 的操作,這種場景比較適合我們只需要 key 的場景,如果也需要使用 value 的場景不建議使用 map.keySet() 來進行遍歷,因為會多一步 map.get() 的操作。

Java 8 的遍歷方式

註意下麵的幾個遍歷方法都是是 JDK 1.8 引入的,如果使用的 JDK 版本不是 1.8 以及之後的版本的話,是不支持的。

7、通過 map.forEach() 來遍歷

JDK 中的 forEach 方法,使用率也挺高的。

public static void testMap7(Map<Integer, Integer> map) {
    final long[] sum = {0};
    map.forEach((key, value) -> {
      sum[0] += key + value;
    });
    System.out.println(sum[0]);
  }

該方法被定義在 java.util.Map#forEach 中,並且是通過 default 關鍵字來標識的,如下圖所示。這裡提個問題,為什麼要使用 default 來標識呢?歡迎把你的答案寫在評論區。

8、Stream 遍歷

public static void testMap8(Map<Integer, Integer> map) {
    long sum = map.entrySet().stream().mapToLong(e -> e.getKey() + e.getValue()).sum();
    System.out.println(sum);
  }

9、ParallelStream 遍歷

 public static void testMap9(Map<Integer, Integer> map) {
    long sum = map.entrySet().parallelStream().mapToLong(e -> e.getKey() + e.getValue()).sum();
    System.out.println(sum);
  }

這兩種遍歷方式都是 JDK 8Stream 遍歷方式,stream 是普通的遍歷,parallelStream 是並行流遍歷,在某些場景會提升性能,但是也不一定。

測試代碼

上面的遍歷方式有了,那麼我們在日常開發中到底該使用哪一種呢?每一種的性能是怎麼樣的呢?為此阿粉這邊通過下麵的代碼,我們來測試一下每種方式的執行時間。

public static void main(String[] args) {
   int outSize = 1;
    int mapSize = 200;
    Map<Integer, Integer> map = new HashMap<>(mapSize);
    for (int i = 0; i < mapSize; i++) {
      map.put(i, i);
    }
    System.out.println("---------------start------------------");
    long totalTime = 0;
    for (int size = outSize; size > 0; size--) {
      long startTime = System.currentTimeMillis();
      testMap1(map);
      totalTime += System.currentTimeMillis() - startTime;
    }
    System.out.println("testMap1 avg time is :" + (totalTime / outSize));
		// 省略其他方法,代碼跟上面一致
}

為了避免一些干擾,這裡通過外層的 for 來進行多次計算,然後求平均值,當我們的參數分別是 outSize = 1,mapSize = 200 的時候,測試的結果如下

當隨著我們增大 mapSize 的時候,我們會發現,後面幾個方法的性能是逐漸上升的。

總結

從上面的例子來看,當我們的集合數量很少的時候,基本上普通的遍歷就可以搞定,不需要使用 JDK 8 的高級 API 來進行遍歷,當我們的集合數量較大的時候,就可以考慮採用 JDK 8forEach 或者 Stream 來進行遍歷,這樣的話效率更高。在普通的遍歷方法中 entrySet() 的方法要比使用 keySet() 的方法好。

本文來自博客園,作者:zi-you,轉載請註明原文鏈接:https://www.cnblogs.com/zi-you/p/16920311.html


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

-Advertisement-
Play Games
更多相關文章
  • 情景說明: 之前用 vue 做數據綁定更新時,發現一個莫名奇妙的問題。 我在 vue 實例中聲明瞭一個數組屬性如 books: [],在非同步請求的回調函數中使用 this.books = res.data.data; 進行數據更新,更新步驟後面緊跟著列印了 console.log(this.book ...
  • webpack優化主要有三個方面:1.提高構建速度,2.減少打包體積,3.優化用戶體驗 提高構建速度: 啟用多線程 thread-loader 使用thread-loader插件可以啟用多線程進行構建。 使用緩存 cache-loader 使用cache-loader插件可以對構建進行緩存,加快後續 ...
  • 前言: 昨天咱們已經把貪吃蛇的頁面寫好了,今天咱們來寫 TS 部分 TS 我們要用面向對象的形式去編寫我們的功能,所以我們要以一個功能去定義一個對象 把這個項目分成幾個模塊,也就是幾個對象功能 Food類(食物類): 寫對象的前提,我們要去定義類,用類去創建對象 首先我們直接 class 一個 Fo ...
  • 如果在後端django里配置了django-cors-headers,前端還是報Access to XMLHttpRequest at‘httplocalhost這樣的跨域問題,可以通過以下的方式解決! 通過配置vue.config.js、proxy 實踐解決跨域方法(基於封裝好的axios,非原生 ...
  • 寫在前面的話 大多數標簽在展示文本內容的時候都會預設把文本中的空白和換行符去掉,這的確大大的使得文本的排版更加美觀了,也怎加了區域的利用率,可是就有一些需求是需要原原本本的展示出原汁原味的文本格式。那該如何展示出文本的內在格式呢?無外乎把不該去掉的空格展示出來,把不該合併的換行讓它換行起來。 那麼 ...
  • 遇到了一個 Spring Boot 3 整合 MyBatis 的問題,然後解決了。當然,這其實不是個大問題,只是自己編碼時遇到了,然後總結總結分享一下。如果有遇到類似問題的,可以參考一下。 交代一下背景 最近在熟悉 Spring Boot 3 版本的代碼,開發過程中遇到了一些小坑,不過很快都解決了。 ...
  • 最近在學習做微服務的項目,在參考他人的微服務項目時,發現資料庫表所對應的實體類都會實現Serializable介面,以往做的項目中並沒有遇到過,也沒有實現過這個介面,所以好奇實體類為什麼需要實現該介面,在查閱相關博客後,進行了總結記錄 原文鏈接:【java】java實體類為什麼要實現Serializ ...
  • # 1.列表的格式 # [數據1,數據2,數據3,···] # 列表 可變數據類型 # 列表可以存儲多個數據,數據之間的逗號以英文逗號分隔 # 列表可以存儲不同類型數據,但一般存儲同一數據類型,這樣便於操作 # list_name = [] # 定義了一個空的列表 # 定義了一個有數據的列表 # 可 ...
一周排行
    -Advertisement-
    Play Games
  • .Net8.0 Blazor Hybird 桌面端 (WPF/Winform) 實測可以完整運行在 win7sp1/win10/win11. 如果用其他工具打包,還可以運行在mac/linux下, 傳送門BlazorHybrid 發佈為無依賴包方式 安裝 WebView2Runtime 1.57 M ...
  • 目錄前言PostgreSql安裝測試額外Nuget安裝Person.cs模擬運行Navicate連postgresql解決方案Garnet為什麼要選擇Garnet而不是RedisRedis不再開源Windows版的Redis是由微軟維護的Windows Redis版本老舊,後續可能不再更新Garne ...
  • C#TMS系統代碼-聯表報表學習 領導被裁了之後很快就有人上任了,幾乎是無縫銜接,很難讓我不想到這早就決定好了。我的職責沒有任何變化。感受下來這個系統封裝程度很高,我只要會調用方法就行。這個系統交付之後不會有太多問題,更多應該是做小需求,有大的開發任務應該也是第二期的事,嗯?怎麼感覺我變成運維了?而 ...
  • 我在隨筆《EAV模型(實體-屬性-值)的設計和低代碼的處理方案(1)》中介紹了一些基本的EAV模型設計知識和基於Winform場景下低代碼(或者說無代碼)的一些實現思路,在本篇隨筆中,我們來分析一下這種針對通用業務,且只需定義就能構建業務模塊存儲和界面的解決方案,其中的數據查詢處理的操作。 ...
  • 對某個遠程伺服器啟用和設置NTP服務(Windows系統) 打開註冊表 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\W32Time\TimeProviders\NtpServer 將 Enabled 的值設置為 1,這將啟用NTP伺服器功 ...
  • title: Django信號與擴展:深入理解與實踐 date: 2024/5/15 22:40:52 updated: 2024/5/15 22:40:52 categories: 後端開發 tags: Django 信號 松耦合 觀察者 擴展 安全 性能 第一部分:Django信號基礎 Djan ...
  • 使用xadmin2遇到的問題&解決 環境配置: 使用的模塊版本: 關聯的包 Django 3.2.15 mysqlclient 2.2.4 xadmin 2.0.1 django-crispy-forms >= 1.6.0 django-import-export >= 0.5.1 django-r ...
  • 今天我打算整點兒不一樣的內容,通過之前學習的TransformerMap和LazyMap鏈,想搞點不一樣的,所以我關註了另外一條鏈DefaultedMap鏈,主要調用鏈為: 調用鏈詳細描述: ObjectInputStream.readObject() DefaultedMap.readObject ...
  • 後端應用級開發者該如何擁抱 AI GC?就是在這樣的一個大的浪潮下,我們的傳統的應用級開發者。我們該如何選擇職業或者是如何去快速轉型,跟上這樣的一個行業的一個浪潮? 0 AI金字塔模型 越往上它的整個難度就是職業機會也好,或者說是整個的這個運作也好,它的難度會越大,然後越往下機會就會越多,所以這是一 ...
  • @Autowired是Spring框架提供的註解,@Resource是Java EE 5規範提供的註解。 @Autowired預設按照類型自動裝配,而@Resource預設按照名稱自動裝配。 @Autowired支持@Qualifier註解來指定裝配哪一個具有相同類型的bean,而@Resourc... ...