给定来自 CodingBat 的任务 sumNumbers:
给定一个string,返回字符串中出现的数字的sum, 忽略所有其他字符。数字是一系列
或更多数字 连续的字符。1
(注意:
测试Character.isDigit(char)
字符是否为1 字符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解决这个问题?
你们这些疯子。对我来说,你可以将任何函数转换为流。 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);
为了使流实现更接近原始解决方案,您仍然可以使用
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
这里是使用预期输出测试代码的链接:
使用Stream API可以解决这个问题吗?
使用
.split("\\D+")
将给定字符串拆分为不由数字组成的子字符串。正则表达式 "\\D+"
匹配由一个或多个非数字字符组成的字符串。结果将是一个数字字符串数组。
在数组上创建一个流并过滤掉不为空的字符串。然后用
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()
。
如果第一个字符串为“空”,它将跳过第一个字符串,并且在遇到第一个“非空”元素后,将不会应用此检查。 IE。传递给 dropWhile()
的谓词 dropWhile()
最多会被执行
2
次。如果字符串很长(否则所有优化都无关紧要),那么它会比使用
replaceAll()
生成新字符串更便宜。
public static int sumNumbers(String str) {
return Arrays.stream(str.split("\\D+"))
.dropWhile(String::isEmpty)
.mapToInt(Integer::parseInt)
.sum();
}
警告: dropWhile()
适用于 Java 9 及以上版本。 CodingBat 仍在 Java 8 上,因此它不知道该功能。尽管如此,它是一个有效且高性能的解决方案。 您可以在 IDE 中测试它并使用
在线演示测试(第一个解决方案):
String[] data = { "abc123xyz", "aa11b33", "7 11" };
\\D
- 分割除非数字字符串之外的任意字符串
转换为
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();
}