在不破坏流管道的情况下引用前面的 Java 流步骤?
Posted
技术标签:
【中文标题】在不破坏流管道的情况下引用前面的 Java 流步骤?【英文标题】:Reference antecedent Java stream step without breaking the stream pipeline? 【发布时间】:2021-10-22 09:24:46 【问题描述】:我是函数式编程的新手,我正在努力变得更好。
目前,我正在试验一些采用以下基本形式的代码:
private static int myMethod(List<Integer> input)
Map<Integer,Long> freq = input
.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()));
return (int) freq
.keySet()
.stream()
.filter(key-> freq.containsKey(freq.get(key)))
.count();
首先使用哈希图来获取列表中每个数字的频率。接下来,我们总结了在地图中也作为键存在的键的数量。
我不喜欢两个流需要彼此分开存在的方式,其中 HashMap 是由一个流制成的,只是为了立即被另一个流专门使用。
有没有办法将它组合成一个流?我在想这样的事情:
private static int myMethod(List<Integer> input)
return (int) input
.stream()
.collect(Collectors.groupingBy(Function.identity(), Collectors.counting()))
.keySet()
.stream()
.filter(key-> freq.containsKey(freq.get(key)))
.count();
但这里的问题是没有可参考的频率映射,因为它被用作管道的一部分,因此过滤器无法完成它需要做的事情。
总之,我不喜欢这样收集到一个哈希图然后再转换回一个键集。有没有办法“简化”(双关语)这个操作来
-
不会在流和 hashmap 之间来回切换
以某种方式引用自身,而无需在管道之前声明单独的映射。
谢谢!
【问题讨论】:
您的建议应该有效。但是,我在您的建议中看到了一个问题。你在哪里获取频率来检索密钥? Freq 不存在,这是代码示例 2 中的问题。我不知道是否有自我引用的方法,如果没有 - 那么 freq 必须像代码示例中一样单独声明1. 你可以使用collectingAndThen()
,但不会更漂亮。
只是为了确保我得到你是这个 .filter(key-> freq.containsKey(freq.get(key)) 来检查值是否等于其中一个键或者我错过了了解一下
这永远不会是真的:freq.containsKey(freq.get(key))
。 freq.get(key)
返回一个Long
,映射的键是Integer
。 Long
对象永远不会等于 Integer
对象(原始 long
可能等于原始 int
,但这是另一回事)。
【参考方案1】:
您的密钥集实际上是由您的input
组成的HashSet
。所以,你应该使用临时存储,这样:
Set<Integer> freq = new HashSet<>(input);
并进一步计数,根据单个流管道中的值进行过滤
return (int) input
.stream()
.collect(Collectors.groupingBy(Function.identity(),
Collectors.counting()))
.values() // just using the frequencies evaluated
.stream()
.filter(count -> freq.contains(count.intValue()))
.count();
【讨论】:
花费 CPU 时间和堆内存来创建冗余HashSet
,只是为了让两个操作显示为单个语句,这是一个值得怀疑的权衡。当结果甚至不是一个语句,而是两个语句时,它已经到了离奇的程度,就像以前一样。
@Holger 你能详细说明一下,为什么HashSet
是多余的?您是否试图暗示最初的List
可以用于执行contains
检查?或者containsKey
+ get
的性能相同,但内存消耗更低?
当然,HashMap
上的containsKey
或get
与HashSet
上的contains
具有相同的性能(实际上,在OpenJDK 中,HashSet
是一个包装器围绕HashMap
,所以contains
在引擎盖下调用containsKey
)。通过流式传输values()
而不是keySet()
,您获得了小幅改进,就像您在答案中所做的那样,无需为每个键调用get
,但这不需要额外的freq
集。剩余的contains
调用与containsKey
调用相当。以上是关于在不破坏流管道的情况下引用前面的 Java 流步骤?的主要内容,如果未能解决你的问题,请参考以下文章
MessagePack Java Jackson 在不关闭输出流(output stream)的情况下序列化多变量
MessagePack Java Jackson 在不关闭输入流(input stream)的情况下反序列化多变量