Come osservato Tagir Valeev, questo tipo di il problema non è ben supportato dall'API dei flussi. Se si desidera leggere in modo incrementale le righe dall'input e stampare le linee corrispondenti con il contesto, è necessario introdurre uno stage stateful pipeline (o un collector o uno splitterator personalizzato) che aggiunge un po 'di complessità.
Se si desidera leggere tutte le righe in memoria, risulta che BitSet
è una rappresentazione utile per la manipolazione di gruppi di corrispondenze. Questo ha qualche somiglianza con la soluzione di Tagir, ma invece di usare intervalli di numeri interi per rappresentare linee da stampare, usa 1 bit in uno BitSet
. Alcuni vantaggi di BitSet
sono che ha un numero di operazioni di massa incorporate e ha una rappresentazione interna compatta. Può anche produrre un flusso di indici di 1-bit, che è abbastanza utile per questo problema.
In primo luogo, cominciamo con la creazione di un BitSet
che ha un 1-bit per ogni riga che corrisponde al predicato:
void contextMatch(Predicate<String> pred, int before, int after, List<String> input) {
int len = input.size();
BitSet matches = IntStream.range(0, len)
.filter(i -> pred.test(input.get(i)))
.collect(BitSet::new, BitSet::set, BitSet::or);
Ora che abbiamo impostato il bit di linee di corrispondenza, Trasmettiamo gli indici di ogni 1 bit. Quindi impostiamo i bit nel bitset che rappresenta il contesto precedente e successivo. Questo ci dà un singolo BitSet
i cui 1 bit rappresentano tutte le linee da stampare, comprese le linee di contesto.
BitSet context = matches.stream()
.collect(BitSet::new,
(bs,i) -> bs.set(Math.max(0, i - before), Math.min(i + after + 1, len)),
BitSet::or);
Se vogliamo solo per stampare tutte le linee, tra cui contesto, siamo in grado di fare questo:
context.stream()
.forEachOrdered(i -> System.out.println(input.get(i)));
L'attuale grep -A a -B b
comando stampa un separatore tra ogni gruppo di righe di contesto. Per capire quando stampare un separatore, guardiamo ogni 1 bit nel bit di contesto impostato. Se c'è un 0 bit che lo precede, o se è all'inizio, abbiamo impostato un po 'nel risultato. Questo ci dà un 1-bit all'inizio di ogni gruppo di righe di contesto:
Non vogliamo stampare il separatore prima di ogni gruppo di righe di contesto; vogliamo stamparlo tra ogni gruppo.Ciò significa che dobbiamo cancellare il primo 1 bit (se presente):
// clear the first bit
int first = separators.nextSetBit(0);
if (first >= 0) {
separators.clear(first);
}
Ora, possiamo stampare le righe dei risultati. Ma prima di stampare ogni linea, controlliamo per vedere se dobbiamo stampare un separatore prima:
context.stream()
.forEachOrdered(i -> {
if (separators.get(i)) {
System.out.println("--");
}
System.out.println(input.get(i));
});
}
che non è, purtroppo, supportati dalle API flusso fuori dalla scatola, ma ciò che si desidera è chiamato un "finestra scorrevole". –