題目2:關於Sets的更多Fun。
對了,這個題目表面上看也是關於Sets的...
程序
import java.net.*;
public class UrlSet {
private static final String[] URL_NAMES = {
"http://javapuzzlers.com",
"http://apache2-snort.skybar.dreamhost.com",
"http://www.google.com",
"http://javapuzzlers.com",
"http://findbugs.sourceforge.net",
"http://www.bianceng.cn"
};
public static void main (string[] args)
throws MalformedURLException {
Set<URL> favorites = new HashSet<URL>();
for (String urlName : URL_NAMES)
favorites.add(new URL(urlName));
System.out.println(favorites.size());
}
}
答案
(a) 4
(b) 5
(c) 6
(d) None of the above
譯者序:
因為最近忙,這個第二集的答案出得太晚了點,希望大家見諒。
另:程序中的一行忘記了括號,Set<URL> favorites = new HashSet<URL>(); 我已經加上了,多謝讀者的細心。
謎題一之答案,分析過程,以及經驗教訓
答案
(d) None of the above
分析過程
如果運行此程序所在的機器與互聯網相接,那麼答案將是(a) 4。為什麼呢?因為URL接口的equals和hashCode方法徹底搞錯了。
"http://javapuzzlers.com"和"http://apache2-snort.skybar.dreamhost.com",這兩個完全不同的網址,解析成的IP地址竟是完全相同的!(譯者注:寄存這兩個不同網址的web服務器顯然只有一個,用的是以名稱為基礎的共享IP網絡寄存技術,這顯然是出題者有意為之,即所謂的Virtual Host)
根據URL類的技術文檔,兩個URL對象將被認為相等,如果以下條件得到滿足:
使用同樣的通信協議,引用相當的寄存服務器,使用同樣的端口,同樣的文件或同樣的一部分文件。
兩個寄存服務器被認為是相等的,如果兩個名稱被解析成同一個IP地址,或者兩個名稱都不可被解析,或者兩個名稱同為空(null)。
因此,如果運行此程序所在的機器不與互聯網相接,得到的結果將是1,應為所有名稱都不可被解析。這個題的答案因此是(d) None of the above。不同網絡環境下的運行,就有不同的結果,這實在是比較糟糕的。
URL類的equals方法是不支持Virtual Host(虛擬寄存)功能的。93年URL類被加進java平台時,基本是沒有虛擬寄存這個技術的。
經驗教訓
URL類有bug,不要使用URL,應該使用URI類。
URI類的equals方法只會進行字符的比較。程序改成Set<URI> favorites = new HashSet<URI>(); 打印出來的結果就是5了,因為有兩個"http://javapuzzlers.com"的緣故。
同時,favorites.add(new URL(urlName))這行程序也要改成favorites.add(URI.create(uriname));
注意這裡用的是更好的靜態工廠的模式,而不是一般的創建,這也是URI所不同於URL的。