Java 8 等同于 Streams 的 getLineNumber()
Java 8 equivalent to getLineNumber() for Streams
在 Java 8 中是否有与 getLineNumber() 等效的流?
我想在文本文件中搜索一个词,return 行号为整数。
这是我的搜索方法:
result = Files.lines(Paths.get(fileName))
.filter(w -> w.contains(word))
.collect(Collectors.<String> 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());
这有点矫枉过正,因为您冒着将整个文件的内容加载到列表中的风险,之后可能只保留几个元素。如果还不满意,可以自己写个for循环就好了,代码不多
也许你会对这个问题感兴趣Zipping streams using JDK8 with lambda (java.util.stream.Streams.zip). For example, using the proton-pack图书馆:
List<Long> lineNumbers =
StreamUtils.zipWithIndex(Files.lines(Paths.get("file")))
.filter(in -> in.getValue().contains(word))
.map(in -> in.getIndex() + 1)
.collect(toList());
或者您可以从 BufferedReader
创建 LineNumberReader
,然后调用 lines()
并将每一行映射到文件中的行号。请注意,如果管道是 运行 并行,此方法将 失败 ,因此我不推荐它。
LineNumberReader numberRdr = new LineNumberReader(Files.newBufferedReader(Paths.get("file")));
List<Integer> linesNumbers = numberRdr.lines()
.filter(w -> w.contains(word))
.map(w -> numberRdr.getLineNumber())
.collect(toList());
我认为在这种情况下,您可以做的最简单的事情就是从流中获取迭代器,然后进行老派搜索:
Iterator<String> iterator = Files.lines(Paths.get(fileName)).iterator();
int lineNumber = 1;
while (iterator.hasNext()) {
if(iterator.next().contains(word)) {
break;
}
lineNumber++;
}
使用此解决方案,您不会为了能够使用流操作而将整个文件读入内存。
如果你想保持 Stream
s 的高效惰性(即如果你只想找到第一个匹配项就不要读取整个文件),你必须自己构建流。这并不难,唯一的障碍是缺少一个元组类型来同时携带行号和行 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());
}
在 Java 8 中是否有与 getLineNumber() 等效的流?
我想在文本文件中搜索一个词,return 行号为整数。 这是我的搜索方法:
result = Files.lines(Paths.get(fileName))
.filter(w -> w.contains(word))
.collect(Collectors.<String> 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());
这有点矫枉过正,因为您冒着将整个文件的内容加载到列表中的风险,之后可能只保留几个元素。如果还不满意,可以自己写个for循环就好了,代码不多
也许你会对这个问题感兴趣Zipping streams using JDK8 with lambda (java.util.stream.Streams.zip). For example, using the proton-pack图书馆:
List<Long> lineNumbers =
StreamUtils.zipWithIndex(Files.lines(Paths.get("file")))
.filter(in -> in.getValue().contains(word))
.map(in -> in.getIndex() + 1)
.collect(toList());
或者您可以从 BufferedReader
创建 LineNumberReader
,然后调用 lines()
并将每一行映射到文件中的行号。请注意,如果管道是 运行 并行,此方法将 失败 ,因此我不推荐它。
LineNumberReader numberRdr = new LineNumberReader(Files.newBufferedReader(Paths.get("file")));
List<Integer> linesNumbers = numberRdr.lines()
.filter(w -> w.contains(word))
.map(w -> numberRdr.getLineNumber())
.collect(toList());
我认为在这种情况下,您可以做的最简单的事情就是从流中获取迭代器,然后进行老派搜索:
Iterator<String> iterator = Files.lines(Paths.get(fileName)).iterator();
int lineNumber = 1;
while (iterator.hasNext()) {
if(iterator.next().contains(word)) {
break;
}
lineNumber++;
}
使用此解决方案,您不会为了能够使用流操作而将整个文件读入内存。
如果你想保持 Stream
s 的高效惰性(即如果你只想找到第一个匹配项就不要读取整个文件),你必须自己构建流。这并不难,唯一的障碍是缺少一个元组类型来同时携带行号和行 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());
}