walterwing  
          日歷
          <2008年11月>
          2627282930311
          2345678
          9101112131415
          16171819202122
          23242526272829
          30123456
          統(tǒng)計(jì)
          • 隨筆 - 12
          • 文章 - 1
          • 評(píng)論 - 7
          • 引用 - 0

          導(dǎo)航

          常用鏈接

          留言簿(1)

          隨筆分類

          隨筆檔案

          搜索

          •  

          最新評(píng)論

          閱讀排行榜

          評(píng)論排行榜

           
          題目:

          public class SetSize {
              
          public static void main(string[] args) {
                  Set
          <Short> set = new HashSet<Short>();
                  
          for(short i = 0; i < 10; i++{
                      set.add(i);
                      set.remove(i 
          - 1);
                  }


                  System.out.println(set.size());
              }

          }

          上面代碼輸出結(jié)果為10,為什么?



          題目乍一看很簡(jiǎn)單,不就是一個(gè)HashSet么,但看完了題目又覺得困惑:對(duì)啊,怎么會(huì)是10?真難為自己還特意花了一些時(shí)間學(xué)習(xí)Java的Collection Framework,居然連這么小的一個(gè)題目都吃不透,汗顏啊……

          看了半天,沒有思路,跳過去做其他題,待到收卷前10分鐘,不甘心,再回過頭來看這道題。發(fā)現(xiàn)一個(gè)值得懷疑的地方:Set<Short> set = ……
          為什么要特意設(shè)定是Short?再看到下面的i - 1,馬上感覺到似乎和類型轉(zhuǎn)換有關(guān)。

          目前可以肯定的是:
          1. set存儲(chǔ)的元素是Short類型
          2. 調(diào)用remove方法時(shí),i - 1會(huì)自動(dòng)轉(zhuǎn)型為int,也就是說,傳給remove方法的參數(shù)是一個(gè)int值

          接下來呢?int值會(huì)被自動(dòng)包裝成Integer還是Short?對(duì)Java的泛型和Auto-Boxing不是很了解,于是在這犯迷糊了,只好將目前的思路草草寫了幾筆,交卷……

          作為一名后知后覺的Java愛好者,自然不能放過這么有意思的題目。如是回來之后,上機(jī)親自試驗(yàn),輸入試題程序,出來的結(jié)果果然是10(嗯,沒騙我……)。然后驗(yàn)證自己的猜測(cè):將Set<Short>改為Set<Integer>,輸出結(jié)果……1!

          看來我還是有一定的嗅覺的……

          接下來就是探索原因了

          首先驗(yàn)證一下i - 1得到的結(jié)果會(huì)不會(huì)有錯(cuò):-1,0,1,……8。嗯,是對(duì)的

          然后驗(yàn)證一下每次remove之后有沒有真的remove掉指定的值……發(fā)現(xiàn)沒有。

          苦思……

          經(jīng)一位精研C++的同學(xué)提醒:是不是因?yàn)榇娴氖莝hort,但刪的是int,所以不能刪?

          試驗(yàn)一下:

          short temp = 1;
          Short s 
          = new Short(temp);  //不能是new Short(1),否則編譯出錯(cuò)
          Integer i = new Integer(1);
          System.out.println(s.equals(i));

          輸出結(jié)果……false!看來終于找到真正的原因了。

          查看一下Short.equals()方法的源代碼:

          public boolean equals(Object obj) {
              
          if (obj instanceof Short) {
                  
          return value == ((Short)obj).shortValue();
              }

              
          return false;
          }

          ft……居然不是同一類型的就無視……這跟胸懷廣闊的集合框架背道而馳啊……當(dāng)然,這是我的錯(cuò)

          目前可以大致推斷:因?yàn)閟et中存入的是值為0~9的Short型對(duì)象,而每次刪除是是要求刪除值為-1~8的Integer型對(duì)象,但值相同的Short與Integer看來并不“相等”,所以無法刪除。

          那就看一下HashSet的remove方法究竟干了些什么

          public boolean remove(Object o) {
              
          return map.remove(o)==PRESENT;
          }

          public V remove(Object key) {
                  Entry
          <K,V> e = removeEntryForKey(key);
                  
          return (e == null ? null : e.value);
          }

          終于到主菜了……
          /**
               * Removes and returns the entry associated with the specified key
               * in the HashMap.  Returns null if the HashMap contains no mapping
               * for this key.
               
          */

              
          final Entry<K,V> removeEntryForKey(Object key) {
                  
          int hash = (key == null? 0 : hash(key.hashCode());
                  
          int i = indexFor(hash, table.length);
                  Entry
          <K,V> prev = table[i];
                  Entry
          <K,V> e = prev;

                  
          while (e != null{
                      Entry
          <K,V> next = e.next;
                      Object k;
                      
          if (e.hash == hash &&
                          ((k 
          = e.key) == key || (key != null && key.equals(k)))) {
                          modCount
          ++;
                          size
          --;
                          
          if (prev == e)
                              table[i] 
          = next;
                          
          else
                              prev.next 
          = next;
                          e.recordRemoval(
          this);
                          
          return e;
                      }

                      prev 
          = e;
                      e 
          = next;
                  }


                  
          return e;
              }

          代碼就不用解釋了,大家都看得懂。

          HashSet里面刪除一個(gè)元素時(shí),首先要根據(jù)傳進(jìn)來對(duì)象的hash值從hash table中找到對(duì)應(yīng)的“Entry”,當(dāng)然,難免會(huì)有碰撞的情況,這時(shí)需要遍歷一下來找到真正需要?jiǎng)h除的對(duì)象。一直到這一步,Short和Integer的差異還沒有體現(xiàn)出來,因?yàn)樗鼈兎祷氐膆ash值是相同的,但當(dāng)執(zhí)行key.equals(k)的時(shí)候,就“不相等”了,因此找不到要?jiǎng)h除的元素,刪除失敗



          小小的一道題,囊括了Java方方面面的細(xì)節(jié),果然語言這個(gè)東西博大精深啊,有時(shí)候你以為自己很明白了,但隨便揪出一個(gè)細(xì)節(jié)來就足以打到你……

          大家還是好好學(xué)習(xí),天天向上吧
          posted on 2008-11-02 19:52 This is Wing 閱讀(647) 評(píng)論(3)  編輯  收藏 所屬分類: Java基礎(chǔ)
          評(píng)論:
          • # re: 一道小而精巧的筆試題——Set、equals、類型轉(zhuǎn)換……  sclsch Posted @ 2008-11-04 09:23
            講得不錯(cuò)啊。學(xué)習(xí)了。  回復(fù)  更多評(píng)論   

          • # re: 一道小而精巧的筆試題——Set、equals、類型轉(zhuǎn)換……  sclsch Posted @ 2008-11-04 09:47
            請(qǐng)講講
            removeEntryForKey
            這段代碼吧,
            我是新手,看不懂。  回復(fù)  更多評(píng)論   

          • # re: 一道小而精巧的筆試題——Set、equals、類型轉(zhuǎn)換……  This is Wing Posted @ 2008-11-04 16:02
            @sclsch
            其實(shí)我建議你先去回顧一下數(shù)據(jù)結(jié)構(gòu)中的哈希表,比如怎么存儲(chǔ),怎么解決碰撞,怎么查找……這些都是很基礎(chǔ)的東西,很多地方都要用到。相信當(dāng)你搞懂哈希表之后,自然也就知道上面的代碼是在做什么了  回復(fù)  更多評(píng)論   

           
          Copyright © This is Wing Powered by: 博客園 模板提供:滬江博客
          主站蜘蛛池模板: 赣榆县| 繁昌县| 济宁市| 西华县| 肃南| 南开区| 溧阳市| 汾阳市| 介休市| 兴海县| 道孚县| 苍山县| 北票市| 中西区| 桂平市| 怀仁县| 美姑县| 临江市| 南宫市| 固始县| 富蕴县| 嵊州市| 恩平市| 屏东市| 海兴县| 茌平县| 梁山县| 武定县| 南溪县| 乌鲁木齐市| 昌都县| 夏河县| 乐业县| 内乡县| 山东省| 垫江县| 潍坊市| 车险| 禄丰县| 鹿邑县| 红桥区|