Java 8相当于流的getLineNumber()

Java 8相当于流的getLineNumber(),java,java-8,java-stream,Java,Java 8,Java Stream,Java 8中的流是否有与getLineNumber()等价的值 我想在文本文件中搜索一个单词,并以整数形式返回行号。 这是我的搜索方法: result = Files.lines(Paths.get(fileName)) .filter(w -> w.contains(word)) .collect(Collectors.<String> toList()); result=Files.line(path.get(fileNa

Java 8中的流是否有与getLineNumber()等价的值

我想在文本文件中搜索一个单词,并以整数形式返回行号。 这是我的搜索方法:

result = Files.lines(Paths.get(fileName))
            .filter(w -> w.contains(word))
            .collect(Collectors.<String> toList());
result=Files.line(path.get(fileName))
.filter(w->w.contains(word))
.collect(Collectors.toList());

我认为没有,因为流的设计并不像集合那样提供对元素的访问

一种解决方法是读取列表中的文件,然后使用
IntStream
生成相应的索引,然后从中应用过滤器:

List<String> list =  Files.readAllLines(Paths.get("file"));

//readAllLines current implementation returns a RandomAccessList so 
//using get will not have a big performance impact.
//The pipeline can be safely run in parallel
List<Integer> lineNumbers = 
     IntStream.range(0, list.size())
              .filter(i -> list.get(i).contains(word))
              .mapToObj(i -> i + 1)
              .collect(toList());

我认为在这种情况下,最简单的方法是从流中获取迭代器,然后进行老式搜索:

    Iterator<String> iterator = Files.lines(Paths.get(fileName)).iterator();

    int lineNumber = 1;
    while (iterator.hasNext()) {
        if(iterator.next().contains(word)) {
            break;
        }
        lineNumber++;
    }
Iterator Iterator=Files.lines(path.get(fileName)).Iterator();
int lineNumber=1;
while(iterator.hasNext()){
if(iterator.next().contains(word)){
打破
}
lineNumber++;
}

使用此解决方案,您不会为了能够使用流操作而将整个文件读入内存。

如果您想保持流的高效惰性(即,如果您只想找到第一个匹配项,就不读取整个文件),您必须自己构造流。这并不难,唯一的障碍是没有一个元组类型来同时携带行号和行
字符串
。您可以滥用
Map.Entry
实例或创建专用类型:

static final class NumberedLine {
    final int number;
    final String line;
    NumberedLine(int number, String line) {
        this.number = number;
        this.line = line;
    }
    public int getNumber() {
        return number;
    }
    public String getLine() {
        return line;
    }
    @Override
    public String toString() {
        return number+":\t"+line;
    }
}
然后可以直接实现流:

public static Stream<NumberedLine> lines(Path p) throws IOException {
    BufferedReader b=Files.newBufferedReader(p);
    Spliterator<NumberedLine> sp=new Spliterators.AbstractSpliterator<NumberedLine>(
        Long.MAX_VALUE, Spliterator.ORDERED|Spliterator.NONNULL) {
            int line;
            public boolean tryAdvance(Consumer<? super NumberedLine> action) {
                String s;
                try { s=b.readLine(); }
                catch(IOException e){ throw new UncheckedIOException(e); }
                if(s==null) return false;
                action.accept(new NumberedLine(++line, s));
                return true;
            }
        };
    return StreamSupport.stream(sp, false).onClose(()->{
        try { b.close(); } catch(IOException e){ throw new UncheckedIOException(e); }});
}
或者把它们全部收集起来

List<Integer> all=lines(path).filter(nl->nl.getLine().contains(word))
                             .map(NumberedLine::getNumber)
                             .collect(Collectors.toList());
List all=line(path).filter(nl->nl.getLine().contains(word))
.map(NumberLine::getNumber)
.collect(Collectors.toList());
或者,在生产代码中,您希望确保适当关闭底层资源:

OptionalInt lNo;
try(Stream<NumberedLine> s=lines(path)) {
    lNo=s.filter(nl->nl.getLine().contains(word))
         .mapToInt(NumberedLine::getNumber)
         .findFirst();
}
可选lNo;
try(流s=行(路径)){
lNo=s.filter(nl->nl.getLine().contains(word))
.mapToInt(NumberedLine::getNumber)
.findFirst();
}
分别

全部列出;
try(流s=行(路径)){
all=s.filter(nl->nl.getLine().contains(word))
.map(NumberLine::getNumber)
.collect(Collectors.toList());
}

返回类型是拆分器实现的列表+1。zip方法被删除(我猜是由于
parallel()
特性)的缘故,这是一个相当令人震惊的消息。一方面,我喜欢这种方式,只要您知道潜在的副作用或失败,就可以“轻松”地并行化您的任务,另一方面,如果没有它,流API可能会更丰富,但我猜还有其他一些我还没有经验或不知道的地方做出了这个决定。。
OptionalInt lNo=lines(path).filter(nl->nl.getLine().contains(word))
                           .mapToInt(NumberedLine::getNumber)
                           .findFirst();
List<Integer> all=lines(path).filter(nl->nl.getLine().contains(word))
                             .map(NumberedLine::getNumber)
                             .collect(Collectors.toList());
OptionalInt lNo;
try(Stream<NumberedLine> s=lines(path)) {
    lNo=s.filter(nl->nl.getLine().contains(word))
         .mapToInt(NumberedLine::getNumber)
         .findFirst();
}
List<Integer> all;
try(Stream<NumberedLine> s = lines(path)) {
    all = s.filter(nl->nl.getLine().contains(word))
            .map(NumberedLine::getNumber)
            .collect(Collectors.toList());
}