<kbd id="afajh"><form id="afajh"></form></kbd>
<strong id="afajh"><dl id="afajh"></dl></strong>
    <del id="afajh"><form id="afajh"></form></del>
        1. <th id="afajh"><progress id="afajh"></progress></th>
          <b id="afajh"><abbr id="afajh"></abbr></b>
          <th id="afajh"><progress id="afajh"></progress></th>

          為什么不建議你使用實數(shù)作為 HashMap 的key?

          共 625字,需瀏覽 2分鐘

           ·

          2021-12-01 01:18

          程序員的成長之路
          互聯(lián)網/程序員/技術/資料共享?
          關注


          閱讀本文大概需要 5 分鐘。

          來自:blog.csdn.net/qq_30219017/article/details/79689492

          1.起因

          讓我關注到這一點的起因是一道題:牛客網上的max-points-on-a-line
          題目是這么描述的:
          Given n points on a 2D plane, find the maximum number of points that lie on the same straight line.
          大意就是給我一些點的X,Y坐標,找到過這些點最多的直線,輸出這條線上的點數(shù)量
          于是我就敲出了以下的代碼:
          import?java.util.HashMap;
          import?java.util.Map;

          //class?Point?{
          //????int?x;
          //????int?y;
          //????Point(int?a,?int?b)?{?x?=?a;?y?=?b;?}
          //}

          public?class?Solution?{
          ????public?int?maxPoints(Point[]?points)?{
          ????????if?(points.length?<=?2)?{
          ????????????return?points.length;
          ????????}

          ????????int?max?=?2;
          ????????for?(int?i?=?0;?i?1;?i++)?{
          ????????????Map?map?=?new?HashMap<>(16);
          ????????????//?記錄垂直點數(shù);?當前和Points[i]在一條線上的最大點數(shù);?和Points[i]垂直的點數(shù)
          ????????????int?ver?=?0,?cur,?dup?=?0;
          ????????????for?(int?j?=?i?+?1;?j?????????????????if?(points[j].x?==?points[i].x)?{
          ????????????????????if?(points[j].y?!=?points[i].y)?{
          ????????????????????????ver++;
          ????????????????????}?else?{
          ????????????????????????dup++;
          ????????????????????}
          ????????????????}?else?{
          ????????????????????float?d?=?(float)((points[j].y?-?points[i].y)?/?(double)?(points[j].x?-?points[i].x));
          ????????????????????map.put(d,?map.get(d)?==?null???1?:?map.get(d)?+?1);
          ????????????????}
          ????????????}

          ????????????cur?=?ver;
          ????????????for?(int?v?:?map.values())?{
          ????????????????cur?=?Math.max(v,?cur);
          ????????????}

          ????????????max?=?Math.max(max,?cur?+?dup?+?1);
          ????????}
          ????????return?max;
          ????}
          }
          這段代碼在天真的我看來是沒啥問題的,可就是沒辦法過,經過長久的排查錯誤,我寫了以下代碼加在上面的代碼里運行
          public?static?void?main(String[]?args)?{
          ????int[][]?vals?=?{{2,3},{3,3},{-5,3}};
          ????Point[]?points?=?new?Point[3];

          ????for?(int?i=0;?i????????points[i]?=?new?Point(vals[i][0],?vals[i][1]);
          ????}

          ????Solution?solution?=?new?Solution();

          ????System.out.println(solution.maxPoints(points));
          }
          它輸出的,竟然是2
          也就是說,它認為(3-3) / (3-2) 和 (3-3) / (-5-2) 不同? 什么鬼…
          經過debug,發(fā)現(xiàn)上述結果分別是0.0和-0.0
          0.0 難道不等于 -0.0 ?
          這時我心里已經一陣臥槽了,不過我還是寫了驗證代碼:
          System.out.println(0.0?==?-0.0);
          結果是True,沒問題啊,我凌亂了……
          一定是java底層代碼錯了! 我沒錯……
          又是一陣debug,我找到了這條語句:
          map.put(d,?map.get(d)?==?null???1?:?map.get(d)?+?1);
          我覺得map.get()很有問題, 它的源代碼是這樣的:
          public?V?get(Object?key)?{
          ????Node?e;
          ????return?(e?=?getNode(hash(key),?key))?==?null???null?:?e.value;
          }
          唔,先獲得hash()是吧,那我找到了它的hash函數(shù):
          static?final?int?hash(Object?key)?{
          ????int?h;
          ????return?(key?==?null)???0?:?(h?=?key.hashCode())?^?(h?>>>?16);
          }
          再來,這里是要比較h 和key的hashCode是吧,那我們去看hashCode()函數(shù)
          public?native?int?hashCode();
          這是一個本地方法,看不到源碼了,唔,,那我就使用它看看吧,測試一下不就好了嗎,我寫了以下的測試代碼:
          ????public?static?void?main(String[]?args)?{
          ????????System.out.println(0.0?==?-0.0);
          ????????System.out.println(new?Float(0.0).hashCode()?==?
          ????????????new?Float(-0.0).hashCode());
          ????}
          結果竟然是True和False !!!
          這個源頭終于找到了, 0.0 和 -0.0 的hashCode值是不同的 !
          經過一番修改,我通過了這道題(其實精度也會有問題,應該使用BigDecimal的,不過牛客網要求沒那么高。后來我想了想只有把直線方程寫成Ax+By+C=0的形式才能完全避免精度問題)
          接下來,探討下實數(shù)的hashCode()函數(shù)是個啥情況:

          2.實數(shù)的hashCode()

          • 在程序執(zhí)行期間,只要equals方法的比較操作用到的信息沒有被修改,那么對這同一個對象調用多次,hashCode方法必須始終如一地返回同一個整數(shù)。

          • 如果兩個對象根據(jù)equals方法比較是相等的,那么調用兩個對象的hashCode方法必須返回相同的整數(shù)結果。

          • 如果兩個對象根據(jù)equals方法比較是不等的,則hashCode方法不一定得返回不同的整數(shù)?!秂ffective java》

          那么我們來看看,0.0和-0.0調用equals方法是否相等:
          System.out.println(new?Float(0.0).equals(0.0f));
          System.out.println(new?Float(0.0).equals((float)?-0.0));
          輸出是True 和 False
          好吧,二者調用equals()?方法不相等,看來是滿足了書里說的邏輯的
          那我們看看Float底層equals函數(shù)咋寫的:
          public?boolean?equals(Object?obj)?{
          ????return?(obj?instanceof?Float)
          ???????????&&?(floatToIntBits(((Float)obj).value)?==?
          ???????????????????floatToIntBits(value));
          }
          哦,原來是把Float轉換成Bits的時候發(fā)生了點奇妙的事,于是我找到了一切的源頭:
          ????/**
          ?????*?Returns?a?representation?of?the?specified?floating-point?value
          ?????*?according?to?the?IEEE?754?floating-point?"single?format"?bit
          ?????*?layout.
          ?????*
          ?????*?

          Bit?31?(the?bit?that?is?selected?by?the?mask
          ?????*?{@code?0x80000000})?represents?the?sign?of?the?floating-point
          ?????*?number.
          ?????*?Bits?30-23?(the?bits?that?are?selected?by?the?mask
          ?????*?{@code?0x7f800000})?represent?the?exponent.
          ?????*?Bits?22-0?(the?bits?that?are?selected?by?the?mask
          ?????*?{@code?0x007fffff})?represent?the?significand?(sometimes?called
          ?????*?the?mantissa)?of?the?floating-point?number.
          ?????*
          ?????*?

          If?the?argument?is?positive?infinity,?the?result?is
          ?????*?{@code?0x7f800000}.
          ?????*
          ?????*?

          If?the?argument?is?negative?infinity,?the?result?is
          ?????*?{@code?0xff800000}.
          ?????*
          ?????*?

          If?the?argument?is?NaN,?the?result?is?{@code?0x7fc00000}.
          ?????*
          ?????*?

          In?all?cases,?the?result?is?an?integer?that,?when?given?to?the
          ?????*?{@link?#intBitsToFloat(int)}?method,?will?produce?a?floating-point
          ?????*?value?the?same?as?the?argument?to?{@code?floatToIntBits}
          ?????*?(except?all?NaN?values?are?collapsed?to?a?single
          ?????*?"canonical"?NaN?value).
          ?????*
          ?????*?@param???value???a?floating-point?number.
          ?????*?@return?the?bits?that?represent?the?floating-point?number.
          ?????*/
          ????public?static?int?floatToIntBits(float?value)?{
          ????????int?result?=?floatToRawIntBits(value);
          ????????//?Check?for?NaN?based?on?values?of?bit?fields,?maximum
          ????????//?exponent?and?nonzero?significand.
          ????????if?(((result?&?FloatConsts.EXP_BIT_MASK)?==
          ??????????????FloatConsts.EXP_BIT_MASK)?&&
          ?????????????(result?&?FloatConsts.SIGNIF_BIT_MASK)?!=?0)
          ????????????result?=?0x7fc00000;
          ????????return?result;
          ????}

          這文檔挺長的,也查了其它資料,看了半天終于搞懂了
          就是說Java浮點數(shù)的語義一般遵循IEEE 754二進制浮點算術標準。IEEE 754標準提供了浮點無窮,負無窮,負零和NaN(非數(shù)字)的定義。在使用Java過程中,一些特殊的浮點數(shù)通常會讓大家很迷惑
          當浮點運算產生一個非常接近0的負浮點數(shù)時,會產生“-0.0”,而這個浮點數(shù)不能正常表示
          我們可以輸出一波0.0和-0.0的數(shù)據(jù):
          System.out.println(Float.floatToIntBits((float)?0.0));
          System.out.println(Float.floatToIntBits((float)?-0.0));
          System.out.println(Float.floatToRawIntBits(0.0f));
          System.out.println(Float.floatToRawIntBits((float)-0.0));
          結果:
          0
          -2147483648
          0
          -2147483648
          就是說,存儲-0.0, 竟然用的是0x80000000
          這也是我們熟悉的Integer.MIN_VALUE

          推薦閱讀:

          MyBatis批量插入幾千條數(shù)據(jù),請慎用foreach!??!

          SpringBoot靜態(tài)獲取 bean的三種方式,你學會了嗎?

          最近面試BAT,整理一份面試資料《Java面試BATJ通關手冊》,覆蓋了Java核心技術、JVM、Java并發(fā)、SSM、微服務、數(shù)據(jù)庫、數(shù)據(jù)結構等等。

          獲取方式:點個「在看」,點擊上方小卡片,進入公眾號后回復「面試題」領取,更多內容陸續(xù)奉上。

          朕已閱?

          瀏覽 35
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          評論
          圖片
          表情
          推薦
          點贊
          評論
          收藏
          分享

          手機掃一掃分享

          分享
          舉報
          <kbd id="afajh"><form id="afajh"></form></kbd>
          <strong id="afajh"><dl id="afajh"></dl></strong>
            <del id="afajh"><form id="afajh"></form></del>
                1. <th id="afajh"><progress id="afajh"></progress></th>
                  <b id="afajh"><abbr id="afajh"></abbr></b>
                  <th id="afajh"><progress id="afajh"></progress></th>
                  乱伦图片区 | 成人毛片女人AAA久久 | 色婷婷大香蕉 | 精品免费视频6 | 色男人色天堂 |