【问题标题】:Merging two TreeMaps while skipping the first entry in Java跳过Java中的第一个条目时合并两个TreeMap
【发布时间】:2018-02-21 23:42:27
【问题描述】:

我有两个要合并的 TreeMap:

TreeMap<foo, bar> map1 = {key1=value1, key2=value2};
TreeMap<foo, bar> map2 = {key1=value6, key3=value3, key4=value4, key5=value5};

我想将这些地图合并成一个,但我想跳过第二张地图的第一个条目,所以我会有这样的东西:

TreeMap<foo, bar> result = {key1=value1, key2=value2, key3=value3, key4=value4, key5=value5};

但是,我不能打电话给map2.remove(map2.firstKey()) 甚至map2.remove(key1),因为equalsfoo 上不能正常工作。

有什么好办法吗?

(目前我正在这样做,效果很好,但我对它不太满意,我认为这不是最好的方法:

TreeMap<foo, bar> result = new TreeMap<>();
result.putAll(map1);

for (Map.Entry<foo, bar> entry : Iterables.skip(map2.entrySet(), 1) ) {
  result.put(entry.getKey(), entry.getValue());
}

)

【问题讨论】:

  • 您的第二张地图有 2 个名为 key3 的键。是错字吗?
  • because equals does not work fine on foo 你应该先修复你的 equals 方法。否则,TreeMap 的所有使用都是徒劳的,因为它可能会或可能不会达到您的预期。
  • @Mritunjay 谢谢,已修复
  • @Absurd-Mind 谢谢,但它是我无权访问的大项目的一部分,需要时间来修复。此外,我所依赖的 TreeMap 的唯一部分是两个地图具有相同的“第一个元素”,TreeMap 不会更改顺序,因此它是可靠的。
  • TreeMap 不使用equals

标签: java dictionary merge treemap


【解决方案1】:

最简单的方法是:

TreeMap<Foo, Bar> result = new TreeMap<>(map2);
result.putAll(map1);

你说两个地图总是有第一个共同的元素,你似乎想保留 map1 的第一个值。因此,您只需要对整个地图做一个简单的putAll,这样map1的第一个值就会覆盖map2的第一个值。不需要任何额外的代码来跳过 map2 的第一个条目。

更新:根据您的评论,Foo 的 compareTo 方法似乎无法正常工作。因此,您需要创建自己的自定义 Comparator,并将其传递给您创建的所有 TreeMap 的 constructors

你可以使用一些类似这样的代码:

Comparator<Foo> myFooComparator = Comparator
        .comparing(Foo::getName)
        .thenComparingInt(Foo::getScore);

TreeMap<Foo, Bar> result = new TreeMap<>(myFooComparator);
result.putAll(map2);
result.putAll(map1);

其中String Foo.getName()int Foo.getScore() 是 Foo 的两个公共方法,用作比较两个 Foo 对象时需要比较的示例。

【讨论】:

  • 正如我所说,由于 equals (可能还有 foo 的 hashcode)有问题,不幸的是这个解决方案不起作用,我最终得到了 {key1=value1, key2=value2, key1=value6, key3=value3, key4=value4, key5=value5}
  • @SalehPirouzi TreeMap 根本不使用equals。它也不使用hashCode。它使用compareTo。如果您的 compareTo 方法行为不正确,您将需要使用自己的自定义比较器构造 TreeMaps。
【解决方案2】:

这里有一个关于 Streams (Java 8) 的建议:

public static void main(String[] args) {
    TreeMap<String, String> map1 = new TreeMap<>();
    map1.put("key1", "value1");
    map1.put("key2", "value2");
    TreeMap<String, String> map2 = new TreeMap<>();
    map2.put("key1", "value6");
    map2.put("key3", "value3");
    map2.put("key4", "value4");
    map2.put("key5", "value5");
    String first = map2.entrySet().iterator().next().getKey();
    Map<String, String> res =
            Stream.of(map1.entrySet(), map2.entrySet().stream().filter(e -> !e.getKey().equals(first)).collect(Collectors.toList()))
                    .flatMap(Collection::stream)
                    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (a, b) -> a.compareTo(b) >= 0 ? a : b, TreeMap::new));
    System.out.println(res);
}

打印出来:

{key1=value1, key2=value2, key3=value3, key4=value4, key5=value5}

使用此解决方案,两个输入映射不会改变。

【讨论】:

    【解决方案3】:

    你可以使用tailMap:

    TreeMap<foo, bar> result = new TreeMap<>(map1);
    result.putAll (map2.tailMap (map2.keySet().iterator().next(), false));
    

    编辑:您不需要找到第二个键。您可以使用 inclusive == false 运行 tailMap,并将第一个键传递给它,该键将被排除。

    【讨论】:

    • 感谢您的回答,但这不起作用,因为 map2.tailMap(key2) 返回一个空的 TreeMap。我想tailMap 使用equals 来定位与输入键匹配的条目。由于 equals 在所有项目上都返回 false,因此您的代码结果将是 map1
    • 您使代码更清晰,但我的问题仍然存在。由于foo.equals()无法比较,map2.tailMap(anykey)返回空TreeMap(anykey是否存在无关紧要,tailmap找不到)。
    • @SalehPirouzi 你的问题不可能是由于 foo 没有正确实现equals,因为TreeMap 从不调用equals()。它只使用compareTo()。您的问题不是由equals 引起的。
    • @KlitosKyriacou,我不确定它有什么问题。我认为它与 equalshashcode 有关
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2023-01-05
    • 1970-01-01
    • 1970-01-01
    • 2012-04-22
    • 1970-01-01
    • 2011-12-24
    • 1970-01-01
    相关资源
    最近更新 更多