下面的程序使用定制的比較器,對(duì)一個(gè)由隨機(jī)挑選的Integer實(shí)例組成的數(shù)組進(jìn)行排序,然后打印了一個(gè)描述了數(shù)組順序的單詞。回憶一下,Comparator接口只有一個(gè)方法,即compare,它在第一個(gè)參數(shù)小于第二個(gè)參數(shù)時(shí)返回一個(gè)負(fù)數(shù),在兩個(gè)參數(shù)相等時(shí)返回0,在第一個(gè)參數(shù)大于第二個(gè)參數(shù)時(shí)返回一個(gè)整數(shù)。這個(gè)程序是展示5.0版特性的一個(gè)樣例程序。它使用了自動(dòng)包裝和解包、泛型和枚舉類型。那么,它會(huì)打印出什么呢?
import java.util.*;
public class SuspiciousSort {
public static void main(String[ ] args) {
Random rnd = new Random();
Integer[ ] arr = new Integer[100];
for (int i = 0; i < arr.length; i++)
arr[i] = rnd.nextInt();
Comparator cmp = new Comparator() {
public int compare(Integer i1, Integer i2) {
return i2 - i1;
}
};
Arrays.sort(arr, cmp);
System.out.println(order(arr));
}
enum Order { ASCENDING, DESCENDING, CONSTANT, UNORDERED };
static Order order(Integer[ ] a) {
boolean ascending = false;
boolean descending = false;
for (int i = 1; i < a.length; i++) {
ascending |= a[i] > a[i-1];
descending |= a[i] < a[i-1];
}
if (ascending && !descending)
return Order.ASCENDING;
if (descending && !ascending)
return Order.DESCENDING;
if (!ascending)
return Order.CONSTANT; // All elements equal
return Order.UNORDERED; // Array is not sorted
}
}
該程序的main方法創(chuàng)建了一個(gè)Integer實(shí)例的數(shù)組,并用隨機(jī)數(shù)對(duì)其進(jìn)行了初始化,然后用比較器cmp對(duì)該數(shù)組進(jìn)行排序。這個(gè)比較器的compare方法將返回它的第二個(gè)參數(shù)減去第一個(gè)參數(shù)的值,如果第二個(gè)參數(shù)表示的是比第一個(gè)參數(shù)大的數(shù)值,其返回值就是正的;如果這兩個(gè)參數(shù)相等,其返回值為0;如果第二個(gè)參數(shù)表示的是比第一個(gè)參數(shù)小的數(shù)值,其返回值就是負(fù)的。這種行為正好與compare方法通常的做法相反,因此,該比較器應(yīng)該施加的是降序排列。
在對(duì)數(shù)組排序之后,main方法將該數(shù)組傳遞給了靜態(tài)方法order,然后打印由這個(gè)方法返回的結(jié)果。該方法在數(shù)組中所有的元素都表示相同的數(shù)值時(shí),返回CONSTANT;在數(shù)組中每一對(duì)毗鄰的元素中第二個(gè)元素都大于等于第一個(gè)元素時(shí),返回ASCENDING;在數(shù)組中每一對(duì)毗鄰的元素中第二個(gè)元素都小于等于第一個(gè)元素時(shí),返回DESCENDING;在這些條件都不滿足時(shí),返回UNORDERED。盡管理論上說(shuō),數(shù)組中的100個(gè)隨機(jī)數(shù)有可能彼此都相等,但是這種奇特現(xiàn)象發(fā)生的非常?。?32×99分之一,即大約5×10953分之一。因此,該程序看起來(lái)應(yīng)該打印DESCENDING。如果你運(yùn)行該程序,幾乎可以肯定你將看到它打印的是UNORDERED。為什么它會(huì)產(chǎn)生如此的行為呢?
order方法很直觀,它并不會(huì)說(shuō)謊。Arrays.sort方法已經(jīng)存在許多年了,它工作得非常好?,F(xiàn)在只有一個(gè)地方能夠發(fā)現(xiàn)bug了:比較器。乍一看,這個(gè)比較器似乎不可能出錯(cuò)。畢竟,它使用的是標(biāo)準(zhǔn)的慣用法:如果你有兩個(gè)數(shù)字,你想得到一個(gè)數(shù)值,其符號(hào)表示它們的順序,那么你可以計(jì)算它們的差。這個(gè)慣用法至少?gòu)?970年代早期就一直存在了,它在早期的UNIX里面被廣泛地應(yīng)用。遺憾的是,這種慣用法從來(lái)都沒(méi)有正確地工作過(guò)。本謎題也許應(yīng)該稱為“白癡一般的慣用法的案例”。這種慣用法的問(wèn)題在于定長(zhǎng)的整數(shù)沒(méi)有大到可以保存任意兩個(gè)同等長(zhǎng)度的整數(shù)之差的程度。當(dāng)你在做兩個(gè)int或long數(shù)值的減法時(shí),其結(jié)果可能會(huì)溢出,在這種情況下我們就會(huì)得到錯(cuò)誤的符號(hào)。
import java.util.*;
public class SuspiciousSort {
public static void main(String[ ] args) {
Random rnd = new Random();
Integer[ ] arr = new Integer[100];
for (int i = 0; i < arr.length; i++)
arr[i] = rnd.nextInt();
Comparator cmp = new Comparator() {
public int compare(Integer i1, Integer i2) {
return i2 - i1;
}
};
Arrays.sort(arr, cmp);
System.out.println(order(arr));
}
enum Order { ASCENDING, DESCENDING, CONSTANT, UNORDERED };
static Order order(Integer[ ] a) {
boolean ascending = false;
boolean descending = false;
for (int i = 1; i < a.length; i++) {
ascending |= a[i] > a[i-1];
descending |= a[i] < a[i-1];
}
if (ascending && !descending)
return Order.ASCENDING;
if (descending && !ascending)
return Order.DESCENDING;
if (!ascending)
return Order.CONSTANT; // All elements equal
return Order.UNORDERED; // Array is not sorted
}
}
該程序的main方法創(chuàng)建了一個(gè)Integer實(shí)例的數(shù)組,并用隨機(jī)數(shù)對(duì)其進(jìn)行了初始化,然后用比較器cmp對(duì)該數(shù)組進(jìn)行排序。這個(gè)比較器的compare方法將返回它的第二個(gè)參數(shù)減去第一個(gè)參數(shù)的值,如果第二個(gè)參數(shù)表示的是比第一個(gè)參數(shù)大的數(shù)值,其返回值就是正的;如果這兩個(gè)參數(shù)相等,其返回值為0;如果第二個(gè)參數(shù)表示的是比第一個(gè)參數(shù)小的數(shù)值,其返回值就是負(fù)的。這種行為正好與compare方法通常的做法相反,因此,該比較器應(yīng)該施加的是降序排列。
在對(duì)數(shù)組排序之后,main方法將該數(shù)組傳遞給了靜態(tài)方法order,然后打印由這個(gè)方法返回的結(jié)果。該方法在數(shù)組中所有的元素都表示相同的數(shù)值時(shí),返回CONSTANT;在數(shù)組中每一對(duì)毗鄰的元素中第二個(gè)元素都大于等于第一個(gè)元素時(shí),返回ASCENDING;在數(shù)組中每一對(duì)毗鄰的元素中第二個(gè)元素都小于等于第一個(gè)元素時(shí),返回DESCENDING;在這些條件都不滿足時(shí),返回UNORDERED。盡管理論上說(shuō),數(shù)組中的100個(gè)隨機(jī)數(shù)有可能彼此都相等,但是這種奇特現(xiàn)象發(fā)生的非常?。?32×99分之一,即大約5×10953分之一。因此,該程序看起來(lái)應(yīng)該打印DESCENDING。如果你運(yùn)行該程序,幾乎可以肯定你將看到它打印的是UNORDERED。為什么它會(huì)產(chǎn)生如此的行為呢?
order方法很直觀,它并不會(huì)說(shuō)謊。Arrays.sort方法已經(jīng)存在許多年了,它工作得非常好?,F(xiàn)在只有一個(gè)地方能夠發(fā)現(xiàn)bug了:比較器。乍一看,這個(gè)比較器似乎不可能出錯(cuò)。畢竟,它使用的是標(biāo)準(zhǔn)的慣用法:如果你有兩個(gè)數(shù)字,你想得到一個(gè)數(shù)值,其符號(hào)表示它們的順序,那么你可以計(jì)算它們的差。這個(gè)慣用法至少?gòu)?970年代早期就一直存在了,它在早期的UNIX里面被廣泛地應(yīng)用。遺憾的是,這種慣用法從來(lái)都沒(méi)有正確地工作過(guò)。本謎題也許應(yīng)該稱為“白癡一般的慣用法的案例”。這種慣用法的問(wèn)題在于定長(zhǎng)的整數(shù)沒(méi)有大到可以保存任意兩個(gè)同等長(zhǎng)度的整數(shù)之差的程度。當(dāng)你在做兩個(gè)int或long數(shù)值的減法時(shí),其結(jié)果可能會(huì)溢出,在這種情況下我們就會(huì)得到錯(cuò)誤的符號(hào)。