美文网首页
Java 魔法Part 1:java.net.URL

Java 魔法Part 1:java.net.URL

作者: MrLyp | 来源:发表于2020-06-12 18:03 被阅读0次

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

    这篇文章比较老了,也有很多人翻译,但是好像都是google翻译的,没有体现出作者的风趣。感觉作者还是很皮的,他的这系列文章都很有意思。


    最近, 我在reddit上发现了一个非常有趣的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"));
    

    你认为第三代码和第五行代码的结果会是什么?
    既然问了这个问题,那很明显不是true, true。先思考两分钟。

    好了,在大多数时候结果是true, false 这是因为你连接了互联网(否则你怎么能看到这篇文章呢?)关闭你的网络连接或者WiFi你将会得到true, true
    问题的原因在于该类方法hashCode() 和 equals()的实现逻辑。
    让我们看下它是如何计算hashCode的:

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

    我们可以看到hashCode是一个实例变量并且只计算一次。这是有意义的,因为URL是不可变的。handler是什么?它是URLStreamHandler子类的实例,具体依赖于不同的协议类型(file,http,ftp)。看下java.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计算呢(这个实在是太皮了)

    好的就当他是阻塞操作吧。另一个奇葩的地方,当计算hashCode的时候这个handler竟然会解析ip地址。更准确的说法是会尝试去解析ip地址,如果无法解析ip地址的话,会根据host地址去计算hashCode。我们拿google.com举个例子。当host的ip是动态的时候,或者说有一个域名解析的负载均衡的时候,不好的事情就发生了。在这种情况下同一个域名会得到不同的hashCode值,如果用在HashSet就会有两个(或者多个)实例在集合列表里。这一点也不好。顺便说一下,hashCode 和 equals 的性能也是很不好的,因为URLStreamHandler会开启一个URLConnection,不过这是另外一个话题了。


    附Java里URLStreamHandler代码实现

    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());
        }
    

    相关文章

      网友评论

          本文标题:Java 魔法Part 1:java.net.URL

          本文链接:https://www.haomeiwen.com/subject/kxjntktx.html