【问题标题】:Extracting numbers from file using stream and lambda expressions使用流和 lambda 表达式从文件中提取数字
【发布时间】:2016-01-23 14:10:48
【问题描述】:

我正在尝试使用 Java 8 流和 lambda 表达式从我的 List<String> list 中仅提取数字。首先,我要做的是将字符串从文件加载到列表。之后我过滤流以获取包含“PL”的字符串。

我的文件:

Jan Kowalski PL 35000
Jiri Prohazka CZ 28000
Anna Malinowska PL 52000
Jozef Bak PL 49999
Helmut Schnittke DE 45000
Kleofas Oginski PL 45000
John Bull US 74000
Lukasz Zolw PL 9400
Franz Beckenbauer DE 83000
Frantisek Kupka CZ 32000

代码:

List<String> list = new ArrayList<>();
try (Stream<String> stream = Files.lines(Paths.get("file"),Charset.defaultCharset())) {
    list = stream
        .filter(line -> line.contains("PL"))
        .peek(System.out::println)
        .collect(Collectors.toList());
}

我认为现在最好的方法是从字符串中删除所有字母并只保留小数,但我有问题该怎么做。 最终结果应该让我将列表的元素解析为整数,对它们进行排序,并获得前三个元素的总和。 我已经做到了,但我确信有更好的方法来做到这一点(例如,只使用一个列表)

List<Integer> iList = new ArrayList<Integer>();
        list.forEach(s->
        {
            s = s.replaceAll("\\D+","");
            iList.add(Integer.parseInt(s));
        });
        Collections.sort(iList);
        Collections.reverse(iList); 
        int sum = 0;
        for(int i=0;i<3;i++){
            sum=sum+iList.get(i); 
        }

任何想法如何在不使用任何额外列表的情况下做到这一点?

【问题讨论】:

    标签: java string lambda java-8 java-stream


    【解决方案1】:

    您可以在单个 Stream 管道中执行此操作。要提取号码,您可以使用Pattern 并为该号码创建一个capturing group。在这种情况下,模式将是"(\\d+)"

    这是通过在Pattern.matcher(input) 的帮助下创建Matcher 来完成的,使用Matcher.find() 再次过滤实际包含数字的行并使用Matcher.group(group) 提取捕获的数字。在这种情况下,数字是第一个捕获的元素,因此它在第 1 组中。

    这个 Stream 被转换为 Stream&lt;Integer&gt;Stream.map(mapper):这里的映射器是返回从每一行解析的 Integer 值的函数。最后,为了对三个最大元素求和,Stream 以相反的顺序排序(sorted(comparator),其中比较器为reverseOrder()),仅限于前 3 个元素(limit(3)),然后将这些元素相加(sum() by首先将Stream&lt;Integer&gt; 转换为IntStreamStream.mapToInt)。

    public static void main(String[] args) throws IOException {
        Pattern pattern = Pattern.compile("(\\d+)");
        try (Stream<String> stream = Files.lines(Paths.get("file"))) {
            int sum = 
                stream.filter(line -> line.contains("PL"))
                      .map(pattern::matcher)
                      .filter(Matcher::find)
                      .map(m -> Integer.valueOf(m.group(1)))
                      .sorted(Comparator.reverseOrder())
                      .limit(3)
                      .mapToInt(Integer::intValue)
                      .sum();
            System.out.println(sum);
        }
    }
    

    对于您问题中的示例,输出为 146999。


    如果您确定在文件中,"PL" 标识符将在要提取的数字之前,您甚至可以删除第一个过滤操作并使用模式 ".*PL.*?(\\d+)":此模式将匹配包含 "PL" 的行并捕获对应的数字。

    【讨论】:

    • 谢谢!这正是我想做的。
    • 查看文件示例,似乎".*PL\\s+(\\d+)" 正则表达式会更健壮。在这种情况下,也可以将Matcher::find 替换为Matcher::matches
    • 这种方法有状态性。首先,创建一个匹配器,然后调用它的findgroup 方法。幸运的是,在 Java 9 中,会有一个 results 方法,所以 .filter(Matcher::find) 可以替换为 .flatMap(Matcher::results)。它甚至可以在一行中找到更多结果时起作用。见download.java.net/jdk9/docs/api/java/util/regex/…
    猜你喜欢
    • 1970-01-01
    • 2018-01-18
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多