[英]Is it possible to do a lazy groupby, returning a stream, in java 8?
我有一些較大的文本文件,我希望通過對其行進行分組來進行處理。
我嘗試使用新的流媒體功能,例如
return FileUtils.readLines(...)
.parallelStream()
.map(...)
.collect(groupingBy(pair -> pair[0]));
問題是AFAIK會生成一個Map。
有沒有辦法像上面的代碼那樣生成高級代碼,例如生成條目流?
更新 :我正在尋找的是類似python的itertools.groupby的東西。 我的文件已經排序(按pair [0]),我只想一個接一個地加載組。
我已經有一個迭代的解決方案。 我只是想知道是否還有一種更具聲明性的方式來做到這一點。 順便說一句,使用番石榴或其他第三方庫不是什么大問題。
您要完成的任務與分組的工作完全不同。 groupingBy
不依賴於Stream
元素的順序,而是依賴於應用於分類器Function
的結果的Map
的算法。
您想要的是將具有公共屬性值的相鄰項目折疊到一個“ 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()));
我收藏了一份關於cyclops-react的文章 ,它提供了分片和分組功能,可以滿足您的需求。
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的映射,該映射由分片的鍵(可能是pair [0]是?)來鍵控。
例如
Map<Integer,Queue<String>> shards;
可以通過StreamEx collapse
來完成
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);
聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.