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

Java源碼解析HashMap的keySet()方法

 更新時間:2019年01月09日 10:25:02   作者:李燦輝  
今天小編就為大家分享一篇關(guān)于Java源碼解析HashMap的keySet()方法,小編覺得內(nèi)容挺不錯的,現(xiàn)在分享給大家,具有很好的參考價值,需要的朋友一起跟隨小編來看看吧

HashMap的keySet()方法比較簡單,作用是獲取HashMap中的key的集合。雖然這個方法十分簡單,似乎沒有什么可供分析的,但真正看了源碼,發(fā)現(xiàn)自己還是有很多不懂的地方。下面是keySet的代碼。

  public Set<K> keySet() {
    Set<K> ks = keySet;
    if (ks == null) {
      ks = new KeySet();
      keySet = ks;
    }
    return ks;
  }

從代碼中了解到,第一次調(diào)用keySet方法時,keySet屬性是null,然后進(jìn)行了初始化,再將keySet屬性返回。也就是說,HashMap里并不會隨著put和remove的進(jìn)行也維護(hù)一個keySet集合,而是在第一次調(diào)用keySet方法時,才給keySet屬性初始化。

按照自己以往的理解,以為keySet返回的是一個集合,集合里面保存了HashMap的所有的Key。因為有了中先入為主的印象,所以讀源碼時,才感覺源碼很奇怪。從源碼中可以看到,初始化時,只是創(chuàng)建了一個KeySet類的對象,并沒有把HashMap的key都加入進(jìn)來,方法就返回了。除了自己以往的理解外,還有一個現(xiàn)象,讓我堅信這時HashMap的key已經(jīng)加入到keySet了,那就是在調(diào)試代碼過程中IDE給出的調(diào)試信息。如下圖。從圖中可以看出,創(chuàng)建完成KeySet()后,調(diào)試信息就已經(jīng)可以顯示出,ks中有2個元素了。這個信息更加堅定了自己之前的理解。

那么,HashMap的key是什么時候加入到keySet集合中的呢?順著這個思路,我進(jìn)行了一步一步的分析。自己看了KeySet類的構(gòu)造函數(shù),發(fā)現(xiàn)只有默認(rèn)構(gòu)造函數(shù)。那么我想,如果沒有在KeySet構(gòu)造函數(shù)里把HashMap的key加入進(jìn)來,那么就有可能是在KeySet的父類的構(gòu)造函數(shù)中加入進(jìn)來的。然后,自己找遍了KeySet類的父類的構(gòu)造函數(shù),發(fā)現(xiàn)都是空實現(xiàn),并沒有任何加入HashMap的key的操作。這到底是怎么回事呢?

其實HashMap的key并沒有加入到keySet集合中,而是在遍歷的時候,使用迭代器對key進(jìn)行的遍歷。這是結(jié)論。下面我們看一下原因和過程。

首先看一下KeySet類的代碼,如下圖。可以看到,KeySet類中的迭代器函數(shù),返回的是一個KeyIterator類的對象。它的next方法返回的是HashIterator的nextNode的key。也就是說,當(dāng)使用迭代器遍歷set內(nèi)的元素時,KeySet類的迭代器,會保證能夠依次獲取到HashMap的節(jié)點的key值,這就是我們遍歷keySet的過程的實質(zhì)。

  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();
      }
    }
  }
  final class KeyIterator extends HashIterator
    implements Iterator<K> {
    public final K next() { return nextNode().key; }
  }
  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;
    }
  }

那么,這里我們可以思考這么一個問題。通過HashMap的keySet獲取到keySet后,難道只能用迭代器遍歷嗎?keySet方法不把HashMap的key都加入到set中,那么調(diào)用者使用for(int i = 0; i < size; i ++)的方式遍歷時,豈不是無法遍歷set中的key了嗎?是的,確實是的。keySet確實沒有把key加入到set中,另外,它不用擔(dān)心調(diào)用者用for(int i = 0; i < size; i ++)的方式遍歷時獲取不到key,因為set根本就沒有set.get(i)這樣類似的方法,要想遍歷set,只能用迭代器,或者使用foreach方式(本質(zhì)還是迭代器)。

這里還有個問題需要解釋,就是在調(diào)試代碼時,既然key沒有加入到set中,那么IDE如何顯示出set中有2個元素這樣的信息的?原來,IDE顯示對象信息時,會調(diào)用對象的toString方法。而集合的toString方法就是顯示出集合中的元素個數(shù)。

這里再思考一步,如果我們在集合的toString方法加上斷點,那么IDE顯示對象信息時,會不先停下來?答案是看情況。記得早些年間使用eclipse調(diào)試代碼時,在toString方法加上斷點后,顯示對象信息時確實會停下來。然而我現(xiàn)在使用的是IDE是idea,idea在這一點上做了優(yōu)化。如果是IDE顯示對象信息調(diào)用的toString方法,那么toString方法的斷點會被跳過,即不生效,但會給出一條提示信息,如下圖。如果程序員主動調(diào)用對象的toString方法,那么,toString方法的斷點會生效,可以正常斷點調(diào)試。

