Introduzione a StreamEx

1. Panoramica

Una delle caratteristiche più interessanti di Java 8 è l' API Stream , che, in poche parole, è un potente strumento per l'elaborazione di sequenze di elementi.

StreamEx è una libreria che fornisce funzionalità aggiuntive per l'API Stream standard insieme ai miglioramenti delle prestazioni.

Ecco alcune caratteristiche principali:

  • Modi più brevi e convenienti per svolgere le attività quotidiane
  • Compatibilità al 100% con gli stream JDK originali
  • Cordialità per l'elaborazione parallela: qualsiasi nuova funzionalità sfrutta il più possibile i flussi paralleli
  • Prestazioni e sovraccarico minimo. Se StreamEx consente di risolvere l'attività utilizzando meno codice rispetto allo Stream standard , non dovrebbe essere significativamente più lento del solito modo (e talvolta è anche più veloce)

In questo tutorial, presenteremo alcune delle funzionalità dell'API StreamEx .

2. Impostazione dell'esempio

Per utilizzare StreamEx , dobbiamo aggiungere la seguente dipendenza al pom.xml :

 one.util streamex 0.6.5 

L'ultima versione della libreria può essere trovata su Maven Central.

Attraverso questo tutorial, useremo una semplice classe User :

public class User { int id; String name; Role role = new Role(); // standard getters, setters, and constructors }

E una semplice classe di ruolo :

public class Role { }

3. Metodi di scelta rapida per i servizi di raccolta

Una delle operazioni terminali più popolari di Streams è l' operazione di raccolta ; ciò consente di riconfezionare gli elementi Stream in una raccolta di nostra scelta.

Il problema è che il codice può diventare inutilmente dettagliato per scenari semplici:

users.stream() .map(User::getName) .collect(Collectors.toList());

3.1. Raccolta in una raccolta

Ora, con StreamEx, non è necessario fornire un Collector per specificare che abbiamo bisogno di List , Set, Map, InmutableList, ecc .:

List userNames = StreamEx.of(users) .map(User::getName) .toList();

L' operazione di raccolta è ancora disponibile nell'API se vogliamo eseguire qualcosa di più complicato che prendere elementi da un flusso e inserirli in una raccolta.

3.2. Collezionisti avanzati

Un'altra scorciatoia è raggruppareBy :

Map
    
      role2users = StreamEx.of(users) .groupingBy(User::getRole);
    

Questo produrrà una mappa con il tipo di chiave specificato nel riferimento al metodo, producendo qualcosa di simile al gruppo per operazione in SQL.

Utilizzando la semplice Stream API, avremmo bisogno di scrivere:

Map
    
      role2users = users.stream() .collect(Collectors.groupingBy(User::getRole));
    

Una forma abbreviata simile può essere trovata per Collectors.joining ():

StreamEx.of(1, 2, 3) .joining("; "); // "1; 2; 3"

Che prende tutti gli elementi nel flusso e produce una stringa che li concatena tutti.

4. Aggiunta, rimozione e selezione di elementi

In alcuni scenari, abbiamo un elenco di oggetti di diversi tipi e dobbiamo filtrarli per tipo:

List usersAndRoles = Arrays.asList(new User(), new Role()); List roles = StreamEx.of(usersAndRoles) .select(Role.class) .toList();

Possiamo aggiungere elementi all'inizio o alla fine del nostro Stream , con queste pratiche operazioni:

List appendedUsers = StreamEx.of(users) .map(User::getName) .prepend("(none)") .append("LAST") .toList();

Possiamo rimuovere elementi null indesiderati utilizzando nonNull () e utilizzare Stream come Iterable :

for (String line : StreamEx.of(users).map(User::getName).nonNull()) { System.out.println(line); }

5. Supporto per operazioni matematiche e tipi primitivi

StreamEx aggiunge i supporti per i tipi primitivi, come possiamo vedere in questo esempio autoesplicativo:

short[] src = {1,2,3}; char[] output = IntStreamEx.of(src) .map(x -> x * 5) .toCharArray();

Ora prendiamo un array di elementi doppi in modo non ordinato. Vogliamo creare un array costituito dalla differenza tra ogni coppia.

Possiamo usare il metodo pairMap per eseguire questa operazione:

public double[] getDiffBetweenPairs(double... numbers) { return DoubleStreamEx.of(numbers) .pairMap((a, b) -> b - a) .toArray(); }

6. Operazioni sulla mappa

6.1. Filtro per chiavi

Un'altra caratteristica utile è la possibilità di creare un flusso da una mappa e filtrare gli elementi utilizzando i valori a cui puntano.

In questo caso, stiamo prendendo tutti i valori non nulli:

Map nameToRole = new HashMap(); nameToRole.put("first", new Role()); nameToRole.put("second", null); Set nonNullRoles = StreamEx.ofKeys(nameToRole, Objects::nonNull) .toSet();

6.2. Operating on Key-Value Pairs

We can also operate on key-value pairs by creating an EntryStream instance:

public Map
    
      transformMap( Map
     
       role2users) { Map
      
        users2roles = EntryStream.of(role2users) .flatMapValues(List::stream) .invert() .grouping(); return users2roles; }
      
     
    

The special operation EntryStream.of takes a Map and transforms it into a Stream of key-value objects. Then we use flatMapValues operation to transform our list of roles to a Stream of single values.

Next, we can invert the key-value pair, making the User class the key and the Role class the value.

And finally, we can use the grouping operation to transform our map to the inversion of the one received, all with just four operations.

6.3. Key-Value Mapping

We can also map keys and values independently:

Map mapToString = EntryStream.of(users2roles) .mapKeys(String::valueOf) .mapValues(String::valueOf) .toMap();

With this, we can quickly transform our keys or values to another required type.

7. File Operations

Using StreamEx, we can read files efficiently, i.e., without loading full files at once. It's handy while processing large files:

StreamEx.ofLines(reader) .remove(String::isEmpty) .forEach(System.out::println);

Note that we've used remove() method to filter away empty lines.

Point to note here is that StreamEx won't automatically close the file. Hence, we must remember to manually perform closing operation on both file reading and writing occasion to avoid unnecessary memory overhead.

8. Conclusion

In questo tutorial, abbiamo imparato a conoscere StreamEx e le sue diverse utilità. C'è molto di più da esaminare - e qui hanno un pratico cheat sheet.

Come sempre, il codice sorgente completo è disponibile su GitHub.