Codingbat 挑战:sumNumbers Stream API 解决方案

问题描述 投票:0回答:4

给定来自 CodingBat 的任务 sumNumbers

给定一个string,返回字符串中出现的数字的sum, 忽略所有其他字符。数字是一系列

1
或更多数字 连续的字符。

(注意:

Character.isDigit(char)
测试
a
字符是否为1 字符
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解决这个问题?

java regex string java-stream
4个回答
2
投票

你们这些疯子。对我来说,你可以将任何函数转换为流。 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);

1
投票

为了使流实现更接近原始解决方案,您仍然可以使用

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


1
投票

使用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" };

1
投票

\\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(); }
    

© www.soinside.com 2019 - 2024. All rights reserved.