總結(jié)

以上就是這篇文章的全部內(nèi)容了,希望本文的內(nèi)容對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,謝謝大家對腳本之家的支持。如果你想了解更多相關(guān)內(nèi)容請查看下面相關(guān)鏈接

相關(guān)文章

  • Java有效處理異常的三個原則

    Java有效處理異常的三個原則

    Java中異常提供了一種識別及響應(yīng)錯誤情況的一致性機(jī)制,有效地異常處理能使程序更加健壯、易于調(diào)試。那么這篇文章總結(jié)了Java有效處理異常的三個原則,有需要的朋友們可以參考借鑒。
    2016-09-09
  • maven中pom.xml詳細(xì)介紹

    maven中pom.xml詳細(xì)介紹

    最近在學(xué)習(xí)maven,這篇文章主要介紹了maven中pom.xml詳細(xì)介紹,具有一定參考價值,需要的朋友可以了解下。
    2017-11-11
  • SpringBoot整合Swagger教程詳解

    SpringBoot整合Swagger教程詳解

    SpringBoot是一個基于Spring框架的輕量級開源框架,它的出現(xiàn)極大地簡化了Spring應(yīng)用的搭建和開發(fā),本文將介紹如何在SpringBoot中使用Swagger來實現(xiàn)接口文檔的自動生成,感興趣的同學(xué)可以參考一下
    2023-04-04
  • Spring?Boot?中正確地在異步線程中使用?HttpServletRequest的方法

    Spring?Boot?中正確地在異步線程中使用?HttpServletRequest的方法

    文章討論了在Spring?Boot中如何在異步線程中正確使用HttpServletRequest的問題,介紹了Tomcat的請求對象復(fù)用機(jī)制及其對異步線程的影響,并解釋了AsyncContext的作用與局限性,感興趣的朋友一起看看吧
    2025-03-03
  • Spring Data JPA的Audit功能審計數(shù)據(jù)庫的變更

    Spring Data JPA的Audit功能審計數(shù)據(jù)庫的變更

    數(shù)據(jù)庫審計是指當(dāng)數(shù)據(jù)庫有記錄變更時,可以記錄數(shù)據(jù)庫的變更時間和變更人等,這樣以后出問題回溯問責(zé)也比較方便,本文討論Spring Data JPA審計數(shù)據(jù)庫變更問題,感興趣的朋友一起看看吧
    2021-06-06
  • 老生常談Java中List與ArrayList的區(qū)別

    老生常談Java中List與ArrayList的區(qū)別

    大家都知道List是接口,ArrayList是List接口的一個實現(xiàn)類,接下來通過本文給大家介紹Java中List與ArrayList的區(qū)別,需要的朋友可以參考下
    2022-08-08
  • Java和c語言隨機(jī)數(shù)Random代碼詳細(xì)

    Java和c語言隨機(jī)數(shù)Random代碼詳細(xì)

    這篇文章主要介紹Java和c語言得隨機(jī)數(shù)Random,隨機(jī)數(shù)的用處在生活中比較少見,但是用處并不少,比如一些小游戲的制作等等。下面我們就一起來學(xué)習(xí)這篇關(guān)于Java和c隨機(jī)數(shù)Random得文章吧
    2021-10-10
  • Spring Boot整合tk.mybatis代碼實例

    Spring Boot整合tk.mybatis代碼實例

    這篇文章主要介紹了Spring Boot整合tk.mybatis代碼實例,文中通過示例代碼介紹的非常詳細(xì),對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友可以參考下
    2019-11-11
  • 淺析java雙向冒泡排序算法

    淺析java雙向冒泡排序算法

    這篇文章主要介紹了淺析java雙向冒泡排序算法,并附上源碼,需要的朋友可以參考下
    2015-02-02
  • Maven插件docker-maven-plugin的使用

    Maven插件docker-maven-plugin的使用

    在我們持續(xù)集成過程中,項目工程一般使用 Maven 編譯打包,然后生成鏡像,docker-maven-plugin 插件就是為了幫助我們在Maven工程中,通過簡單的配置,自動生成鏡像并推送到倉庫中。感興趣的可以了解一下
    2021-06-06

最新評論

武乡县| 清原| 湘西| 和龙市| 威信县| 孙吴县| 天祝| 耒阳市| 甘孜县| 阿拉善盟| 安宁市| 三门县| 福贡县| 平遥县| 宁化县| 衡东县| 华蓥市| 乌兰县| 苏尼特右旗| 册亨县| 平顺县| 台中市| 科技| 宁陕县| 开封县| 井冈山市| 汕头市| 新巴尔虎右旗| 辉南县| 巍山| 玉龙| 衡南县| 垣曲县| 恩施市| 柘荣县| 中西区| 瓮安县| 卓资县| 台州市| 新田县| 吴堡县|