在java流中,使用.peek()被视为仅用于调试目的,日志记录会被视为调试吗?



所以我有一个对象列表,我希望处理部分或全部,我想记录那些已处理的对象。

考虑一个虚构的例子:

List<ClassInSchool> classes;
classes
.stream()
.filter(verifyClassInSixthGrade())
.filter(classHasNoClassRoom())
.peek(classInSchool -> log.debug("Processing classroom {} in sixth grade without classroom.", classInSchool)
.forEach(findMatchingClassRoomIfAvailable());

在这种情况下使用 .peek() 会被视为对 API 的意外使用吗?

为了进一步解释,在这个问题上,关键要点是:">不要以意想不到的方式使用 API,即使它实现了你的直接目标。我的问题是,在调试流直到您验证整个链按设计工作并再次删除 .peek() 之前,每次使用 peek 是否都是意外用途。因此,如果将其用作记录流实际处理的每个对象的方法,则被视为意外用途。

peek的文档将意图描述为

此方法主要是为了支持调试,在调试中,您希望在元素流过管道中的某个点时查看它们。

.peek(classInSchool -> log.debug("Processing classroom {} in sixth grade without classroom.", classInSchool)形式的表达式满足了这一意图,因为它是关于报告元素的处理。无论您使用日志记录框架还是仅打印语句都无关紧要,如文档的示例.peek(e -> System.out.println("Filtered value: " + e)).无论哪种情况,意图都很重要,而不是技术方法。如果有人使用peek打印所有元素,即使它使用与文档示例相同的技术方法(System.out.println),那也是错误的。

该文档并未要求您必须区分生产环境或调试环境,以删除前者的peek用法。实际上,您的使用甚至可以实现这一点,因为日志记录框架允许您通过可配置的日志记录级别将该操作静音。

我仍然建议记住,对于某些管道,插入peek操作可能会比实际操作插入更多的开销(或在一定程度上阻碍 JVM 的循环优化)。但是,如果您没有遇到性能问题,则可以遵循旧建议,除非您有真正的理由,否则不要尝试优化...

应避免窥视,因为对于某些终端操作,它可能不会被调用,请参阅此答案。在该示例中,最好在forEach的操作中进行日志记录,而不是使用peek。在这种情况下,调试意味着用于修复 bug 或诊断问题的临时代码。

在 Java 流中,使用.peek()被视为仅用于调试目的,日志记录会被视为调试吗?

这取决于您的日志记录代码是否将成为代码的永久固定装置。

只有您才能真正知道日志记录的真正目的...

另请注意,javadoc 说:

如果流实现能够优化部分或全部元素的产生(例如使用短路操作,如findFirst,或在count()中描述的示例中),将不会为这些元素调用操作。

因此,您可能会发现,在某些情况下,peek不是记录(或调试)管道的可靠方法。

通常,添加peek可能会改变管道的行为和/或 JVM 优化它的能力......在当前或未来一代 JVM 中。

呃,它有点可以解释。意图并不总是那么容易确定。

我认为添加 API 说明主要是为了阻止过度使用peek,因为几乎所有理想的行为都可以在没有它的情况下完成。对于开发人员来说,完全排除它太有用了,但他们希望明确,它的包含不应被视为无条件的认可;他们看到了滥用的可能性,并试图解决这个问题。

我怀疑 - 尽管我只是猜测 - 对于是否包含它存在不同的意见,并且在JavaDoc中包含带有警告的版本是妥协。

考虑到这一点,我认为我决定何时使用peek的建议很简单:除非你有很好的理由,否则不要使用它。

就你而言,你绝对没有充分的理由。您正在迭代所有内容并将结果传递给方法findMatchingClassRoomIfAvailable(嗯,大概 - 您的示例不是很好)。如果您想为流中的每个项目记录一些内容,则只需将其记录在该方法的顶部即可。

是误用吗?我不这么认为。我会这样写吗?不。

最新更新