読者です 読者をやめる 読者になる 読者になる

java.util.TreeMapとjava.util.Comparatorの罠

Java

ちょっと職場で引っかかったコード。

java.util.TreeMapのコンストラクタにはjava.util.Comparatorを渡すことが出来て、エントリの並び順を細かく制御することができるけど、それでハマったのでメモ。環境はjava8

import java.util.Comparator;
import java.util.Map;
import java.util.TreeMap;


public class Sample {

  public static void main(String[] args) {
    Map<String, String> map = new TreeMap<>(new Comparator<String>() {
      @Override
      public int compare(String o1, String o2) {
        //実際のコードだともっと複雑で、ある特定の条件下でのみo1とo2が異なっていても0を戻す
        return 0;
      }
    });    
    map.put("hogehoge", "value_of_hogehoge");
    map.put("hogefuga", "value_of_hogefuga");
    map.put("fugaguga", "value_of_fugafuga");
    //mapに3つのKey-Valueを入れたつもりが・・・
    System.out.println(map);
  }
}

上のような感じのコード(実際にはComparator#compareの中身はもっと複雑)でmapにKey-Valueを追加した。で、3つのKey-Valueが入ると思ったのに実際の標準出力は

{hogehoge=value_of_fugafuga}

とmapの中に一つしか入ってない。
まあ、サンプルコード見れば分かるけど、Comparator#compareが0を戻している事が原因。

TreeMap (Java Platform SE 8)
を見ると

これはMapインタフェースがequalsオペレーションに基づいて定義されるためですが、ソート・マップはそのcompareTo (またはcompare)メソッドを使用してすべてのキー比較を実行するので、このメソッドによって等しいと見なされる2つのキーは、ソート・マップから見ても同じものです。ソート・マップの動作は、その順序付けがequalsと一貫性がない場合でも明確に定義されていますが、Mapインタフェースの一般規約には準拠していません。

とあるのでまあ仕様なんだけど、Comparatorは並び順を定義するだけだと思い込んでいたので引っかかった。
あと、コンストラクタの説明の方にも一言入れといて欲しい。