2016-04-28 6 views
5

JavaDoc di Spliterator menziona che:C'è qualche pericolo nel rendere l'azione. Accept() più di un elemento in un'implementazione di .tryAdance() di Spliterator?

Uno Spliterator può attraversare elementi singolarmente (tryAdvance()) o sequenzialmente in massa (forEachRemaining()).

Poi andiamo al javadoc of tryAdvance() che dice che:

Se un elemento residuo esiste, esegue l'azione data su di esso, restituendo vero; altrimenti restituisce false.

Forse sono travisamento da qualche parte, ma a me sembra che a condizione che vi sia un elemento, o più, rimanendo, il Consumer come argomento dovrebbe solo ogni .accept() un argomento prima di tornare true, e che se, per esempio, ho due argomenti immediatamente disponibile, allora non posso:

action.accept(arg1); 
action.accept(arg2); 
return true; 

In this project, ho riscritto l'ampiezza prima spliterator in modo che ora recita:

// deque is a Deque<Iterator<T>> 

@Override 
public boolean tryAdvance(final Consumer<? super T> action) 
{ 
    Iterator<T> iterator; 
    T element; 

    while (!deque.isEmpty()) { 
     iterator = deque.removeFirst(); 
     while (iterator.hasNext()) { 
      element = iterator.next(); 
      deque.add(fn.apply(element)); 
      action.accept(element); 
     } 
    } 
    return false; 
} 

In breve, faccio in modo che lo action accetti tutti gli argomenti e restituisca falso ... e il test, anche se piuttosto semplice, ha ancora esito positivo (link).

Nota che .trySplit() restituisce sempre null; e lo spliterator ha le caratteristiche DISTINCT, ORDERED e NONNULL.

Quindi, c'è un flusso di utilizzo in cui il codice sopra non funzionerà a causa del metodo sopra consumando tutti gli elementi contemporaneamente?

+1

è veramente importante se l'attuale implementazione di un particolare pause API, quando si viola il contratto generale un 'interfaccia'? Non stai solo invocando l'azione più di una volta, stai anche restituendo 'false' nonostante ci fossero degli elementi. – Holger

+0

@Holger il problema è che non è chiaro per me cosa dovrebbe accadere se rimane più di un elemento – fge

+1

Mirare a come interpretare il contratto o cosa dovresti fare, praticamente? – Holger

risposta

7

L'ipotesi che tryAdvance() debba consumare solo un elemento è corretta. Questo, tuttavia, non implica che noterai immediatamente violazioni del contratto. Quando si prova utilizzando le operazioni come .collect(Collectors.toList()) è ancora più improbabile individuare un tale violazione come la maggior parte delle operazioni che consumano tutti gli elementi invocheranno forEachRemaining() sul spliterator cui default implementation is documented as:

L'implementazione predefinita invoca ripetutamente tryAdvance (java.util.function .Consumer) finché non restituisce false.

Ovviamente, per il del metodo non fa alcuna differenza.

Il framework Stream invierà tryAdvance() durante l'esecuzione di operazioni lazy. Pertanto, quando si utilizza .peek(System.out::println).findFirst(), è possibile notare una differenza quando l'implementazione dello tryAdvance() invia più di un valore. Ancora, data l'attuale implementazione, il risultato è il primo elemento corretto. Apparentemente, il consumatore fornito dall'implementazione ignora i valori successivi dopo aver incontrato un valore.

Questo potrebbe essere collegato ad altri dettagli di implementazione come quello discusso in “Why filter() after flatMap() is ‘not completely’ lazy in Java streams?”.Se l'implementazione dello stream richiede più valori del necessario in determinate circostanze, è necessario preparare l'end di ricezione nella stessa implementazione per gestire tale caso.

Tuttavia, è necessario sottolineare che questo è il comportamento di una particolare implementazione di Stream API. Un'implementazione diversa o la prossima versione di Java potrebbero fare affidamento su un'implementazione corretta di tryAdvance. Inoltre, potrebbero esserci casi d'uso per Spliterator s oltre agli Stream.


Beh, finalmente ho trovato un esempio di un'operazione che rompe con il Spliterator:

for(Iterator<?> it=Spliterators.iterator(spliterator); it.hasNext();) { 
    System.out.println(it.next()); 
} 
Problemi correlati