如何跳过从Files.lines获得的Stream <String>的偶数行

Mig*_*boa 10 java-8 java-stream

在这种情况下,只有奇数行具有有意义的数据,并且没有唯一标识这些行的字符.我的目的是获得与以下示例等效的内容:

Stream<DomainObject> res = Files.lines(src)
     .filter(line -> isOddLine())
     .map(line -> toDomainObject(line))
Run Code Online (Sandbox Code Playgroud)

没有共享全球状态,有没有"干净"的方式去做?

aio*_*obe 6

不,没有办法用API方便地做到这一点.(基本上与为什么没有简单方法的原因相同zipWithIndex,请参阅是否有简洁的方法在Java 8中使用索引迭代流?).

你仍然可以使用Stream,但是去寻找迭代器:

Iterator<String> iter = Files.lines(src).iterator();
while (iter.hasNext()) {
    iter.next();                  // discard
    toDomainObject(iter.next());  // use
}
Run Code Online (Sandbox Code Playgroud)

(您可能希望在该流上使用try-with-resource.)


Hol*_*ger 4

一种干净的方法是更深入一层并实现Spliterator. 在此级别上,您可以控制流元素的迭代,并在下游请求一项时简单地迭代两项:

\n\n
public class OddLines<T> extends Spliterators.AbstractSpliterator<T>\n    implements Consumer<T> {\n\n    public static <T> Stream<T> oddLines(Stream<T> source) {\n        return StreamSupport.stream(new OddLines(source.spliterator()), false);\n    }\n    private static long odd(long l) { return l==Long.MAX_VALUE? l: (l+1)/2; }\n    Spliterator<T> originalLines;\n\n    OddLines(Spliterator<T> source) {\n        super(odd(source.estimateSize()), source.characteristics());\n        originalLines=source;\n    }\n\n    @Override\n    public boolean tryAdvance(Consumer<? super T> action) {\n        if(originalLines==null || !originalLines.tryAdvance(action))\n            return false;\n        if(!originalLines.tryAdvance(this)) originalLines=null;\n        return true;\n    }\n\n    @Override\n    public void accept(T t) {}\n}\n
Run Code Online (Sandbox Code Playgroud)\n\n

然后你可以像这样使用它

\n\n
Stream<DomainObject> res = OddLines.oddLines(Files.lines(src))\n    .map(line -> toDomainObject(line));\n
Run Code Online (Sandbox Code Playgroud)\n\n

该解决方案没有副作用,并且保留了StreamAPI 的大部分优点,例如惰性求值。然而,应该清楚的是,它对于无序流处理没有有用的语义(注意微妙的方面,比如使用forEachOrdered而不是forEach在所有元素上执行终端操作时),并且在原则上支持并行处理的同时,它\xe2\x80\x99s 不太可能非常有效\xe2\x80\xa6

\n