最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

解讀為什么不推薦使用keySet()進(jìn)行遍歷HashMap

 更新時(shí)間:2025年05月15日 14:26:39   作者:程序員小2  
這篇文章主要介紹了我為什么不推薦使用keySet()進(jìn)行遍歷HashMap的問題,具有很好的參考價(jià)值,希望對大家有所幫助,如有錯(cuò)誤或未考慮完全的地方,望不吝賜教

不推薦使用keySet()進(jìn)行遍歷HashMap

HashMap相信所有學(xué)Java的都一定不會感到陌生,作為一個(gè)非常重用且非常實(shí)用的Java提供的容器,它在我們的代碼里面隨處可見。因此遍歷操作也是我們經(jīng)常會使用到的。

HashMap的遍歷方式現(xiàn)如今有非常多種:

  • 使用迭代器(Iterator)。
  • 使用 keySet() 獲取鍵的集合,然后通過增強(qiáng)的 for 循環(huán)遍歷鍵。
  • 使用 entrySet() 獲取鍵值對的集合,然后通過增強(qiáng)的 for 循環(huán)遍歷鍵值對。
  • 使用 Java 8+ 的 Lambda 表達(dá)式和流。

以上遍歷方式的孰優(yōu)孰劣,在《阿里巴巴開發(fā)手冊》中寫道:

這里推薦使用的是entrySet進(jìn)行遍歷,在Java8中推薦使用Map.forEach()。給出的理由是遍歷次數(shù) 上的不同。

  • keySet遍歷,需要經(jīng)過兩次 遍歷。
  • entrySet遍歷,只需要一次 遍歷。

其中keySet遍歷了兩次,一次是轉(zhuǎn)為Iterator對象,另一次是從hashMap中取出key所對應(yīng)的value。

其中后面一段話很好理解,但是前面這句話卻有點(diǎn)繞,為什么轉(zhuǎn)換成了Iterator遍歷了一次?

我查閱了各個(gè)平臺對HashMap的遍歷,其中都沒有或者原封不動的照搬上句話。(當(dāng)然也可能是我沒有查閱到靠譜的文章,歡迎指正)

keySet如何遍歷了兩次

我們首先寫一段代碼,使用keySet遍歷Map。

public class Test {
    public static void main(String[] args) {
        Map<String, String> map = new HashMap<>();
        map.put("k1", "v1");
        map.put("k2", "v2");
        map.put("k3", "v3");
        for (String key : map.keySet()) {
            String value = map.get(key);
            System.out.println(key + ":" + value);
        }
    }

}

運(yùn)行結(jié)果顯而易見的是

k1:v1
k2:v2
k3:v3

兩次遍歷,第一次遍歷所描述的是轉(zhuǎn)為Iterator對象我們好像沒有從代碼中看見,我們看到的后面所描述的遍歷,也就是遍歷map,keySet()所返回的Set集合中的key,然后去HashMap中拿取value的。

Iterator對象呢?如何遍歷轉(zhuǎn)換為Iterator對象的呢?

首先我們這種遍歷方式大家都應(yīng)該知道是叫:增強(qiáng)for循環(huán),for-each

這是一種Java的語法糖~。

我們可以通過反編譯,或者直接通過Idea在class文件中查看對應(yīng)的Class文件

public class Test {
    public Test() {
    }

    public static void main(String[] args) {
        Map<String, String> map = new HashMap();
        map.put("k1", "v1");
        map.put("k2", "v2");
        map.put("k3", "v3");
        Iterator var2 = map.keySet().iterator();

        while(var2.hasNext()) {
            String key = (String)var2.next();
            String value = (String)map.get(key);
            System.out.println(key + ":" + value);
        }

    }
}

和我們編寫的是存在差異的,其中我們可以看到其中通過map.keySet().iterator()獲取到了我們所需要看見的Iterator對象。

那么它又是怎么轉(zhuǎn)換成的呢?為什么需要遍歷呢?我們查看iterator()方法

1 iterator()

發(fā)現(xiàn)是Set定義的一個(gè)接口。返回此集合中元素的迭代器

2 HashMap.KeySet#iterator()

我們查看HashMap中keySet類對該方法的實(shí)現(xiàn)。

final class KeySet extends AbstractSet<K> {
    public final int size()                 { return size; }
    public final void clear()               { HashMap.this.clear(); }
    public final Iterator<K> iterator()     { return new KeyIterator(); }
    public final boolean contains(Object o) { return containsKey(o); }
    public final boolean remove(Object key) {
        return removeNode(hash(key), key, null, false, true) != null;
    }
    public final Spliterator<K> spliterator() {
        return new KeySpliterator<>(HashMap.this, 0, -1, 0, 0);
    }
    public final void forEach(Consumer<? super K> action) {
        Node<K,V>[] tab;
        if (action == null)
            throw new NullPointerException();
        if (size > 0 && (tab = table) != null) {
            int mc = modCount;
            for (int i = 0; i < tab.length; ++i) {
                for (Node<K,V> e = tab[i]; e != null; e = e.next)
                    action.accept(e.key);
            }
            if (modCount != mc)
                throw new ConcurrentModificationException();
        }
    }
}

