如何在Java Stream表达式中优雅集成计数变量?
解决Java Stream中集成计数变量的优雅方案
问题分析
你原始代码存在两个核心问题:
- 在
forEach的lambda内部初始化int i = 0,导致每次循环都会重置计数,最终永远输出[0]; - 若将
i移到forEach外部,lambda要求捕获的变量是final或有效final,i++会破坏这个规则,直接导致编译失败。
下面是几种更优雅的实现方式:
方案1:先收集行到列表,结合IntStream.range生成索引
这种方式直观且兼容Java 8+,先将所有行收集到列表,再通过索引关联计数:
void appendLines(StringBuilder sb, String str) { List<String> lineList = str.lines().collect(Collectors.toList()); IntStream.range(0, lineList.size()) .forEach(index -> sb.append("\n [") .append(index) .append("] ") .append(lineList.get(index)) ); }
方案2:使用Stream.iterate生成带索引的流(Java 9+)
利用Stream.iterate生成包含索引和行元素的配对流,结合takeWhile终止迭代:
void appendLines(StringBuilder sb, String str) { Iterator<String> lineIter = str.lines().iterator(); // 种子为(初始索引, 行迭代器)的配对 Stream.iterate(Map.entry(0, lineIter), entry -> entry.getValue().hasNext()) .map(entry -> Map.entry(entry.getKey(), entry.getValue().next())) .forEach(entry -> sb.append("\n [") .append(entry.getKey()) .append("] ") .append(entry.getValue()) ); }
这里Stream.iterate的第一个参数是种子值(初始索引0+行迭代器),第二个参数是迭代终止条件(迭代器还有元素时继续),后续通过map取出当前索引和行内容,最后完成拼接。
方案3:使用AtomicInteger(单线程场景下简洁)
如果不想提前收集所有行,单线程环境下可以用AtomicInteger作为可变计数(虽然它是线程安全的,但单线程场景下使用完全没问题):
void appendLines(StringBuilder sb, String str) { AtomicInteger counter = new AtomicInteger(0); str.lines().forEach(line -> sb.append("\n [") .append(counter.getAndIncrement()) .append("] ") .append(line) ); }
内容的提问来源于stack exchange,提问作者CoSoCo
相关产品推荐
相关产品推荐

