带有比较器的 TreeMap 比较键中字符串的长度

TreeMap with comparator comparing length of string in keys

我需要一个使用字符串长度作为排序标准的 TreeMap 将字符串映射到整数。这是我的代码:

TreeMap<String, Integer> map = new TreeMap<>(Comparator.comparingInt(String::length)) {{
  put("one", 1);
  put("two", 2);
  put("three", 3);
  put("four", 4);
  put("five", 5);
  put("six", 6);
}};

我希望地图的内容是这样的:

{one=1, two=2, six=6, four=4, five=5, three=3}

但我得到的是:

{one=6, four=5, three=3}

查看TreeMap的put方法的代码,可以看出,如果定义了Comparator,则使用比较器返回的值来决定是否创建一个新的key地图,或覆盖现有地图的值。在我的例子中,因为我有三个长度为 3 的键(“一”、“二”和“六”),它只插入一个长度为 3 的键(第一个插入的键,“一个”)并更新它的值(第一个 1 ,然后是 2,最后是 6)。长度为 4 的密钥(“四”和“五”)也是如此。

如何让树图插入我定义的所有键(“一”、“二”、“三”、“四”、“五”、“六”)按键的长度排序?

您需要一个双重比较器,首先比较字符串长度,然后比较字符串本身。喜欢:

public class MyTreeMapComparator implements Comparator<String> {
  public int compare(String one, String two) {
    if (one.length() == two.length()) {
      return one.compareTo(two);
    } else return two.length() - one.length();
  }
}

指定的顺序{one=1, two=2, six=6, four=4, five=5, three=3}不能用TreeMap实现,因为这个实现只能按键排序,一旦键的长度有多次冲突,额外的比较器需要值

因此,可以先创建原始地图,然后可以使用 Stream API Stream::sortedCollectors.toMap 以自定义顺序对其 entrySet 进行排序:

  1. 按密钥长度(升序)
  2. 按值(升序)

然后将结果收集到一个LinkedHashMap维护插入顺序的著名的:

Map<String, Integer> raw = new HashMap<>() {{
  put("one", 1);
  put("two", 2);
  put("three", 3);
  put("four", 4);
  put("five", 5);
  put("six", 6);
}};

Map<String, Integer> map = raw.entrySet()
    .stream()
    .sorted(Comparator.<Map.Entry<String, Integer>>comparingInt(e -> e.getKey().length())
        .thenComparingInt(Map.Entry::getValue))
    .collect(Collectors.toMap(
        Map.Entry::getKey,
        Map.Entry::getValue,
        (a, b) -> a,
        LinkedHashMap::new
    ));
System.out.println(map);

输出:

{one=1, two=2, six=6, four=4, five=5, three=3}

更新

对于给定的输入,似乎可以为 TreeMap 维护插入顺序提供自定义比较器:

Map<String, Integer> map2 = new TreeMap<>(
    Comparator.comparingInt(String::length)
        .thenComparing((k1, k2) -> 1) // pick the "earlier" key of the two
) {{
  put("one", 1);
  put("two", 2);
  put("three", 3);
  put("four", 4);
  put("five", 5);
  put("six", 6);
}};
System.out.println(map2);

输出:

{one=1, two=2, six=6, four=4, five=5, three=3}