其中的iterator()方法返回的是一個(gè)KeyIterator對象,那么究竟是在哪里進(jìn)行了遍歷呢?我們接著往下看去。

3 HashMap.KeyIterator

final class KeyIterator extends HashIterator
    implements Iterator<K> {
    public final K next() { return nextNode().key; }
}

這個(gè)類也很簡單:

  • 繼承了HashIterator類。
  • 實(shí)現(xiàn)了Iterator接口。
  • 一個(gè)next()方法。

還是沒有看見哪里進(jìn)行了遍歷,那么我們繼續(xù)查看HashIterator

4 HashMap.HashIterator

abstract class HashIterator {
    Node<K,V> next;        // next entry to return
    Node<K,V> current;     // current entry
    int expectedModCount;  // for fast-fail
    int index;             // current slot

    HashIterator() {
        expectedModCount = modCount;
        Node<K,V>[] t = table;
        current = next = null;
        index = 0;
        if (t != null && size > 0) { // advance to first entry
            do {} while (index < t.length && (next = t[index++]) == null);
        }
    }

    public final boolean hasNext() {
        return next != null;
    }

    final Node<K,V> nextNode() {
        Node<K,V>[] t;
        Node<K,V> e = next;
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        if (e == null)
            throw new NoSuchElementException();
        if ((next = (current = e).next) == null && (t = table) != null) {
            do {} while (index < t.length && (next = t[index++]) == null);
        }
        return e;
    }

    public final void remove() {
        Node<K,V> p = current;
        if (p == null)
            throw new IllegalStateException();
        if (modCount != expectedModCount)
            throw new ConcurrentModificationException();
        current = null;
        K key = p.key;
        removeNode(hash(key), key, null, false, false);
        expectedModCount = modCount;
    }
}

我們可以發(fā)現(xiàn)這個(gè)構(gòu)造器中存在了一個(gè)do-while循環(huán)操作,目的是找到一個(gè)第一個(gè)不為空的entry。

HashIterator() {
    expectedModCount = modCount;
    Node<K,V>[] t = table;
    current = next = null;
    index = 0;
    if (t != null && size > 0) { // advance to first entry
        do {} while (index < t.length && (next = t[index++]) == null);
    }
}

KeyIterator是extendHashIterator對象的。這里涉及到了繼承的相關(guān)概念,大家忘記的可以找相關(guān)的文章看看,或者我也可以寫一篇~~dog。

例如兩個(gè)類

public class Father {

    public Father(){
        System.out.println("father");
    }
}
public class Son extends Father{

    public static void main(String[] args) {
        Son son = new Son();
    }
}

創(chuàng)建Son對象的同時(shí),會執(zhí)行Father構(gòu)造器。也就會打印出father這句話。

那么這個(gè)循環(huán)操作就是我們要找的循環(huán)操作了。

總結(jié)

使用keySet遍歷,其實(shí)內(nèi)部是使用了對應(yīng)的iterator()方法。

  • iterator()方法是創(chuàng)建了一個(gè)KeyIterator對象。
  • KeyIterator對象extendHashIterator對象。
  • HashIterator對象的構(gòu)造方法中,會遍歷找到第一個(gè)不為空的entry

keySet->iterator()->KeyIterator->HashIterator

以上為個(gè)人經(jīng)驗(yàn),希望能給大家一個(gè)參考,也希望大家多多支持腳本之家。

