如何使用java.util.stream处理文件的块

时间:2014-10-20 10:47:31

标签: java parallel-processing java-8 java-stream

为了熟悉流api,我尝试编写一个非常简单的模式。

问题:拥有一个不包含嵌套文本块的文本文件。所有块都由start / endpatterns标识(例如<start><stop>。块的内容在语法上与块之间的噪声无法区分。因此,使用简单(无状态)是不可能的)lambdas。

我只能实现丑陋的东西:
Files.lines(path).collect(new MySequentialParseAndProsessEachLineCollector<>());
说实话,这不是我想要的。

我正在寻找一个类似的地图:
Files.lines(path).map(MyMapAllLinesOfBlockToBuckets()).parallelStream().collect(new MyProcessOneBucketCollector<>());

is there a good way to extract chunks of data from a java 8 stream似乎包含解决方案的骨架。不幸的是,我想把它翻译成我的问题。 ; - )

任何提示?

1 个答案:

答案 0 :(得分:2)

这是一个解决方案,可用于将Stream<String>(表示一行的每个元素)转换为Stream<List<String>>,每个元素表示使用指定分隔符找到的块:

public class ChunkSpliterator implements Spliterator<List<String>> {
    private final Spliterator<String> source;
    private final Predicate<String> start, end;
    private final Consumer<String> getChunk;
    private List<String> current;

    ChunkSpliterator(Spliterator<String> lineSpliterator,
        Predicate<String> chunkStart, Predicate<String> chunkEnd) {
        source=lineSpliterator;
        start=chunkStart;
        end=chunkEnd;
        getChunk=s -> {
            if(current!=null) current.add(s);
            else if(start.test(s)) current=new ArrayList<>();
        };
    }
    public boolean tryAdvance(Consumer<? super List<String>> action) {
        while(current==null || current.isEmpty()
                            || !end.test(current.get(current.size()-1)))
            if(!source.tryAdvance(getChunk)) return false;
        current.remove(current.size()-1);
        action.accept(current);
        current=null;
        return true;
    }
    public Spliterator<List<String>> trySplit() {
        return null;
    }
    public long estimateSize() {
        return Long.MAX_VALUE;
    }
    public int characteristics() {
        return ORDERED|NONNULL;
    }

    public static Stream<List<String>> toChunks(Stream<String> lines,
        Predicate<String> chunkStart, Predicate<String> chunkEnd,
        boolean parallel) {

        return StreamSupport.stream(
            new ChunkSpliterator(lines.spliterator(), chunkStart, chunkEnd),
            parallel);
    }
}

与谓词匹配的行不包含在块中;如果需要,可以很容易地改变这种行为。

可以像这样使用:

ChunkSpliterator.toChunks( Files.lines(Paths.get(myFile)),
    Pattern.compile("^<start>$").asPredicate(),
    Pattern.compile("^<stop>$").asPredicate(),
    true )
   .collect(new MyProcessOneBucketCollector<>())

模式指定为^word$,要求整行仅由单词组成;没有这些锚点,包含模式的行可以开始和结束一个块。源流的性质在创建块时不允许并行,因此当使用立即收集操作进行链接时,整个操作的并行性相当有限。如果可以存在任何并行性,则取决于MyProcessOneBucketCollector

如果您的最终结果不依赖于源文件中存储桶的出现顺序,则强烈建议收集器报告自己为UNORDERED或插入unordered()流的方法链在collect之前。