如果您想保持Streams 的高效惰性特性(即,如果您只想找到第一个匹配项,则不读取整个文件),您必须自己构建流。这并不太难,唯一的障碍是没有一个元组类型可以同时携带行号和行String。您可以滥用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); }});
}
使用您可以搜索第一次出现的方法
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());
}