如何使用映射值创建地图?

时间:2016-01-14 04:04:43

标签: java java-8 java-stream

假设我有一组字符串和一个哈希函数(或任何单边函数)和一个测试函数。我想创建一个从输入字符串到其哈希值的映射,它使用Java 8流传递测试函数。我的问题是如何在keyMapper编写Collectors.toMap()

伪代码:

Map<String, String> result = inputStrings.stream()
    .map(str -> hashFunc(str))
    .filter(hash -> hash.startsWith("00"))
    .collect(Collectors.toMap(hash -> ???,  // the original input string is lost
                              Function::identity));

在其他函数式编程语言中,我可以使用过滤的哈希流压缩输入流,但Java 8没有zip。此外,在map()中,我可以返回输入字符串对和散列值,以便输入将向下传递给收集器。但Java 8也没有对或元组。

似乎旧的for循环是最简洁的解决方案。

3 个答案:

答案 0 :(得分:4)

你是对的,没有lambda会在那里工作。有一些替代选项,但我使用的选项是:

Map<String, String> result = inputStrings.stream()
    .map(str -> new AbstractMap.SimpleImmutableEntry<>(str, hashFunc(str)))
    .filter(entry -> entry.getValue().startsWith("00"))
    .collect(Collectors.toMap(Entry::getKey, Entry::getValue));

(如果我没有收集到Map,我会创建适合用例的自定义元组类型,而不是使用Map.Entry,但此处Map.Entry已足够类型。)

答案 1 :(得分:2)

嗯,即使Java允许纯功能解决方案,但是,它的可读性严重受到以下事实的影响:

Map<String, String> result = inputStrings.stream()
    .map(str -> { String hash=hashFunc(str);
                  return (Function<BinaryOperator<String>,String>)f->f.apply(str, hash); })
    .filter(f -> f.apply((s,hash)->hash).startsWith("00"))
    .collect(Collectors.toMap(f->f.apply((s,hash)->s), f->f.apply((s,hash)->hash)));

如果被拒绝的条目数量与接受条目的数量相比预期相当低,您可以简单地创建一个完整的地图,然后删除错误的条目:

Map<String, String> result = inputStrings.stream()
    .collect(Collectors.collectingAndThen(
                 Collectors.toMap(Function.identity(), str -> hashFunc(str)),
                 map -> { map.values().removeIf(s->!s.startsWith("00")); return map; }));

这可能比将元素和散列结果包装到任何对类型中更有效,最后将它们添加到Map(创建另一种特定于地图的对,即Map.Entry)。但当然,它可能会有更高的偷看内存使用率。

答案 2 :(得分:0)

如果散列函数很便宜,您可以在映射之前进行过滤。

Map<String, String> result = inputStrings.stream()
    .filter(val -> hashFunc(val).startsWith("00"))
    .distinct()
    .collect(Collectors.toMap(Function.identy(), this::hashFunc));

distinct操作是为了确保每个值仅作为一个键出现一次 - 它们将映射到相同的哈希值。