当前位置: 首页 > 知识库问答 >
问题:

在Java8中,何时在收集器中触发合并功能。托马普?

通远
2023-03-14

我想转换哈希映射

代码如下。但是当初始Hashmap中存在相同长度的键时,它会触发抛出Exception的合并函数(我打算这样做,因为在我的情况下不会有相同的字符串)。我想知道为什么会触发合并函数,因为toMap()的JavaDoc说“如果映射的键包含重复项(根据Object#equals(Object)),则将值映射函数应用于每个相等的元素,并使用提供的合并函数合并结果。”我认为在我的代码中,“映射键”应该是由Entry::getKey映射的hashMap中的条目,而不是TreeMap比较器中的string.length()。即“abc" ! = "def”。所以它不应该触发合并。但是??搞什么鬼?

public class TestToMap {

    public static Map<String, Long> map1 = new HashMap<String, Long>() {
        {
            put("abc", 123L);
            put("def", 456L);
        }
    };

    public static void main(String[] args) {
        Map<String, Long> priceThresholdMap = map1.entrySet().stream()
            .collect(Collectors.toMap(Entry::getKey,
                                      Entry::getValue,
                                      throwingMerger(),
                                      () -> new TreeMap<String, Long>(
                                          (a, b) -> {
                                              return a.length() - b.length();
                                          }))); // this will trigger merge function, why?
        //() -> new TreeMap<String, Long>(Comparator.comparingInt(String::length).thenComparing(String::compareTo))));  // but this won't trigger merge function 

    }

    private static <T> BinaryOperator<T> throwingMerger() {
        return (u, v) -> {
            throw new IllegalStateException(String.format("priceThresholdMap has duplicate v1 %s,v2 %s", u, v));
        };
    }
}

共有2个答案

范鸿
2023-03-14

根据toMap()源代码,它创建一个累加器,将源流中的每个元素折叠到map中。

Collector<T, ?, M> toMap(Function<? super T, ? extends K> keyMapper,
                                Function<? super T, ? extends U> valueMapper,
                                BinaryOperator<U> mergeFunction,
                                Supplier<M> mapSupplier) {
        BiConsumer<M, T> accumulator
                = (map, element) -> map.merge(keyMapper.apply(element),
                                              valueMapper.apply(element), mergeFunction);
        return new CollectorImpl<>(mapSupplier, accumulator, mapMerger(mergeFunction), CH_ID);
    }

在地图上。merge(),当get(“def”)时,将返回exist oldValue=123,该键是“abc”,因为通过我给TreeMap的比较器,“def”等于“abc”。然后是oldValue=null调用合并函数。

 default V merge(K key, V value,
            BiFunction<? super V, ? super V, ? extends V> remappingFunction) {
        Objects.requireNonNull(remappingFunction);
        Objects.requireNonNull(value);
        V oldValue = get(key);
        V newValue = (oldValue == null) ? value :
                   remappingFunction.apply(oldValue, value); // call the merge function
        if(newValue == null) {
            remove(key);
        } else {
            put(key, newValue);
        }
        return newValue;
    }

参考:收集器映射重复密钥

文鸣
2023-03-14

当然,它应该触发合并。merge函数用于合并输出映射中具有相同键的值,在您的示例中,该映射是树映射。

在树映射中,如果比较器的比较方法返回0,则键是相同的,因此具有相同长度的两个键被视为相同的,应该合并它们相应的值。

请注意,比较器会导致输出树映射(TreeMap)无法正确实现映射(Map)接口,因为它定义的顺序与等于(equals)不一致:

请注意,与任何排序映射一样,树映射维护的排序以及是否提供显式比较器,如果此排序映射要正确实现Map接口,则必须与equals一致

(来自树形图)

如果要按长度对字符串进行排序,仍然可以与等于一致:

而不是

return a.length() - b.length()

使用

return a.length() == b.length() ? a.compareTo(b) : Integer.compare(a.length(),b.length())

现在具有相同长度的不相等Strings将按字典顺序排列,而具有不同长度的String将按长度排序。

 类似资料:
  • 问题内容: 示例程序: 因此,为简化起见,没有最终转换,因此生成的代码非常简单。 现在,产生一个顺序流。我只是将结果装箱到s中,然后将其收集到中。很简单 而且,无论我运行此示例程序多少次,都不会成功,这意味着永远不会调用我的虚拟组合器。 我有点期望,但是后来我已经误解了流,以至于我不得不问这个问题… 可以将的组合时,流过被称为 保证 是连续的? 问题答案: 仔细阅读ReduceOps.java中的

  • Google Apps Script支持将事件传递给触发函数的触发器。不幸的是,开发环境将允许您在没有参数传递的情况下测试函数,因此您无法以这种方式模拟事件。如果您尝试,您会得到一个错误,如: 未定义e。 或者 TypeError:无法读取属性*从未定义 (其中未定义) 人们可以将事件视为可选参数,并使用JavaScript中是否有更好的方法来执行可选函数参数的任何技术将默认值插入触发器函数。但这

  • 在jdk8中,Stream提供带有、和的功能 我在注释中看到了将String Stream转换为String的示例,但我很困惑组合器在函数签名中的用途。我认为在累加器中,新元素已添加到结果大陆? 我尝试给组合器一个null值,但我得到了一个null指针异常。

  • 我想迭代一个对象列表。每个对象都包含一些对象的变量。更具体地说: 我的名单是: 所需的映射结构是:

  • 示例程序: 所以,为了简化这里的问题,没有最终的转换,所以得到的代码非常简单。

  • 问题内容: 我为作业配置了两个构建触发器(轮询SCM和FStrigger- MonitorFiles),比如说轮询计划为每日轮询(@daily),现在,如果轮询SCM检测到更改或FStrigger- MonitorFiles这样做是正常行为,则触发构建。 但是,如果两个都检测到更改,则每次触发器尝试触发构建时都会触发两次构建,那么我该如何实现每天将上述两个触发器的结果组合在一起并且仅触发/不触发一