Java中keySet()方法的作用是什么?相信很多沒有經(jīng)驗的人對此束手無策,為此本文總結(jié)了問題出現(xiàn)的原因和解決方法,通過這篇文章希望你能解決這個問題。
創(chuàng)新互聯(lián)建站是一家專業(yè)提供安吉企業(yè)網(wǎng)站建設(shè),專注與網(wǎng)站設(shè)計制作、成都做網(wǎng)站、HTML5建站、小程序制作等業(yè)務(wù)。10年已為安吉眾多企業(yè)、政府機(jī)構(gòu)等服務(wù)。創(chuàng)新互聯(lián)專業(yè)網(wǎng)站制作公司優(yōu)惠進(jìn)行中。
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)試。
Java是一門面向?qū)ο缶幊陶Z言,可以編寫桌面應(yīng)用程序、Web應(yīng)用程序、分布式系統(tǒng)和嵌入式系統(tǒng)應(yīng)用程序。
看完上述內(nèi)容,你們掌握J(rèn)ava中keySet()方法的作用是什么的方法了嗎?如果還想學(xué)到更多技能或想了解更多相關(guān)內(nèi)容,歡迎關(guān)注創(chuàng)新互聯(lián)行業(yè)資訊頻道,感謝各位的閱讀!
當(dāng)前標(biāo)題:Java中keySet()方法的作用是什么
本文URL:http://jinyejixie.com/article2/ppjiic.html
成都網(wǎng)站建設(shè)公司_創(chuàng)新互聯(lián),為您提供外貿(mào)建站、網(wǎng)站建設(shè)、面包屑導(dǎo)航、網(wǎng)站策劃、網(wǎng)站導(dǎo)航、
聲明:本網(wǎng)站發(fā)布的內(nèi)容(圖片、視頻和文字)以用戶投稿、用戶轉(zhuǎn)載內(nèi)容為主,如果涉及侵權(quán)請盡快告知,我們將會在第一時間刪除。文章觀點不代表本網(wǎng)站立場,如需處理請聯(lián)系客服。電話:028-86922220;郵箱:631063699@qq.com。內(nèi)容未經(jīng)允許不得轉(zhuǎn)載,或轉(zhuǎn)載時需注明來源: 創(chuàng)新互聯(lián)