2015-05-27 13 views
21

mi chiedo c'è un'alternativa alGet ultimi n elementi dal flusso

List<X> lastN = all.subList(Math.max(0, all.size() - n), all.size()); 

con stream utilizzo?

+1

Non credo che questo è generalmente possibile con i flussi, come le dimensioni di un corso d'acqua non possono essere noto a priori, o potrebbe anche essere infinito. E se si crea lo stream da una lista, basta usare la sottolista, come hai fatto tu. –

+1

@tobias_k l'OP sembra avere comunque una lista finita ... – Puce

+1

Se hai già una lista, allora 'subList' è la strada da percorrere. Puoi quindi copiarlo, riprodurlo in streaming e qualsiasi altra cosa desideri. –

risposta

19

È possibile scrivere un collettore personalizzato come questo:

public static <T> Collector<T, ?, List<T>> lastN(int n) { 
    return Collector.<T, Deque<T>, List<T>>of(ArrayDeque::new, (acc, t) -> { 
     if(acc.size() == n) 
      acc.pollFirst(); 
     acc.add(t); 
    }, (acc1, acc2) -> { 
     while(acc2.size() < n && !acc1.isEmpty()) { 
      acc2.addFirst(acc1.pollLast()); 
     } 
     return acc2; 
    }, ArrayList<T>::new); 
} 

e usarlo in questo modo:

List<String> lastTen = input.stream().collect(lastN(10)); 
+2

Non è necessario scrivere 'ArrayList :: new', solo' ArrayList :: new' è sufficiente poiché i riferimenti al metodo utilizzano sempre l'inferenza di tipo anziché i tipi non elaborati (come se l'operatore "diamond" fosse sempre presente quando si utilizza ' :: new'). Lo usi già con 'ArrayDeque :: new'. Btw. Preferirei 'removeFirst' /' removeLast' su 'pollFirst' /' pollLast' qui ... – Holger

+2

@Holger, prima ho scritto 'ArrayList :: new', ma Eclipse ha mostrato un avvertimento sul costruttore non controllato. Bene, probabilmente questo è un problema specifico della CGE. –

+0

È interessante notare che 'ArrayDeque :: new' trarrebbe vantaggio da un testimone di tipo, poiché l'uso di' ArrayDeque :: new' renderebbe il tipo testimone alla chiamata 'Collector.of' obsoleto (e' 'è più semplice di' , Lista > ') mentre il tipo testimone in' ArrayList :: new' non è necessario in quanto il suo tipo può essere dedotto dal tipo di destinazione. – Holger

12

Uso Stream.skip()

restituisce un flusso costituito dai restanti elementi di questo flusso dopo scartando i primi n elementi del flusso. Se questo flusso contiene meno di n elementi, verrà restituito uno stream vuoto.

all.stream().skip(Math.max(0, all.size() - n)).forEach(doSomething); 
+0

Questo non restituisce gli ultimi n elementi. Salta il primo n. non è quello che l'OP ha chiesto. – Bohemian

+0

@Bohemian 'skip (all.size() - n)' ti dà gli ultimi n elementi ... – Puce

+0

@puce yeah OK. Lo modificherò in. – Bohemian

3

Nel caso in cui il torrente ha una dimensione sconosciuta, probabilmente non c'è modo per aggirare consumando l'intero flusso e il buffering ultimi n elementi incontrati finora. Puoi farlo usando un qualche tipo di deque, o un buffer ad anello specializzato che mantiene automaticamente la sua dimensione massima (vedi this related question per alcune implementazioni).

public static <T> List<T> lastN(Stream<T> stream, int n) { 
    Deque<T> result = new ArrayDeque<>(n); 
    stream.forEachOrdered(x -> { 
     if (result.size() == n) { 
      result.pop(); 
     } 
     result.add(x); 
    }); 
    return new ArrayList<>(result); 
} 

Tutte queste operazioni (size, pop, add) dovrebbe avere complessità O (1), quindi complessità generale per un flusso con (sconosciuta) lunghezza n sarebbe O (n).

Problemi correlati