在不破坏流管道的情况下引用前面的 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 流步骤?的主要内容,如果未能解决你的问题,请参考以下文章