Non vi è alcuna differenza semantica tra una serie concatenata di invocazioni e una serie di invocazioni che memorizzano i valori di ritorno intermedi. Pertanto, i seguenti frammenti di codice sono equivalenti:
a = object.foo();
b = a.bar();
c = b.baz();
e
c = object.foo().bar().baz();
In ogni caso, ogni metodo viene richiamato sul risultato della chiamata precedente. Ma nel secondo caso, i risultati intermedi non vengono memorizzati ma persi al prossimo richiamo. Nel caso dell'API stream, i risultati intermedi non devono essere da utilizzare dopo aver richiamato il metodo successivo su di esso, pertanto il concatenamento è il modo naturale di utilizzare il flusso poiché garantisce intrinsecamente di non richiamare più di un metodo su un riferimento restituito.
Tuttavia, non è sbagliato memorizzare il riferimento a un flusso finché si obbedisce al contratto di non utilizzare un riferimento restituito più di una volta. Usandolo come nella tua domanda, cioè sovrascrivendo la variabile con il risultato della successiva chiamata, assicurati anche di non invocare più di un metodo su un riferimento restituito, quindi, è un uso corretto.Naturalmente, questo funziona solo con risultati intermedi dello stesso tipo, quindi quando si utilizza map
o flatMap
, ottenendo un flusso di un tipo di riferimento diverso, non è possibile sovrascrivere la variabile locale. Quindi bisogna stare attenti a non riutilizzare la vecchia variabile locale, ma, come detto, fino a quando non la si utilizza dopo l'invocazione successiva, non c'è nulla di sbagliato nella memoria intermedia.
A volte, è avere per memorizzarlo, ad es.
try(Stream<String> stream = Files.lines(Paths.get("myFile.txt"))) {
stream.filter(s -> !s.isEmpty()).forEach(System.out::println);
}
Si noti che il codice è equivalente alle seguenti alternative:
try(Stream<String> stream = Files.lines(Paths.get("myFile.txt")).filter(s->!s.isEmpty())) {
stream.forEach(System.out::println);
}
e
try(Stream<String> srcStream = Files.lines(Paths.get("myFile.txt"))) {
Stream<String> tmp = srcStream.filter(s -> !s.isEmpty());
// must not be use variable srcStream here:
tmp.forEach(System.out::println);
}
Sono equivalenti perché forEach
viene sempre invocato sul risultato di filter
che è sempre invocato sul risultato di Files.lines
e non importa su quale risultato viene richiamata l'operazione finale close()
d come chiusura interessa l'intera condotta del flusso.
Per dirla in una frase, il modo in cui lo si utilizza, è corretto.
Ho anche preferiscono a farlo in quel modo, come non concatenamento un'operazione limit
quando non si desidera applicare un limite è il modo più pulito di espressione vostro intento. E 'anche interessante notare che le alternative suggerite possono lavorare in molti casi, ma sono non semanticamente equivalenti:
.limit(condition? aLimit: Long.MAX_VALUE)
presuppone che il numero massimo di elementi, si può sempre incontrare, è Long.MAX_VALUE
ma stream può avere più elementi di quello, potrebbero persino essere infiniti.
.limit(condition? aLimit: list.size())
quando la sorgente stream è list
, sta rompendo la valutazione pigra di un flusso. In linea di principio, un'origine di flusso mutabile può essere legalmente modificata arbitrariamente fino al punto in cui inizia l'azione terminale. Il risultato rifletterà tutte le modifiche apportate fino a questo punto. Quando aggiungi un'operazione intermedia che incorpora list.size()
, ovvero la dimensione effettiva dell'elenco a questo punto, le successive modifiche applicate alla raccolta tra questo punto e l'operazione del terminale potrebbero far sì che questo valore abbia un significato diverso rispetto al previsto "effettivamente senza limite" semantico.
Paragonare con “Non Interference” section of the API documentation:
Per fonti di flusso beneducati, la sorgente può essere modificato prima dell'operazione terminale inizia e tali modifiche si rifletterà negli elementi coperti. Ad esempio, si consideri il seguente codice:
List<String> l = new ArrayList(Arrays.asList("one", "two"));
Stream<String> sl = l.stream();
l.add("three");
String s = sl.collect(joining(" "));
Prima un elenco viene creato che consiste di due stringhe: "uno"; e due". Quindi viene creato un flusso da quella lista. Successivamente l'elenco viene modificato aggiungendo una terza stringa: "tre". Infine, gli elementi del flusso vengono raccolti e uniti. Poiché la lista è stata modificata prima dell'inizio dell'operazione di raccolta del terminale, il risultato sarà una stringa di "uno due tre".
Ovviamente, questo è un raro caso d'angolo, in quanto normalmente un programmatore formerà un'intera pipeline di flusso senza modificare la raccolta di sorgenti nel mezzo. Tuttavia, il semantico rimane e potrebbe trasformarsi in un bug molto difficile da trovare quando si entra in un caso così angusto.
Inoltre, poiché non sono equivalenti, l'API del flusso non riconoscerà mai questi valori come "effettivamente senza limite". Anche specificare Long.MAX_VALUE
implica che l'implementazione dello stream deve tracciare il numero di elementi elaborati per garantire che il limite sia stato rispettato. Pertanto, non aggiungere un'operazione limit
può avere un significativo vantaggio in termini di prestazioni rispetto all'aggiunta di un limite con un numero che il programmatore si aspetta di non superare mai.
Sembra che tu non stia catturando l'output di 'stream.filter()'. – whaleberg
Se vuoi scriverlo su una sola riga, potresti scrivere '.limit (limit! = -1? Limit: Long.MAX_VALUE)' ma non lo farei. – zapl
@whaleberg, @WillShackleford, @Peter: errore mio, ho dimenticato di assegnare il valore del flusso filtrato a 'stream' nel mio codice di esempio originale. Immagino di aver capito i flussi in modo errato. Ho letto che tutti i flussi dovevano essere seguiti da un'operazione terminale per poter essere eseguiti, quindi ho pensato che fosse scorretto memorizzare il flusso risultante nella stessa variabile 'stream'. Aggiornerò la mia domanda originale – Lani