Java 魔法Part 1:java.net.URL

原文 http://mishadoff.com/blog/java-magic-part-1-java-dot-net-dot-url/

這篇文章比較老了,也有很多人翻譯,但是好像都是google翻譯的,沒有體現(xiàn)出作者的風(fēng)趣。感覺作者還是很皮的,他的這系列文章都很有意思。


最近, 我在reddit上發(fā)現(xiàn)了一個非常有趣的Java代碼片段(有一點修改)

HashSet set = new HashSet();
set.add(new URL("http://google.com"));
set.contains(new URL("http://google.com"));
Thread.sleep(60000);
set.contains(new URL("http://google.com"));

你認(rèn)為第三代碼和第五行代碼的結(jié)果會是什么?
既然問了這個問題,那很明顯不是true, true。先思考兩分鐘。

好了,在大多數(shù)時候結(jié)果是true, false 這是因為你連接了互聯(lián)網(wǎng)(否則你怎么能看到這篇文章呢?)關(guān)閉你的網(wǎng)絡(luò)連接或者WiFi你將會得到true, true。
問題的原因在于該類方法hashCode() 和 equals()的實現(xiàn)邏輯。
讓我們看下它是如何計算hashCode的:

public synchronized int hashCode() {
  if (hashCode != -1)
    return hashCode;
  hashCode = handler.hashCode(this);
  return hashCode;
}

我們可以看到hashCode是一個實例變量并且只計算一次。這是有意義的,因為URL是不可變的。handler是什么?它是URLStreamHandler子類的實例,具體依賴于不同的協(xié)議類型(file,http,ftp)??聪耲ava.net.URL的Java文檔說明:

The hash code is based upon all the URL components relevant for URL comparison. As such, this operation is a blocking operation.

等一下! 阻塞式操作?!

對不起我昨天沒有收新郵件,因為hashCode計算阻塞了

或者更好的例子:

不是的,媽媽,我不能看X片。你知道的,在做hashCode計算呢(這個實在是太皮了)

好的就當(dāng)他是阻塞操作吧。另一個奇葩的地方,當(dāng)計算hashCode的時候這個handler竟然會解析ip地址。更準(zhǔn)確的說法是會嘗試去解析ip地址,如果無法解析ip地址的話,會根據(jù)host地址去計算hashCode。我們拿google.com舉個例子。當(dāng)host的ip是動態(tài)的時候,或者說有一個域名解析的負(fù)載均衡的時候,不好的事情就發(fā)生了。在這種情況下同一個域名會得到不同的hashCode值,如果用在HashSet就會有兩個(或者多個)實例在集合列表里。這一點也不好。順便說一下,hashCode 和 equals 的性能也是很不好的,因為URLStreamHandler會開啟一個URLConnection,不過這是另外一個話題了。


附Java里URLStreamHandler代碼實現(xiàn)

protected int hashCode(URL u) {
        int h = 0;

        // Generate the protocol part.
        String protocol = u.getProtocol();
        if (protocol != null)
            h += protocol.hashCode();

        // Generate the host part.
        InetAddress addr = getHostAddress(u);
        if (addr != null) {
            h += addr.hashCode();
        } else {
            String host = u.getHost();
            if (host != null)
                h += host.toLowerCase().hashCode();
        }

        // Generate the file part.
        String file = u.getFile();
        if (file != null)
            h += file.hashCode();

        // Generate the port part.
        if (u.getPort() == -1)
            h += getDefaultPort();
        else
            h += u.getPort();

        // Generate the ref part.
        String ref = u.getRef();
        if (ref != null)
            h += ref.hashCode();

        return h;
    }

好消息是Android源碼里對此作了更改

protected int hashCode(URL u) {
        // Android-changed: Avoid network I/O
        // Hash on the same set of fields that we compare in equals().
        return Objects.hash(
                u.getRef(),
                u.getQuery(),
                u.getProtocol(),
                u.getFile(),
                u.getHost(),
                u.getPort());
    }
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
【社區(qū)內(nèi)容提示】社區(qū)部分內(nèi)容疑似由AI輔助生成,瀏覽時請結(jié)合常識與多方信息審慎甄別。
平臺聲明:文章內(nèi)容(如有圖片或視頻亦包括在內(nèi))由作者上傳并發(fā)布,文章內(nèi)容僅代表作者本人觀點,簡書系信息發(fā)布平臺,僅提供信息存儲服務(wù)。

相關(guān)閱讀更多精彩內(nèi)容

  • 從三月份找實習(xí)到現(xiàn)在,面了一些公司,掛了不少,但最終還是拿到小米、百度、阿里、京東、新浪、CVTE、樂視家的研發(fā)崗...
    時芥藍閱讀 42,761評論 11 349
  • Java繼承關(guān)系初始化順序 父類的靜態(tài)變量-->父類的靜態(tài)代碼塊-->子類的靜態(tài)變量-->子類的靜態(tài)代碼快-->父...
    第六象限閱讀 2,247評論 0 9
  • BAT 常問的 Java基礎(chǔ)39道常見面試題 1.八種基本數(shù)據(jù)類型的大小,以及他們的封裝類 2.引用數(shù)據(jù)類型 3....
    AI喬治閱讀 2,521評論 0 75
  • (一)Java部分 1、列舉出JAVA中6個比較常用的包【天威誠信面試題】 【參考答案】 java.lang;ja...
    獨云閱讀 7,241評論 0 62
  • 久違的晴天,家長會。 家長大會開好到教室時,離放學(xué)已經(jīng)沒多少時間了。班主任說已經(jīng)安排了三個家長分享經(jīng)驗。 放學(xué)鈴聲...
    飄雪兒5閱讀 7,788評論 16 22

友情鏈接更多精彩內(nèi)容