<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          面試官:HashMap有幾種遍歷方法?推薦使用哪種?

          共 5743字,需瀏覽 12分鐘

           ·

          2021-12-09 19:30

          作者 |?磊哥

          來(lái)源 | Java面試真題解析(ID:aimianshi666)

          轉(zhuǎn)載請(qǐng)聯(lián)系授權(quán)(微信ID:GG_Stone)

          HashMap 的遍歷方法有很多種,不同的 JDK 版本有不同的寫(xiě)法,其中 JDK 8 就提供了 3 種 HashMap 的遍歷方法,并且一舉打破了之前遍歷方法“很臃腫”的尷尬。

          1.JDK 8 之前的遍歷

          JDK 8 之前主要使用 EntrySet 和 KeySet 進(jìn)行遍歷,具體實(shí)現(xiàn)代碼如下。

          1.1 EntrySet 遍歷

          EntrySet 是早期 HashMap 遍歷的主要方法,其實(shí)現(xiàn)代碼如下:

          public?static?void?main(String[]?args)?{
          ????//?創(chuàng)建并賦值?hashmap
          ????HashMap?map?=?new?HashMap()?{{
          ????????put("Java",?"?Java?Value.");
          ????????put("MySQL",?"?MySQL?Value.");
          ????????put("Redis",?"?Redis?Value.");
          ????}};
          ????//?循環(huán)遍歷
          ????for?(Map.Entry?entry?:?map.entrySet())?{
          ????????System.out.println(entry.getKey()?+?":"?+?entry.getValue());
          ????}
          }

          以上程序的執(zhí)行結(jié)果,如下圖所示:

          1.2 KeySet 遍歷

          KeySet 的遍歷方式是循環(huán) Key 內(nèi)容,再通過(guò) map.get(key) 獲取 Value 的值,具體實(shí)現(xiàn)如下:

          public?static?void?main(String[]?args)?{
          ????//?創(chuàng)建并賦值?hashmap
          ????HashMap?map?=?new?HashMap()?{{
          ????????put("Java",?"?Java?Value.");
          ????????put("MySQL",?"?MySQL?Value.");
          ????????put("Redis",?"?Redis?Value.");
          ????}};
          ????//?循環(huán)遍歷
          ????for?(String?key?:?map.keySet())?{
          ????????System.out.println(key?+?":"?+?map.get(key));
          ????}
          }

          以上程序的執(zhí)行結(jié)果,如下圖所示:

          KeySet 性能問(wèn)題

          通過(guò)以上代碼,我們可以看出使用 KeySet 遍歷,其性能是不如 EntrySet 的,因?yàn)?KeySet 其實(shí)循環(huán)了兩遍集合,第一遍循環(huán)是循環(huán) Key,而獲取 Value 有需要使用 map.get(key),相當(dāng)于有循環(huán)了一遍集合,所以 KeySet 循環(huán)不能建議使用,因?yàn)檠h(huán)了兩次,效率比較低。

          1.3 EntrySet 迭代器遍歷

          EntrySet 和 KeySet 除了以上直接循環(huán)外,我們還可以使用它們的迭代器進(jìn)行循環(huán),如 EntrySet 的迭代器實(shí)現(xiàn)代碼如下:

          public?static?void?main(String[]?args)?{
          ????//?創(chuàng)建并賦值?hashmap
          ????HashMap?map?=?new?HashMap()?{{
          ????????put("Java",?"?Java?Value.");
          ????????put("MySQL",?"?MySQL?Value.");
          ????????put("Redis",?"?Redis?Value.");
          ????}};
          ????//?循環(huán)遍歷
          ????Iterator>?iterator?=?map.entrySet().iterator();
          ????while?(iterator.hasNext())?{
          ????????Map.Entry?entry?=?iterator.next();
          ????????System.out.println(entry.getKey()?+?":"?+?entry.getValue());
          ????}
          }

          以上程序的執(zhí)行結(jié)果,如下圖所示:

          1.4 KeySet 迭代器遍歷

          KeySet 也可以使用迭代器的方式進(jìn)行遍歷,實(shí)現(xiàn)代碼如下:

          public?static?void?main(String[]?args)?{
          ????//?創(chuàng)建并賦值?hashmap
          ????HashMap?map?=?new?HashMap()?{{
          ????????put("Java",?"?Java?Value.");
          ????????put("MySQL",?"?MySQL?Value.");
          ????????put("Redis",?"?Redis?Value.");
          ????}};
          ????//?循環(huán)遍歷
          ????Iterator?iterator?=?map.keySet().iterator();
          ????while?(iterator.hasNext())?{
          ????????String?key?=?iterator.next();
          ????????System.out.println(key?+?":"?+?map.get(key));
          ????}
          }

          以上程序的執(zhí)行結(jié)果,如下圖所示:雖然 KeySet 循環(huán)方式不推薦使用,但還是有必要了解一下的。

          1.5 迭代器的作用

          既然能直接遍歷,那為什么還要用迭代器呢?通過(guò)以下例子我們就知道了。

          不使用迭代器刪除

          如果不使用迭代器,假如我們?cè)诒闅v EntrySet 時(shí),在遍歷代碼中刪除元素,代碼的實(shí)現(xiàn)如下:

          public?static?void?main(String[]?args)?{
          ????//?創(chuàng)建并賦值?hashmap
          ????HashMap?map?=?new?HashMap()?{{
          ????????put("Java",?"?Java?Value.");
          ????????put("MySQL",?"?MySQL?Value.");
          ????????put("Redis",?"?Redis?Value.");
          ????}};
          ????//?循環(huán)遍歷
          ????for?(Map.Entry?entry?:?map.entrySet())?{
          ????????if?("Java".equals(entry.getKey()))?{
          ????????????//?刪除此項(xiàng)
          ????????????map.remove(entry.getKey());
          ????????????continue;
          ????????}
          ????????System.out.println(entry.getKey()?+?":"?+?entry.getValue());
          ????}
          }

          以上程序的執(zhí)行結(jié)果,如下圖所示:可以看到,如果在遍歷的代碼中動(dòng)態(tài)刪除元素,非迭代器的方式就會(huì)報(bào)錯(cuò)。

          使用迭代器刪除

          接下來(lái),我們使用迭代器循環(huán) EntrySet,并且在循環(huán)中動(dòng)態(tài)刪除元素,實(shí)現(xiàn)代碼如下:

          public?static?void?main(String[]?args)?{
          ????//?創(chuàng)建并賦值?hashmap
          ????HashMap?map?=?new?HashMap()?{{
          ????????put("Java",?"?Java?Value.");
          ????????put("MySQL",?"?MySQL?Value.");
          ????????put("Redis",?"?Redis?Value.");
          ????}};
          ????//?循環(huán)遍歷
          ????Iterator>?iterator?=?map.entrySet().iterator();
          ????while?(iterator.hasNext())?{
          ????????Map.Entry?entry?=?iterator.next();
          ????????if?("Java".equals(entry.getKey()))?{
          ????????????//?刪除此項(xiàng)
          ????????????iterator.remove();
          ????????????continue;
          ????????}
          ????????System.out.println(entry.getKey()?+?":"?+?entry.getValue());
          ????}
          }

          以上程序的執(zhí)行結(jié)果,如下圖所示:從上述結(jié)果可以看出,使用迭代器的優(yōu)點(diǎn)是可以在循環(huán)的時(shí)候,動(dòng)態(tài)的刪除集合中的元素。而上面非迭代器的方式則不能在循環(huán)的過(guò)程中刪除元素(程序會(huì)報(bào)錯(cuò))。

          2.JDK 8 之后的遍歷

          在 JDK 8 之后 HashMap 的遍歷就變得方便很多了,JDK 8 中包含了以下 3 種遍歷方法:

          • 使用 Lambda 遍歷
          • 使用 Stream 單線程遍歷
          • 使用 Stream 多線程遍歷

          我們分別來(lái)看。

          2.1 Lambda 遍歷

          使用 Lambda 表達(dá)式的遍歷方法實(shí)現(xiàn)代碼如下:

          public?static?void?main(String[]?args)?{
          ????//?創(chuàng)建并賦值?hashmap
          ????HashMap?map?=?new?HashMap()?{{
          ????????put("Java",?"?Java?Value.");
          ????????put("MySQL",?"?MySQL?Value.");
          ????????put("Redis",?"?Redis?Value.");
          ????}};
          ????
          ????//?循環(huán)遍歷
          ????map.forEach((key,?value)?->?{
          ????????System.out.println(key?+?":"?+?value);
          ????});
          }

          以上程序的執(zhí)行結(jié)果,如下圖所示:

          2.2 Stream 單線程遍歷

          Stream 遍歷是先得到 map 集合的 EntrySet,然后再執(zhí)行 forEach 循環(huán),實(shí)現(xiàn)代碼如下:

          public?static?void?main(String[]?args)?{
          ????//?創(chuàng)建并賦值?hashmap
          ????HashMap?map?=?new?HashMap()?{{
          ????????put("Java",?"?Java?Value.");
          ????????put("MySQL",?"?MySQL?Value.");
          ????????put("Redis",?"?Redis?Value.");
          ????}};
          ????
          ????//?循環(huán)遍歷
          ????map.entrySet().stream().forEach((entry)?->?{
          ????????System.out.println(entry.getKey()?+?":"?+?entry.getValue());
          ????});
          }

          以上程序的執(zhí)行結(jié)果,如下圖所示:

          2.3 Stream 多線程遍歷

          Stream 多線程的遍歷方式和上一種遍歷方式類(lèi)似,只是多執(zhí)行了一個(gè) parallel 并發(fā)執(zhí)行的方法,此方法會(huì)根據(jù)當(dāng)前的硬件配置生成對(duì)應(yīng)的線程數(shù),然后再進(jìn)行遍歷操作,實(shí)現(xiàn)代碼如下:

          public?static?void?main(String[]?args)?{
          ????//?創(chuàng)建并賦值?hashmap
          ????HashMap?map?=?new?HashMap()?{{
          ????????put("Java",?"?Java?Value.");
          ????????put("MySQL",?"?MySQL?Value.");
          ????????put("Redis",?"?Redis?Value.");
          ????}};
          ????//?循環(huán)遍歷
          ????map.entrySet().stream().parallel().forEach((entry)?->?{
          ????????System.out.println(entry.getKey()?+?":"?+?entry.getValue());
          ????});
          }

          以上程序的執(zhí)行結(jié)果,如下圖所示:注意上述圖片的執(zhí)行結(jié)果,可以看出當(dāng)前執(zhí)行結(jié)果和之前的所有遍歷結(jié)果都不一樣(打印元素的順序不一樣),因?yàn)槌绦蚴遣l(fā)執(zhí)行的,所以沒(méi)有辦法保證元素的執(zhí)行順序和打印順序,這就是并發(fā)編程的特點(diǎn)。

          推薦使用哪種遍歷方式?

          不同的場(chǎng)景推薦使用的遍歷方式是不同的,例如,如果是 JDK 8 之后的開(kāi)發(fā)環(huán)境,推薦使用 Stream 的遍歷方式,因?yàn)樗銐蚝?jiǎn)潔;而如果在遍歷的過(guò)程中需要?jiǎng)討B(tài)的刪除元素,那么推薦使用迭代器的遍歷方式;如果在遍歷的時(shí)候,比較在意程序的執(zhí)行效率,那么推薦使用 Stream 多線程遍歷的方式,因?yàn)樗銐蚩臁K赃@個(gè)問(wèn)題的答案是不固定的,我們需要知道每種遍歷方法的優(yōu)缺點(diǎn),再根據(jù)不同的場(chǎng)景靈活變通。

          總結(jié)

          本文介紹了 7 種 HashMap 的遍歷方式,其中 JDK 8 之前主要使用 EntrySet 和 KeySet 的遍歷方式,而 KeySet 的遍歷方式性能比較低,一般不推薦使用。然而在 JDK 8 之后遍歷方式就有了新的選擇,可以使用比較簡(jiǎn)潔的 Lambda 遍歷,也可以使用性能比較高的 Stream 多線程遍歷。

          是非審之于己,毀譽(yù)聽(tīng)之于人,得失安之于數(shù)。

          博主介紹:80 后程序員,寫(xiě)博客這件事“堅(jiān)持”了 11 年,愛(ài)好:讀書(shū)、慢跑、羽毛球。

          我的公眾號(hào):Java面試真題解析

          個(gè)人微信:GG_Stone,歡迎圍觀朋友圈,做個(gè)點(diǎn)贊只交。




          推薦閱讀:

          如出一轍。。。

          Java 中的監(jiān)控與管理原理概述

          《吃透 MQ 系列》之 Kafka 架構(gòu)設(shè)計(jì)的任督二脈

          《吃透 MQ 系列》之扒開(kāi) Kafka 的神秘面紗

          《吃透 MQ 系列》之 Kafka 存儲(chǔ)選型的奧秘



          關(guān)號(hào)互聯(lián)網(wǎng)全棧架構(gòu)價(jià)。

          瀏覽 36
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          評(píng)論
          圖片
          表情
          推薦
          點(diǎn)贊
          評(píng)論
          收藏
          分享

          手機(jī)掃一掃分享

          分享
          舉報(bào)
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  琪琪先锋 torrent magnet 国产精品久久久久久久久久久久久免费看 | 欧美性爱大香蕉 | 久久久无码精品成人A片小说 | 操逼网网 | 日本A∨ |