假设我有一组字符串和一个哈希函数(或任何单边函数)和一个测试函数。我想创建一个从输入字符串到其哈希值的映射,它使用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循环是最简洁的解决方案。
答案 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
操作是为了确保每个值仅作为一个键出现一次 - 它们将映射到相同的哈希值。