java.util.Arrays的BUG - 二分搜索算法

Joshua Bloch, 獲得過Jolt最暢銷獎的《Effective Java》的作者, 是Sun Microsystems的傑出工程師和Transarc的資深系統設計師, J2SE 5.0 Tiger的代言人和領路人, 也是還是JSR166的發起人之一..

後來, Joshua Bloch去了Google, 成爲了Google的首席工程師

Joshua Bloch擁有卡耐基.梅隆大學(CMU)計算機科學的博士學位。

在最近Joshua Bloch的一篇文章裏, Joshua Bloch回憶了當年在CMU上課的時候, vividly Jon Bentley 第一節算法課, 就要求所有剛進來的PHD學生, 每人都寫一個二分查找算法. 然後發現, 多數人的算法都存在BUG, 這在當時給了Joshua Bloch 一個很深的印象..

在之後, Joshua Bloch 負責java.util.Arrays 代碼編寫的時候, 採用了Bentley 在<<Programming Pearls >>一書中的二分查找算法, 結果在8年後的今天, Joshua Bloch 發現, 這裏面原來還是有一個BUG.

問題到底是出在哪裏? 竟然逃過了Bentley 和Joshua Bloch 的雙重檢測?

一起來看看java.util.Arrays的代碼:

1:     public static int binarySearch(int[] a, int key) {
2:         int low = 0;
3:         int high = a.length - 1;
4:
5:         while (low <= high) {
6:             int mid = (low + high) / 2;
7:             int midVal = a[mid];
8:
9:             if (midVal < key)
10:                 low = mid + 1;
11:             else if (midVal > key)
12:                 high = mid - 1;
13:             else
14:                 return mid; // key found
15:         }
16:         return -(low + 1);  // key not found.
17:     }


這是很經典的一個二分查找算法.

bug出現在第6行:

6:             int mid =(low + high) / 2;

在一般情況下, 這個語句是不會出錯的, 但是, 當low+high的值超過了最大的正int值 (231- 1) 的時候, mid會變成負值,  這個時候, 會拋出ArrayIndexOutOfBoundsException 異常..


所以當一個數組包含超過2的30次方 個元素的時候, 就很可能會帶來問題... 當然, 在一般的應用裏面, 很少數組會包含那麼多元素, 但是現在這樣的情況已經越來越多了, 比如Google的海量運算..

那如何解決這個問題?

很簡單, 修改這行語句爲:

6:             int mid = low + ((high - low) / 2);
或者
6:             int mid = (low + high) >>> 1;


在c或者c++中, 則可以如下實現:
6:             mid = ((unsigned) (low + high)) >> 1;


這個問題告訴我們, 無論什麼時候, 我們都不要想當然我們的程序是完美的. 我們需要細心的設計,測試再測試,符合規範的方法等等...對此, 你有什麼經驗和大家分享嗎?

同樣給我們帶來的思考是: 8年了, java.util.Arrays 竟然存在這樣一個bug, 這不得不讓我們對JDK本身的測試性, 穩定性 懷有疑問.. 將來又會有多少個類似的bug出現呢?

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章