Java:使用 lambda 向文件添加行号的优雅方式

标签 java lambda java-8

很长一段时间以来,我习惯于使用 lambda 逐行解析文件(比 bufferedReader.readLine() 简洁得多)。但是今天我遇到了一个问题:为每一行添加一个行号。

它需要一个计数器,但 lambda 中的变量应该是有效的最终变量。最后,我用一个 int 数组破解了它。

代码:

public static void main(String[] args) {
    int[] counter = new int[1];
    counter[0] = 0;
    try (Stream<String> lines = Files.lines(Paths.get("/tmp/timeline.txt"), Charset.defaultCharset())) {
        lines.limit(10).forEachOrdered(line -> {
            line = line.trim();
            counter[0] ++;
            System.out.println("Line " + counter[0] + ": " + line);
        });
    } catch (IOException e) {
        e.printStackTrace();
    }
}

输出:

Line 1: p 5714026 wEkQ
Line 2: v 8235889
Line 3: v 6534726
...

我的问题是,如何避免我的 hack 并优雅地解决该问题?

最佳答案

对于非功能性任务,没有优雅的功能性解决方案。您可能首先考虑的是诉诸普通的匿名内部类:

String path = "/tmp/timeline.txt";
try(Stream<String> lines = Files.lines(Paths.get(path), Charset.defaultCharset())) {
    lines.limit(10).forEachOrdered(new Consumer<String>() {
        int counter = 0;
        public void accept(String line) {
            System.out.println("Line " + counter++ + ": " + line.trim());
        }
    });
} catch (IOException e) {
    e.printStackTrace();
}

优点是它不会假装在没有功能的地方发挥作用,并且 counter 变量的范围具有此任务所需的最小范围。


如果您要做的不仅仅是打印这些编号的行,并且需要一个与所有流操作兼容的解决方案,那么重新实现流源是一个直接的解决方案:

static Stream<String> numberedLines(Path path, Charset cs) throws IOException {
    BufferedReader br = Files.newBufferedReader(path, cs);
    return StreamSupport.stream(new Spliterators.AbstractSpliterator<String>(
            Long.MAX_VALUE, Spliterator.ORDERED|Spliterator.NONNULL) {
        int counter;
        public boolean tryAdvance(Consumer<? super String> action) {
            String line;
            try {
                line = br.readLine();
                if(line==null) return false;
                action.accept("Line " + counter++ + ": " + line.trim());
                return true;
            } catch (IOException ex) {
                throw new UncheckedIOException(ex);
            }
        }
    }, true).onClose(()->{ try { br.close(); }
        catch (IOException ex) { throw new UncheckedIOException(ex); }
    });
}

当然,这不像单个 lambda 表达式那么简单,但使用这种可重用的方法,您可以毫无问题地使用所有流操作,例如

String path = "/tmp/timeline.txt";
try(Stream<String> lines = numberedLines(Paths.get(path), Charset.defaultCharset())) {
    lines.skip(10).limit(10).forEachOrdered(System.out::println);
} catch(IOException e) {
    e.printStackTrace();
}

关于Java:使用 lambda 向文件添加行号的优雅方式,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36478579/

相关文章:

c# lambda表达式+思考题

node.js - Kibana - 无法在 Windows 10 上启动 kibana

java - 如何处理流的流?

java - 使用 removeif 过滤包含对象的列表

java - 使用截断的时区信息解析 Java 日期

不支持 java.util.Date

java - 如果在服务器发现期间客户端端口已被占用,如何更改服务器中的UDP广播端口?

function - C++11 : easiest way to create a function always returning true to use for a function argument

java - JTable 未显示

java - 考虑 for-each 循环中 lambda 表达式中的两种情况