为何Kotlin的generateSequence在示例中返回的元素数量多一个?
问题根源:Sequence与ArrayList实现的判断时机差异
你的问题核心在于两种实现对元素是否符合边界条件的判断时机完全不同,导致Sequence版本多返回了一个超出toExclusive的元素。咱们一步步拆解:
先看ArrayList版本的逻辑(正确行为)
ArrayList的代码是典型的「先判断,再加入」流程:
var it = cron.next(fromInclusive.minusNanos(1)) while (it !== null) { if (it.isBefore(toExclusive)) { // 先判断当前元素是否符合要求 list.add(it) // 符合才加入列表 it = cron.next(it) } else { break // 不符合直接跳出,不加入 } }
以你的测试用例为例:当生成到2021-02-01T00:00时,这个元素不满足isBefore(toExclusive)(因为toExclusive就是2021-02-01T00:00),所以直接break,不会把这个元素加入列表。最终列表只有31个元素,符合预期。
再看原Sequence版本的逻辑(问题所在)
generateSequence的工作机制是:先把初始值加入序列,然后每次用lambda生成下一个元素,直到lambda返回null为止。你的原代码逻辑是:
generateSequence(cron.next(fromInclusive.minusNanos(1))) { if (it.isBefore(toExclusive)) { // 判断的是前一个元素是否符合要求 cron.next(it) // 符合就生成下一个元素(直接加入序列) } else { null } }
这里的坑在于:lambda判断的是前一个元素是否在边界内,然后生成下一个元素——但生成的下一个元素并没有被判断,直接就加入序列了!
还是拿测试用例说:
- 初始值是
2021-01-01T00:00,加入序列 - 每次前一个元素(比如
2021-01-31T00:00)符合isBefore(toExclusive),所以生成下一个元素2021-02-01T00:00,这个元素直接加入序列 - 接下来判断
2021-02-01T00:00是否符合条件,发现不符合,返回null,序列停止 - 最终序列里包含了
2021-02-01T00:00这个多余的元素,导致size变成32,断言失败
修复Sequence版本的方法
只需要调整逻辑,让生成的每个元素都经过边界判断即可,最简洁的方式是用takeWhile过滤:
fun instants( fromInclusive: LocalDateTime = LocalDateTime.now(), toExclusive: LocalDateTime = fromInclusive.plusMonths(1) ): Sequence<LocalDateTime> { return generateSequence(cron.next(fromInclusive.minusNanos(1))) { cron.next(it) } .takeWhile { it.isBefore(toExclusive) } }
这个逻辑和ArrayList版本完全对齐:先生成所有可能的后续元素,然后只保留那些严格小于toExclusive的元素,这样就不会出现多余的元素了。
内容的提问来源于stack exchange,提问作者Matthew Adams
相关产品推荐
相关产品推荐

