tutorial - java stream filter




API Java 8 stream: Eccezioni durante la modifica degli elenchi (3)

Prendiamo un ArrayList e ArrayList con qualcosa di semplice:

List<String> list = new ArrayList<>();
for (int i = 0; i < 10; i++) {
    list.add(""+i);
}

Proverò a rimuovere un membro, ad esempio denominato 5, con un metodo di streaming API diverso. Per questo definisco il metodo, che mi darà un ConcurentModificationException quando si utilizza l'iterazione tradizionale con iteratore.

void removeMember(String clientListener) {
    list.remove(clientListener);
}

Questo codice mi dà quell'eccezione, che capisco:

list.parallelStream()
    .filter(string -> string.equalsIgnoreCase("5"))
    .forEach(string -> removeMember(string));

Tuttavia, provando solo stream() , non parallelStream() dà un'eccezione di puntatore nullo (NPE), che è strano per me:

list.stream()
    .filter(string -> string.equalsIgnoreCase("5"))
    .forEach(string -> removeMember(string));

Ora cambia il tipo di List in LinkedList<> . L'ultimo codice con stream() mi fornisce ConcurentModificationException e parallelStream() funziona all'improvviso!

Quindi, le domande.

  1. La cucina interna parallelStream() (Spliterators e altri magic) è abbastanza intelligente da utilizzare tale rimozione degli elementi per LinkedList ? Funzionerà sempre?

  2. Perché era NPE per ArrayList ? Perché NPE, non ConcurentModificationException intendo.


Durante l'utilizzo di Java8 Lambdas, è meglio non prendere la traccia dello stack sul suo valore nominale. L'errore dovrebbe essere letto per capire che un NPE è causato da qualche riga di codice all'interno di forEach forOO.So, è necessario valutare ogni riga, e vedere cosa potrebbe causare questo.


Il comportamento del tuo codice è essenzialmente indefinito (da qui le varie risposte che ottieni). La documentazione dello stream (sezione Non Intereference) afferma:

A meno che l'origine del flusso sia concomitante, la modifica dell'origine dati di un flusso durante l'esecuzione di una pipeline di flusso può causare eccezioni, risposte errate o comportamenti non conformi.

E ArrayList e LinkedList non sono concomitanti.

È possibile utilizzare una sorgente simultanea ma sarebbe più sensato allontanarsi dalla modifica della sorgente dello stream, ad esempio utilizzando Collection#removeIf :

list.removeIf(string -> string.equalsIgnoreCase("5"));

Se si desidera utilizzare gli stream, invece di modificare la raccolta originale (vedere l' immutabilità con la relativa sicurezza intrinseca per i thread ), è sufficiente recuperare un nuovo elenco senza quell'elemento:

list.stream().filter(string -> !string.equalsIgnoreCase("5"))
                    .collect(Collectors.toList());

Riguardo la tua altra domanda su parallelStream e se quell'approccio potrebbe sempre funzionare?

No, sicuramente non lo farà. Gli Lists che stai utilizzando non sono costruiti per supportare l'accesso simultaneo, a volte sembrerà funzionare, altre volte falliranno come hai visto o ti hanno dato risultati "inaspettati". Se sai che una struttura dati sarà accessibile da più thread, inserisci sempre il codice corrispondente.







java-stream