Dopo aver diffusamente parlato degli Stream, introdotti in Java 8, negli articoli Collection e Stream in Java e Parallel Stream in Java, vogliamo questa volta concentrarci sui diversi modi in cui uno Stream può essere creato/inizializzato.
Empty Stream
Il primo e più semplice modo per generare uno stream è attraverso il metodo empty()
della classe java.util.stream.Stream
. Tale metodo è utile soprattutto quando su vuole evitare di restituire NULL
nel caso di stream vuoti.
1 2 3 |
public Stream<String> streamOf(List<String> list) { return list == null || list.isEmpty() ? Stream.empty() : list.stream(); } |
A Partire da Collezioni
La modalità sicuramente più comune per ottenere uno stream è partendo da un qualsiasi oggetto di tipo java.util.Collection
(List
, Queue
, Set
, etc.), attraverso il metodo stream()
della classe Collection
:
1 2 3 4 5 6 7 8 |
Collection<String> collection = Arrays.asList("A", "B", "C", "D"); collection.stream().forEach(System.out::println); List<String> list = Arrays.asList("1", "2", "3", "4"); list.stream().forEach(System.out::println); Set<String> set = new HashSet<>(list); set.stream().forEach(System.out::println); |
A Partire da Array
Altro modo tra i più comuni per ottenere uno stream è attraverso un oggetto di tipo java.util.Array
o parte di esso, mediante il metodo statico stream()
della classe Array
, il quale ammette oltre, all’array sorgente, anche una coppia di indici di inizio e fine array.
1 2 3 4 5 6 7 |
String[] array = new String[] { "a", "b", "c" }; Stream<String> streamFromArrayFull = Arrays.stream(array); streamFromArrayFull.forEach(System.out::println); Stream<String> streamFromArrayPart = Arrays.stream(array, 1, 3); streamFromArrayPart.forEach(System.out::println); |
La classe java.util.stream.Stream
offre inoltre un metodo statico of()
che consente di generare uno strema a partire da un array di oggetti (dello stesso tipo) forniti come input:
1 2 |
Stream<String> streamOfArray = Stream.of("a", "b", "c"); streamOfArray.forEach(System.out::println); |
Stream Builder
Un utili metodo per la costruzione di uno stream è l’utilizzo del pattern Builder. A tale scopo la classe Stream
fornisce metodo statico builder()
, il cui uso richiede di esplicitarne il tipo nello statement.
1 2 |
Stream<String> streamBuilder = Stream.<String>builder().add("java").add("boss").add("is").add("great").build(); streamBuilder.forEach(System.out::println); |
Metodi generate()
ed iterate()
Il metodo generate()
della classe Stream
utilizza un oggetto di tipo Supplier
per generare uno stream. Supplier<T>
è un’interfaccia funzionale introdotta in Java 8 nel package java.util.function
. Il suo utilizzo è pensato principalmente per generare funzioni lambda senza input ma che ritornano un output di tipo specifico T->()
. Nel nostro caso l’output fornito è utilizzato per generare lo stream:
1 2 |
Stream<String> streamGenerated = Stream.generate(() -> "javaboss").limit(10); streamGenerated.forEach(System.out::println); |
Il metodo iterate()
invece, utilizza un’altra interfaccia funzionale: UnaryOperator
. Esso rappresenta un’operazione su un singolo operando che produce un risultato dello stesso tipo del l’operando. In altri termini rappresenta una specializzazione funzione lambda per il caso in cui l’operando e il risultato sono dello stesso tipo. Utilizzandola per generare uno stream come argomento di iterate()
si può scrivere:
1 2 |
Stream<Integer> streamIterated = Stream.iterate(1, n -> n * 2).limit(5); streamIterated.forEach(System.out::println); |
Java NIO
La generazione di stream è possibile anche da file per mezzo alla classe di utilità Files
del package java.nio.file
. In particolare il metodo lines()
consente di generare uno stream da file di testo in cui ciascuna linea diviene un elemento dello stream:
1 2 3 4 |
Path path = Paths.get("file.txt"); Stream<String> streamOfStrings = Files.lines(path); streamOfStrings.forEach(System.out::println); streamOfStrings.close(); |
Stream di Tipi Primitivi
Poiché Stream
è un’interfaccia generica e non è possibile utilizzare tipi primitivi come parametro di tipo con tali interfacce, sono state create tre nuove interfacce specializzate: IntStream
, LongStream
, DoubleStream
. L’utilizzo di tali interfacce mitica l’auto-boxing non necessario, consentendo una maggiore produttività. In particolare:
IntStream.range() |
Restituisce uno stream a partire da una coppia di indici ed incrementando di 1. |
LongStream.rangeClosed() |
Restituisce uno stream a partire da una coppia di indici ed incrementando di 1. |
Random.doubles() | Restituisce uno stream di numeri casuali. |
1 2 3 4 5 6 7 8 9 |
IntStream intStream = IntStream.range(5, 10); intStream.forEach(System.out::println); LongStream longStream = LongStream.rangeClosed(5, 10); longStream.forEach(System.out::println); Random random = new Random(); DoubleStream doubleStream = random.doubles(3); doubleStream.forEach(System.out::println); |
Codice Sorgente
Il codice sorgente con tutti gli esempi mostrati è scaricabile qui stream-create.