如何在Java Stream中实现MatchResult到String的归约操作?
问题解答:将
MatchResult流归约为String 问题描述
我希望将MatchResult流归约为String。标准库中是否有对应的方法,还是必须自定义Collector?
目前我的方法实现如下:
public static String pointUnderlineRunnables(String line, String regex) { List<MatchResult> matches = Pattern.compile(regex).matcher(line).results() // 查找匹配项 .filter(mRes -> ProgramHelper.isRunnableCode(mRes.start(), line)) // 过滤掉字符串常量和注释 .toList(); String underline = ""; for (MatchResult m : matches) underline += " ".repeat(m.start() - underline.length()) + "^".repeat(m.end() - m.start()); return line.strip() + "\n" + underline; }
但我想写出类似下面的代码却做不到,因为现有映射方法不支持:
public static String pointUnderlineRunnables(String line, String regex) { return line.strip() + "\n" + Pattern.compile(regex).matcher(line).results() // .filter(mRes -> ProgramHelper.isRunnableCode(mRes.start(), line)) // .reduce("", (subtotal, elem) // -> subtotal + " ".repeat(elem.start() - subtotal.length()) + "^".repeat(elem.end() - elem.start()); }
原因是reduce要求累加器的类型和流元素类型必须相同。有没有我没注意到的其他方法可以实现这个需求?
补充说明:该方法会给行中所有正则匹配项加上下划线,仅排除少数经过过滤的内容。
示例
输入
- line: "I love food, food is cool!"
- regex: "food"
输出
I love food, food is cool! ^^^^ ^^^^
解决方案
不需要自定义Collector,可以直接用流的collect方法结合StringBuilder实现,既符合流API的使用习惯,又比字符串拼接的性能更好(避免大量中间字符串对象)。
实现代码如下:
public static String pointUnderlineRunnables(String line, String regex) { String strippedLine = line.strip(); String underline = Pattern.compile(regex).matcher(line).results() .filter(mRes -> ProgramHelper.isRunnableCode(mRes.start(), line)) .collect(StringBuilder::new, (sb, match) -> { // 补空格到当前匹配的起始位置 int currentLength = sb.length(); if (match.start() > currentLength) { sb.append(" ".repeat(match.start() - currentLength)); } // 添加对应长度的下划线符号 sb.append("^".repeat(match.end() - match.start())); }, StringBuilder::append) .toString(); return strippedLine + "\n" + underline; }
方法说明
collect的三个核心参数作用:
- 供应器:
StringBuilder::new,创建一个可变的累加容器,避免字符串拼接的性能损耗 - 累加器:处理每个
MatchResult,先补全匹配起始位置前的空格,再添加对应长度的^符号 - 组合器:
StringBuilder::append,用于并行流场景下合并不同线程的累加结果(串行流中该参数不会生效,但必须声明)
你尝试的reduce确实不适合这个场景——reduce针对的是不可变归约,要求累加输入和输出类型一致;而这里需要从MatchResult转换为字符串类型的可变累加,collect才是标准库中对应的解决方案。
内容的提问来源于stack exchange,提问作者xtay2
相关产品推荐
相关产品推荐

