题目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的。