提问人:Evgeniy 提问时间:6/4/2022 最后编辑:VLAZEvgeniy 更新时间:8/27/2023 访问量:209
Codingbat 挑战赛:sumNumbers Stream API 解决方案
Codingbat challenge: sumNumbers Stream API Solution
问:
给定 CodingBat 的任务 sumNumbers:
给定一个字符串,返回字符串中出现的数字的总和, 忽略所有其他字符。数字是一系列或多个数字 字符。
1
(注意:测试 char 是否为 1 的字符 , , ..., . 将字符串转换为 .)
Character.isDigit(char)
a
0
1
9
Integer.parseInt(string)
a
int
sumNumbers("abc123xyz") → 123
sumNumbers("aa11b33") → 44
sumNumbers("7 11") → 18
我对这个问题的解决方案如下:
public int sumNumbers(String str) {
int sum = 0;
java.util.regex.Matcher matcher = java.util.regex.Pattern.compile("[0-9]+").matcher(str);
while (matcher.find()) {
sum += Integer.parseInt(matcher.group());
}
return sum;
}
是否可以使用 Stream API 解决这个问题?
答:
是否可以使用 Stream API 解决这个问题?
使用不由数字组成的子字符串拆分给定的字符串。正则表达式匹配由一个或多个非数字字符组成的字符串。结果将是一个数字字符串数组。.split("\\D+")
"\\D+"
在数组上创建一个流,并筛选出不为空的字符串。然后将字符串解析为 with 并作为终端操作应用。int
mapToInt()
sum()
该解决方案通过了 CodingBat 上的所有测试:
public int sumNumbers(String str) {
return Arrays.stream(str.split("\\D+"))
.filter(s -> !s.isEmpty())
.mapToInt(Integer::parseInt)
.sum();
}
由于在数组的开头只能有一个空字符串,因此为了减少在流流管道中执行的操作的数量,可以用 替换为 。.split("\\D+")
filter()
dropWhile()
如果第一个字符串为空,它将跳过该字符串,并且在遇到第一个非空元素后,将不会应用此检查。即传递给 的谓词在大多数时候都会被执行。如果字符串很长(否则所有优化都无关紧要),它比使用 .dropWhile()
dropWhile()
2
replaceAll()
public static int sumNumbers(String str) {
return Arrays.stream(str.split("\\D+"))
.dropWhile(String::isEmpty)
.mapToInt(Integer::parseInt)
.sum();
}
注意:Java 9 及更高版本可用。CodingBat 仍然在 Java 8 上,因此它不知道这个特性。尽管如此,它仍然是一个有效且性能良好的解决方案。
您可以在 IDE 中对其进行测试,并使用在线演示dropWhile()
测试(第一种解决方案):
是的。
String[] data = { "abc123xyz", "aa11b33", "7 11" };
\\D
- 拆分除非数字字符串以外的任何数字- 从拆分中筛选空字符串
- 转换为 an 并将值求和。
int
- 返回一个条目以显示字符串和总和(不需要,但显示关联)
Arrays.stream(data).map(
str -> new AbstractMap.SimpleEntry<String, Integer>(
str,
Arrays.stream(str.split("\\D+"))
.filter(s -> !s.isBlank())
.mapToInt(Integer::parseInt).sum()))
.forEach(e -> System.out.printf("%-10s -> %d%n",
e.getKey(), e.getValue()));
指纹
abc123xyz -> 123
aa11b33 -> 44
7 11 -> 18
如果您只想要总和,您可以执行以下操作:
public static int getSum(String str) {
return Arrays.stream(str.split("\\D+"))
.filter(s -> !s.isBlank())
.mapToInt(Integer::parseInt)
.sum();
}
评论
str.replaceAll("^\\D+", "").split("\\D+")
replaceAll
replaceFirst
filter()
若要使流实现更接近原始解决方案,您仍然可以使用 和 ,然后流式传输 的结果。Pattern
Matcher
Matcher
public int sumNumbers(String s) {
return Pattern.compile("\\d+").matcher(s).results()
.collect(Collectors.summingInt(m -> Integer.valueOf(m.group())));
}
输出
123
44
18
下面是一个链接,用于测试具有预期输出的代码:
https://www.jdoodle.com/iembed/v0/rRS
你们这些疯子。对我来说,您可以将任何函数转换为流。Map/Reduce是一种常见的模式:
int s = Stream.of("abc123xyz").mapToInt(str->{
int sum = 0;
java.util.regex.Matcher matcher = java.util.regex.Pattern.compile("[0-9]+").matcher(str);
while (matcher.find()) {
sum += Integer.parseInt(matcher.group());
}
return sum;
}).sum();
System.out.println(s);
评论
sum()
IntStream
Collectors.summingInt()