工作中可能用Set比較少,但是如果用的時候,出的一些問題很讓人摸不著頭腦,然后我就看了一下Set的底層實現,大吃一驚。 ###看一個問題
Map map = new HashMap();map.put(1,"a");map.put(12,"ab");map.put(123,"abc");Set set1 = map.keySet();Set set2 = map.keySet();Set set3 = map.keySet();set1.remove(1);set1.forEach(p-> System.out.println(p.toString()));set2.forEach(p-> System.out.println(p.toString()));set3.forEach(p-> System.out.println(p.toString()));
復制代碼
然后我的運行結果是
123
12
----------------
123
12
----------------
123
12
復制代碼
為什么我在set1里面執行remove(1);其它的兩個set對象為什么也刪掉了第一個元素呢? 為什么會受到我前面操作的影響呢。 ###分析底層實現
1.最簡單的實踐
我們大概能猜出問題的所在,就是set1其實調用的還是map對象。那怎樣才具有說服力呢。 學過反射的應該都清楚,我們可以看下set1它到底是什么類型的對象。
Class classes = set1.getClass();System.out.println(classes.getTypeName());
復制代碼
控制臺打印:
java.util.HashMap$KeySet
復制代碼
是不是眼前一亮,wtf竟然是個Map類型。我不是明明給它實例化了個Set對象嗎。好了,這個現象成功吸引了我的興趣。于是
找底層實現
我們找到這個KeySet方法
public Set<K> keySet() {Set<K> ks = keySet;if (ks == null) {ks = new AbstractSet<K>() {public Iterator<K> iterator() {return new Iterator<K>() {private Iterator<Entry<K,V>> i = entrySet().iterator();public boolean hasNext() {return i.hasNext();}public K next() {return i.next().getKey();}public void remove() {i.remove();}};}public int size() {return AbstractMap.this.size();}public boolean isEmpty() {return AbstractMap.this.isEmpty();}public void clear() {AbstractMap.this.clear();}public boolean contains(Object k) {return AbstractMap.this.containsKey(k);}};keySet = ks;}return ks;}
復制代碼
我們可以看到,有一個成員內部類AbstractSet(),里面有兩部分,一部分是new 一個迭代器(內部類),一部分是調用AbstractMap對象(外部類)。外部類對象調用的內部類的構造函數,反編譯的話,會看出傳入了外部類對象的引用進去。所以它最終的類型應該是AbstractMap,(Map的派生類)。
所以,眼看是實例化了一個Set對象,其實底層還是調用的map對象。