Java-Stream & Optional - 查找与 stream-element 匹配的值或提供默认值



我有一个Dictionary对象,它由几个条目组成:

record Dictionary(String key, String value, String other) {};

我想将给定CCD_ 2中的单词替换为a;键";在具有相应值的词典之一中。我可以做到这一点,但我想,一定有更好的方法。

一个例子:

> Input: One <sup>a</sup> Two <sup>b</sup> Three <sup>D</sup> Four
> Output: One [a-value] Two [b-value] Three [D] Four

需要改进的代码:

public class ReplaceStringWithDictionaryEntries {
public static void main(String[] args) {
List<Dictionary> dictionary = List.of(new Dictionary("a", "a-value", "a-other"),
new Dictionary("b", "b-value", "b-other"));
String theText = "One <sup>a</sup> Two <sup>b</sup> Three <sup>D</sup> Four";
Matcher matcher = Pattern.compile("<sup>([A-Za-z]+)</sup>").matcher(theText);
StringBuilder sb = new StringBuilder();
int matchLast = 0;
while (matcher.find()) {
sb.append(theText, matchLast, matcher.start());
Optional<Dictionary> dict = dictionary.stream().filter(f -> f.key().equals(matcher.group(1))).findFirst();
if (dict.isPresent()) {
sb.append("[").append(dict.get().value()).append("]");
} else {
sb.append("[").append(matcher.group(1)).append("]");
}
matchLast = matcher.end();
}
if (matchLast != 0) {
sb.append(theText.substring(matchLast));
}
System.out.println("Result: " + sb.toString());
}
}

输出:

Result: One [a-value] Two [b-value] Three [D] Four

你有更优雅的方法吗?

由于Java 9,Matcher#replaceAll可以接受一个回调函数来返回每个匹配值的替换。

String result = Pattern.compile("<sup>([A-Za-z]+)</sup>").matcher(theText)
.replaceAll(mr -> "[" + dictionary.stream().filter(f -> f.key().equals(mr.group(1)))
.findFirst().map(Dictionary::value)
.orElse(mr.group(1)) + "]");

使用key作为键,value作为值,从列表中创建一个映射,使用Matcher#appendReplacement方法替换使用上述映射并调用Map.getOrDefault的匹配,使用group(1)值作为默认值。使用String#join将替换项放在方括号中

public static void main(String[] args) {
List<Dictionary> dictionary = List.of(
new Dictionary("a", "a-value", "a-other"),
new Dictionary("b", "b-value", "b-other"));
Map<String,String> myMap = dictionary.stream()
.collect(Collectors.toMap(Dictionary::key, Dictionary::value));
String theText  = "One <sup>a</sup> Two <sup>b</sup> Three <sup>D</sup> Four";
Matcher matcher = Pattern.compile("<sup>([A-Za-z]+)</sup>").matcher(theText);
StringBuilder sb = new StringBuilder();
while (matcher.find()) {
matcher.appendReplacement(sb, 
String.join("", "[", myMap.getOrDefault(matcher.group(1), matcher.group(1)), "]"));
}
matcher.appendTail(sb);
System.out.println(sb.toString());
}
record Dictionary( String key, String value, String other) {};

地图与列表

正如@Chaosfire在评论中指出的那样,MapList更适合任务,因为它消除了访问特定元素时迭代集合的需要

Map<String, Dictionary> dictByKey = Map.of(
"a", new Dictionary("a", "a-value", "a-other"),
"b", new Dictionary("b", "b-value", "b-other")
);

我还建议用一个类包装Map,以便提供对字典字符串值的大陆访问,否则我们必须检查从映射返回的字典是否不是null,然后才进行调用以获得所需的值,这很不方便。实用程序类可以方便地在单个方法调用中获取目标值。

为了避免使答案复杂化,我不会实现这样一个实用程序类,为了简单起见,我将使用Map<String,String>(它基本上充当一个用于操作的实用程序类——在单个调用中提供值(。

public static final Map<String, String> dictByKey = Map.of(
"a", "a-value",
"b", "b-value"
);

Pattern.splitAsStream((

我们可以用通过splitAsStream()创建的流来替换while-循环。

为了区分标记<sup>text</sup>所包含的字符串值,我们可以使用被称为Lookbacking(?<=</sup>(和Lookahead的特殊构造(?=<sup>(。

  • (?<=foo)-匹配String0之前的位置
  • CCD_ 21-匹配紧接在CCD_ 22之后的位置

有关更多信息,请参阅本教程

模式"(?=<sup>)|(?<=</sup>)"将匹配给定字符串中开始标记之前和结束标记式splitAsStream()分割字符串时,它将生成一个流,其中包含用标签包围的"<sup>a</sup>"等元素,以及"One""Two""Three"等普通字符串。

请注意,为了在不重新编译的情况下重用模式,它可以在类级别上声明:

public static final Pattern pattern = Pattern.compile("(?=<sup>)|(?<=</sup>)");

最终的解决方案将产生贫而简单的物流:

public static void foo(String text) {
String result = pattern.splitAsStream(text)
.map(str -> getValue(str))      // or MyClass::getValue
.collect(Collectors.joining());
System.out.println(result);
}

与其在lambda中处理条件逻辑,不如将其提取到一个单独的方法中(当然,如果您愿意,可以使用三元运算符并将此逻辑直接放置在流中的map操作中,而不是使用此方法,但这会有点混乱(:

public static String getValue(String str) {
if (str.matches("<sup>\p{Alpha}+</sup>")) {
String key = str.replaceAll("<sup>|</sup>", "");
return "[" + dictByKey.getOrDefault(key, key) + "]";
}
return str;
}

main()

public static void main(String[] args) {
foo("One <sup>a</sup> Two <sup>b</sup> Three <sup>D</sup> Four");
}

输出:

Result: One [a-value] Two [b-value] Three [D] Four

联机演示链接

最新更新