TODESKING
技術ブログ

java.net.URLをHashMapに突っ込むと大変なことになるのでやめろ、それどころかequalsを呼ぶだけでも大変なことに

参照: http://stackoverflow.com/questions/2348399/why-does-java-net-urls-hashcode-resolve-the-host-to-an-ip

公式ドキュメントによると:

public boolean equals(Object obj)

2 つの URL オブジェクトが等しいのは、同じプロトコルを持ち、同じホストを参照し、ホスト上のポート番号が同じで、ファイルとファイルのフラグメントが同じ場合です。

2 つのホストが等価と見なされるのは、両方のホスト名が同じ IP アドレスに解決されるか、どちらかのホスト名を解決できない場合は、大文字小文字に関係なくホスト名が等しいか、両方のホスト名が null に等しい場合です。

ホスト比較には名前解決が必要なので、この操作はブロック操作です。

java.net.URL#equals()

もちろん等価性に依存するhashCode()などもこの影響を受けるので、うっかりコレクションにURLを格納すると大量の名前解決が発生して死ぬほど遅くなる。

代替案:java.net.URIを使う

ではどうするのがいいかというと、java.net.URIのほうを使うと名前解決しないので良いです。

基本的には同じような操作が可能ですが、java.net.URLより書式に厳密なので注意。不正な文字列を与えるとjava.net.URISyntaxExceptionになります。

よくある日本語文字列がそのまま入ったURLなどもアウトなので、事前にjava.net.URLEncoderなどを使ってダメな文字をエスケープする必要があります。

別の代替案:URLStreamHandlerを明示的に指定してURLを構築する

ホスト比較時の動作はURLStreamHandlerを使ってるようなので、名前解決しない実装を作成してURL(String protocol, String host, int port, String file, URLStreamHandler handler)等のコンストラクタに明示的に渡すようにすれば良いと思われます(未確認)。こっちのほうが影響範囲は少なそう。

Comments