L'attività che si desidera raggiungere è molto diversa da ciò che fa il raggruppamento. groupingBy
non si basa sull'ordine degli elementi dello Stream
ma sull'algoritmo dello Map
applicato al risultato del classificatore Function
.
Ciò che si desidera è piegare articoli adiacenti aventi un valore di proprietà comune in un articolo List
. Non è nemmeno necessario che lo Stream
sia ordinato in base a tale proprietà, purché sia possibile garantire che tutti gli articoli con lo stesso valore di proprietà siano raggruppati.
Forse è possibile formulare questa attività come una riduzione, ma per me la struttura risultante sembra troppo complicata.
Quindi, a meno che il supporto diretto per questa funzione viene aggiunto al Stream
s, un approccio basato iteratore sembra più pragmatico per me:
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; }
}
La natura pigra del risultante piegato Stream
può essere meglio dimostrata applicando ad un flusso infinito:
Folding.foldBy(Stream.iterate(0, i->i+1), i->i>>4)
.filter(e -> e.getKey()>5)
.findFirst().ifPresent(e -> System.out.println(e.getValue()));
Come può fai un pigro raggruppa per? Per raggruppare alcune proprietà dell'oggetto contenuto nel flusso, è necessario eseguire un'iterazione su tutti gli elementi nel flusso. – Eran
Cosa intendi per "raggruppare le sue linee?" intendi il binning come il metodo Stream 'groupBy' o intendi la lettura di più righe alla volta alla rinfusa? – dkatzel
Grazie per i commenti, aggiunto un UPDATE alla domanda. –