如何用Java Streams改写含continue的do-while循环并处理大字符串?
Java函数式编程改写字段匹配代码的问题
我尝试用Java函数式编程(比如Streams)改写下面这段代码。输入字符串str的格式是用竖线|和等号=分隔的,结构为FieldName1=FieldValue1|FieldName2=FieldValue2|……,这段代码的作用是从指定起始索引startIdx开始,找到第一个匹配fieldName的字段并返回对应的值。我想知道怎么用Java Streams处理原代码里的continue逻辑?另外,输入字符串str体积很大,不确定先完全拆分再用Stream处理是否合理。
示例输入
- str=Bond=US922646AS37|Isin=US922646AS37|Cusip=922646AS3|Price=100.5|Currency=USD|Position=2000000|.......|Bond=US69318FAJ75|Isin=US69318FAJ75|Cusip=69318FAJ7|Price=102.5|Currency=USD|Position=1000000|.....
- fieldName=Price
- startIdx=100
输出
102.5
原代码
String process(String fieldName, int startIdx, String str) { do { int start = str.indexOf(fieldName, startIdx); if (start == -1) { return null; } if ( start > 0 && str.charAt(start-1) != '|' ) { startIdx = start+1; continue; } int val = str.indexOf("=", start); if ( val == -1 ) { return null; } if (val - fieldName.length() != start) { int end = str.indexOf("|", startIdx); if (end == -1) { return null; } start = end + 1; continue; } int end = str.indexOf("|", val); if (end == -1) { return str.substring(val + 1); } else { return str.substring(val + 1, end); } } while (true); }
关于Streams处理continue逻辑的思路
原代码里的continue本质是跳过当前不合法的匹配,继续寻找下一个符合条件的字段。用Streams处理的话,可以通过**过滤(filter)**操作实现类似逻辑:把符合匹配规则的字段筛选出来,跳过不符合的,然后取第一个匹配的结果。
大字符串的处理合理性分析
如果直接用split("\\|")拆分大字符串,会一次性把整个字符串加载到内存并分割成数组,当字符串体积非常大时,会占用大量内存,甚至可能引发OOM。所以这种方式不合理。
更高效的方式是流式遍历字符串的子序列,不需要完全拆分整个字符串,而是逐个定位字段的位置,类似原代码的循环逻辑,但用Stream的方式封装。
基于Stream的改写实现
可以通过生成一个无限流来模拟原代码的循环,每次迭代寻找下一个fieldName的位置,然后校验规则,直到找到符合条件的字段或者遍历结束:
import java.util.Optional; import java.util.stream.Stream; public class FieldExtractor { public static String processWithStream(String fieldName, int startIdx, String str) { int fieldLength = fieldName.length(); return Stream.iterate(startIdx, currentIdx -> { int nextMatch = str.indexOf(fieldName, currentIdx); if (nextMatch == -1) { return -1; // 终止信号 } // 检查是否是字段开头 boolean isFieldStart = nextMatch == 0 || str.charAt(nextMatch - 1) == '|'; if (!isFieldStart) { return nextMatch + 1; // 跳过当前位置,继续找下一个 } // 检查是否紧跟= int eqPos = str.indexOf("=", nextMatch); if (eqPos == -1 || eqPos != nextMatch + fieldLength) { // 跳过当前字段,找到下一个|的位置继续 int pipePos = str.indexOf("|", nextMatch); return pipePos == -1 ? -1 : pipePos + 1; } // 符合条件,返回特殊值终止流 return -2; }) .takeWhile(idx -> idx != -1 && idx != -2) .reduce((prev, curr) -> curr) // 取最后一个迭代的索引,即符合条件前的最后一个位置 .map(lastIdx -> { int matchPos = str.indexOf(fieldName, lastIdx); int eqPos = str.indexOf("=", matchPos); int pipePos = str.indexOf("|", eqPos); return pipePos == -1 ? str.substring(eqPos + 1) : str.substring(eqPos + 1, pipePos); }) .orElse(null); } }
代码说明
Stream.iterate生成迭代流,每次迭代更新查找的起始索引:- 如果找不到匹配,返回
-1终止流 - 如果匹配位置不是字段开头,返回
nextMatch + 1继续查找 - 如果字段名后没有紧跟
=,找到下一个|的位置,从那里继续查找 - 符合所有条件时返回
-2终止流
- 如果找不到匹配,返回
takeWhile控制流的终止条件,直到找到合法匹配或遍历结束reduce获取最后一次迭代的索引,用来定位合法匹配的位置,然后提取字段值
这种方式不需要拆分整个大字符串,而是按需查找位置,内存效率和原代码相当,同时用Stream的方式实现了函数式编程风格。
内容的提问来源于stack exchange,提问作者Sushil
相关产品推荐
相关产品推荐

