我想要通过对其行进行分组来处理一些大型文本文件。
我尝试使用新的流媒体功能,例如
return FileUtils.readLines(...)
.parallelStream()
.map(...)
.collect(groupingBy(pair -> pair[0]));
问题是,AFAIK,这会生成一个Map。
有没有办法像上面那样生成高级代码,例如,条目流?
更新:我正在寻找的是像python的itertools.groupby。我的文件已经排序(通过pair [0]),我只想逐个加载组。
我已经有了一个迭代解决方案。我只是想知道是否有更多的声明方式来做到这一点。顺便说一句,使用番石榴或其他第三方图书馆不会是一个大问题。
答案 0 :(得分:3)
您想要实现的任务与分组完成的任务完全不同。 groupingBy
不依赖于Stream
元素的顺序,而是依赖于应用于分类器Map
的结果的Function
算法。
您想要的是将具有共同属性值的相邻项目折叠到一个List
项目中。只要您可以保证具有相同属性值的所有项都被聚集在一起,甚至不必将Stream
按该属性排序。
也许有可能将此任务表述为减少,但对我来说,结果结构看起来太复杂了。
因此,除非直接支持此功能添加到Stream
,否则基于迭代器的方法对我来说看起来最实用:
class Folding<T,G> implements Spliterator<Map.Entry<G,List<T>>> {
static <T,G> Stream<Map.Entry<G,List<T>>> foldBy(
Stream<? extends T> s, Function<? super T, ? extends G> f) {
return StreamSupport.stream(new Folding<>(s.spliterator(), f), false);
}
private final Spliterator<? extends T> source;
private final Function<? super T, ? extends G> pf;
private final Consumer<T> c=this::addItem;
private List<T> pending, result;
private G pendingGroup, resultGroup;
Folding(Spliterator<? extends T> s, Function<? super T, ? extends G> f) {
source=s;
pf=f;
}
private void addItem(T item) {
G group=pf.apply(item);
if(pending==null) pending=new ArrayList<>();
else if(!pending.isEmpty()) {
if(!Objects.equals(group, pendingGroup)) {
if(pending.size()==1)
result=Collections.singletonList(pending.remove(0));
else {
result=pending;
pending=new ArrayList<>();
}
resultGroup=pendingGroup;
}
}
pendingGroup=group;
pending.add(item);
}
public boolean tryAdvance(Consumer<? super Map.Entry<G, List<T>>> action) {
while(source.tryAdvance(c)) {
if(result!=null) {
action.accept(entry(resultGroup, result));
result=null;
return true;
}
}
if(pending!=null) {
action.accept(entry(pendingGroup, pending));
pending=null;
return true;
}
return false;
}
private Map.Entry<G,List<T>> entry(G g, List<T> l) {
return new AbstractMap.SimpleImmutableEntry<>(g, l);
}
public int characteristics() { return 0; }
public long estimateSize() { return Long.MAX_VALUE; }
public Spliterator<Map.Entry<G, List<T>>> trySplit() { return null; }
}
通过将折叠Stream
应用于无限流,可以最好地证明所得到的折叠Folding.foldBy(Stream.iterate(0, i->i+1), i->i>>4)
.filter(e -> e.getKey()>5)
.findFirst().ifPresent(e -> System.out.println(e.getValue()));
的惰性:
{{1}}
答案 1 :(得分:1)
cyclops-react,我为图书馆做出了贡献,提供了sharding和可能做你想做的事情的分组功能。
ReactiveSeq<ListX<TYPE>> grouped = ReactiveSeq.fromCollection(FileUtils.readLines(...) )
.groupedStatefullyWhile((batch,next) -> batch.size()==0 ? true : next.equals(batch.get(0)));
groupedStatefullyWhile运算符允许根据批处理的当前状态对元素进行分组。 ReactiveSeq是单线程顺序流。
Map<Key, Stream<Value> sharded =
new LazyReact()
.fromCollection(FileUtils.readLines(...) )
.map(..)
.shard(shards, pair -> pair[0]);
这将创建一个LazyFutureStream(实现java.util.stream.Stream),它将异步并行地处理文件中的数据。它是懒惰的,在数据通过之前不会开始处理。
唯一需要注意的是,您需要事先定义分片。即上面的'shards'参数是一个async.Queue的Map,它是由分片的键控制的(可能是对[0]是什么?)。
e.g。
Map<Integer,Queue<String>> shards;
答案 2 :(得分:0)
可以collapse
使用StreamEx
final int[][] aa = { { 1, 1 }, { 1, 2 }, { 2, 2 }, { 2, 3 }, { 3, 3 }, { 4, 4 } };
StreamEx.of(aa)
.collapse((a, b) -> a[0] == b[0], Collectors.groupingBy(a -> a[0]))
.forEach(System.out::println);
我们可以添加peek
和limit
来验证它是否是惰性计算:
StreamEx.of(aa)
.peek(System.out::println)
.collapse((a, b) -> a[0] == b[0], Collectors.groupingBy(a -> a[0]))
.limit(1)
.forEach(System.out::println);