相關(guān)文章

  • Java 漢字獲取拼音或首字母工具類代碼分析

    Java 漢字獲取拼音或首字母工具類代碼分析

    在本篇內(nèi)容里小編給大家分享的是一篇關(guān)于Java 漢字獲取拼音或首字母工具類知識點(diǎn)內(nèi)容,有需要的朋友們可以學(xué)習(xí)參考下。
    2021-06-06
  • Spring Jms 模塊案例講解

    Spring Jms 模塊案例講解

    本文詳細(xì)介紹了Spring-JMS模塊,包括其核心功能和作用,通過ActiveMQ作為消息代理,提供了一個(gè)基于XML配置的完整示例,幫助開發(fā)者快速掌握Spring-JMS的使用方式,感興趣的朋友一起看看吧
    2025-02-02
  • Java統(tǒng)計(jì)代碼的執(zhí)行時(shí)間的N種方法

    Java統(tǒng)計(jì)代碼的執(zhí)行時(shí)間的N種方法

    在日常開發(fā)中經(jīng)常需要測試一些代碼的執(zhí)行時(shí)間,但又不想使用向 JMH(Java?Microbenchmark Harness,Java 微基準(zhǔn)測試套件)這么重的測試框架,所以本文就匯總了一些 Java 中比較常用的執(zhí)行時(shí)間統(tǒng)計(jì)方法,總共包含以下 6 種,需要的朋友可以參考下
    2022-08-08
  • Java利用redis實(shí)現(xiàn)防止接口重復(fù)提交

    Java利用redis實(shí)現(xiàn)防止接口重復(fù)提交

    本文主要為大家詳細(xì)介紹了Java如何利用redis實(shí)現(xiàn)防止接口重復(fù)提交,文中通過示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下
    2022-11-11
  • Spring框架中的跨域CORS配置的完整教學(xué)

    Spring框架中的跨域CORS配置的完整教學(xué)

    CORS(Cross-Origin?Resource?Sharing,跨域資源共享)是一種機(jī)制,允許在瀏覽器中向不同源(域名、協(xié)議或端口不同)的服務(wù)器發(fā)起請求,下面我們就來看看如何在Spring框架配置跨域CORS吧
    2025-10-10
  • 詳解Java中Array和ArrayList的比較和轉(zhuǎn)換

    詳解Java中Array和ArrayList的比較和轉(zhuǎn)換

    在 Java 編程中,arrays 和 arraylists 都是基本的數(shù)據(jù)結(jié)構(gòu),用來存放數(shù)據(jù)集合,雖然兩者的用途一樣,但是它們的特點(diǎn)極大地影響應(yīng)用的性能和靈活性,本文探討 arrays 和 arraylists 的重要特性,它們各自的強(qiáng)項(xiàng)和弱點(diǎn),,需要的朋友可以參考下
    2023-08-08
  • spring聲明式事務(wù)@Transactional開發(fā)常犯的幾個(gè)錯(cuò)誤及最新解決方案

    spring聲明式事務(wù)@Transactional開發(fā)常犯的幾個(gè)錯(cuò)誤及最新解決方案

    使用聲明式事務(wù)@Transactional進(jìn)行事務(wù)一致性的管理,在開發(fā)過程中,發(fā)現(xiàn)很多開發(fā)同學(xué)都用錯(cuò)了spring聲明式事務(wù)@Transactional或使用不規(guī)范,導(dǎo)致出現(xiàn)各種事務(wù)問題,這篇文章主要介紹了spring聲明式事務(wù)@Transactional開發(fā)常犯的幾個(gè)錯(cuò)誤及解決辦法,需要的朋友可以參考下
    2024-02-02
  • Java使用Sftp和Ftp實(shí)現(xiàn)對文件的上傳和下載

    Java使用Sftp和Ftp實(shí)現(xiàn)對文件的上傳和下載

    這篇文章主要介紹了Java使用Sftp和Ftp實(shí)現(xiàn)對文件的上傳和下載,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2021-03-03
  • Graphics2D 寫圖片中文亂碼問題及解決

    Graphics2D 寫圖片中文亂碼問題及解決

    這篇文章主要介紹了Graphics2D 寫圖片中文亂碼問題及解決方案,具有很好的參考價(jià)值,希望對大家有所幫助。如有錯(cuò)誤或未考慮完全的地方,望不吝賜教
    2021-11-11
  • Spring?Security重寫AuthenticationManager實(shí)現(xiàn)賬號密碼登錄或者手機(jī)號碼登錄

    Spring?Security重寫AuthenticationManager實(shí)現(xiàn)賬號密碼登錄或者手機(jī)號碼登錄

    本文主要介紹了Spring?Security重寫AuthenticationManager實(shí)現(xiàn)賬號密碼登錄或者手機(jī)號碼登錄,文中通過示例代碼介紹的非常詳細(xì),需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2025-08-08

最新評論

房产| 沐川县| 宾川县| 肃南| 宣化县| 广元市| 宜君县| 尼勒克县| 苏尼特左旗| 含山县| 乳山市| 宿迁市| 商河县| 偃师市| 东宁县| 贺兰县| 盐亭县| 柳江县| 黑河市| 郓城县| 上犹县| 清涧县| 泸州市| 哈密市| 合作市| 莱阳市| 横山县| 台湾省| 徐州市| 山阳县| 常州市| 胶州市| 巴南区| 冷水江市| 富蕴县| 双牌县| 拜城县| 镇沅| 萝北县| 赣榆县| 尉犁县|