Scala中incomplete.map{}行为解析及输出顺序异常排查
问题解答:
incomplete.map{}的作用与输出顺序问题 咱们先直接给结论:你的猜测不对,incomplete.map{}完全没有启动新线程,输出顺序混乱的核心原因是你后来发现的——Scala里filterKeys是惰性求值的。下面咱们一步步拆解清楚:
一、incomplete.map{}的真实作用
incomplete是Option[LocalDate]类型,Option.map是Scala处理可选值的标准方法,逻辑非常简单:
- 只有当
incomplete是Some(某个具体日期)时,才会执行map里的代码块,返回处理后的结果 - 如果
incomplete是None,整个map块的代码会直接跳过,返回None
对应到你的代码里:
incomplete.map { i => println(s"i = $i") byDate.filterKeys { d => println(s"dd = $d vs i = $i") } }
这段代码的意思是:
- 要是
incomplete有值(Some),就先打印这个值,然后给byDate加一个过滤规则(注意:只是加规则,没真执行) - 要是
incomplete没值(None),这堆代码啥也不做,直接返回None
最后通过getOrElse(byDate)兜底:如果map返回None,就直接用原byDate。
二、为什么输出会被后续代码分隔?全是惰性求值的锅
你找到的原因完全正确:filterKeys不会立刻执行过滤和打印操作,它只是创建了一个待计算的视图(view)。只有当filtered这个变量被后续代码实际使用的时候(比如遍历、取长度、转成普通Map),才会触发真正的计算,执行里面的println语句。
所以你看到的输出顺序是这么来的:
- 定义
filtered,此时filterKeys的逻辑只是“存起来”,没执行 - 后续代码先跑,输出了一堆内容
- 后续某个地方用到了
filtered,才触发filterKeys的计算,打印那些dd = ... vs i = ...的内容 - 要是
filtered被多次使用,filterKeys的逻辑会重复执行,这就是你看到重复打印的原因(如果想避免重复计算,可以把结果转成普通Map:.filterKeys(...).toMap)
三、验证小技巧
你可以做个简单测试验证这个逻辑:在定义filtered之后立刻加一行代码强制触发计算:
val filtered = (incomplete.map { i => println(s"i = $i") byDate.filterKeys { d => println(s"dd = $d vs i = $i") } }).getOrElse(byDate) // 强制触发计算 filtered.size
这时候你会发现,打印语句会立刻执行,不会被后续代码的输出分隔开,彻底排除多线程的可能。
内容的提问来源于stack exchange,提问作者BAE
相关产品推荐
相关产品推荐

