12 modi comuni per utilizzare i flussi Java

Fonte: Dev.to L'API Java Streams è apparsa per la prima volta in Java 8. Il suo scopo è fornire un modo più compatto per eseguire operazioni comuni su raccolte di oggetti. Inoltre, l'API Java Streams può essere utilizzata per implementare algoritmi complessi. In questo articolo parleremo dei casi d'uso comuni di Java Streams. Pausa caffè #108.  12 usi comuni dei flussi Java, come valutare l'allocazione della memoria di un oggetto in Java - 1Per prima cosa chiariamo alcune nozioni di base:
  • stream() : crea uno stream dalla raccolta.

  • collector() - raccoglie un flusso in un oggetto. Un oggetto può essere una raccolta, una primitiva o una classe personalizzata.

  • Collectors è una classe che fornisce (molti) metodi statici per raccogliere flussi.

Ora diamo un'occhiata ad alcuni casi d'uso per Streams:

1. Filtraggio

  • Utilizzato per rimuovere valori da una raccolta in base a una condizione.

  • Per filtrare gli elementi della raccolta in base a una condizione, utilizzare il metodo filter() . Vengono salvati solo gli elementi corrispondenti.

Esempio: rimuovi tutti i numeri dispari dalla lista.
List<Integer> evenNumbers = originalList.stream()
        .filter(n -> n % 2 == 0)
        .collect(Collectors.toList());

2. Preelaborazione

  • Utile quando ogni valore in una raccolta deve essere modificato sul posto.

  • Il metodo map() viene utilizzato per applicare una funzione a ciascun elemento della raccolta e restituire una nuova raccolta di valori calcolati.

Ad esempio, convertiamo ciascun valore nel relativo quadrato.
List<Integer> squares = originalList.stream()
        .map(n -> n * n)
        .collect(Collectors.toList());

3. Conversione

  • Utile quando vogliamo trasformare una collezione in un'altra collezione.

  • Esistono diversi modi per raggiungere questo obiettivo.

Come accennato in precedenza, possiamo utilizzare i metodi map() e collector() per trasformare una raccolta in un'altra raccolta.

Esempio 1: creare una mappa da elenchi.

Converti un elenco di stringhe in una mappa di stringhe e lunghezze.
Map<String, Integer> wordLengths = words.stream()
        .collect(Collectors.toMap(
                word -> word,
                word -> word.length()));

Esempio 2. Conversione di una lista in insiemi.

Questo è un caso d'uso comune per la rimozione dei duplicati. Inoltre, se vogliamo reinserire gli elementi nell'elenco, possiamo utilizzare due volte i metodi stream() e collector() . Ad esempio, convertiamo un elenco di stringhe in un elenco di stringhe univoche:
// if we want to collect to a set
Set<String> uniqueWords = words.stream()
        .collect(Collectors.toSet());

// OR

// if we want to start and end as a list
List<String> uniqueWords = words.stream()
        .collect(Collectors.toSet()).stream().collect(Collectors.toList());

Esempio 3. Conversione di un elenco di prodotti in un elenco dei loro nomi. (Appiattimento - Allineamento)

List<String> productNames = products.stream()
        .map(product -> product.getName())
        .collect(Collectors.toList());

4. Riduzione

  • Riduce Collection a un singolo valore.

  • Il metodo reduce() viene utilizzato per applicare una funzione a ciascun elemento della raccolta e restituire un singolo valore.

Tieni presente che poiché il metodo reduce() restituisce un singolo valore, non può essere utilizzato per restituire una Collection. Esempio, riassumiamo tutti i valori della lista:
int sum = numbers.stream()
        .reduce(0, (a, b) -> a + b);

5. Raggruppamento

  • Raggruppa gli elementi di una Collection in base ad una determinata condizione.

  • Per raggruppare gli elementi Collection in base alla condizione, utilizzare il metodo Collectors.groupingBy() .

Ad esempio, raggruppiamo tutti i prodotti in elenchi di prodotti in base alle rispettive categorie.
Map<String, List<Product>> productsByCategory = products.stream()
        .collect(Collectors.groupingBy(product -> product.getCategory()));

6. Trovare

  • Cerca il primo o qualsiasi elemento della raccolta che corrisponde a una condizione.

  • I metodi findFirst() e findAny() vengono utilizzati per la ricerca .

Di solito è simile a una ricerca lineare. Ad esempio, stiamo cercando la prima parola dell'elenco, la cui lunghezza supera i 5 caratteri.
Optional<String> firstLongWord = words.stream()
        .filter(word -> word.length() > 5)
        .findFirst();
// Note that findFirst() and findAny() methods return Optional<T> objects.

7. Ordinamento

  • Ordina gli elementi delle raccolte.

  • Il metodo sorted() viene utilizzato per l'ordinamento .

In genere, Collections.sort() è sufficiente per ordinare una raccolta. Possiamo usare sorted() specificatamente se vogliamo eseguire un'altra operazione. Ad esempio, ordiniamo un elenco di numeri in ordine crescente e quindi restituiamo i primi k elementi.
List<Integer> topK = numbers.stream()
        .sorted()
        .limit(k)
        .collect(Collectors.toList());

8. Partizionamento

  • Separa gli elementi di una Collection in base ad una determinata condizione.

  • Il metodo Collectors.partitioningBy() viene utilizzato per separare gli elementi .

Una suddivisione è simile a un gruppo, tranne per il fatto che restituisce due raccolte: una per gli elementi che soddisfano la condizione e una per gli elementi che non soddisfano la condizione. Ad esempio, dividiamo gli studenti in quelli che hanno superato l'esame e quelli che non lo hanno superato.
Map<Boolean, List<Student>> passingFailing = students
        .stream()
        .collect(Collectors.partitioningBy(s -> s.getGrade() >= PASS_THRESHOLD));

9. Conteggio

  • Conta il numero di elementi che soddisfano una condizione.

  • Il metodo count() viene utilizzato per contare il numero di elementi che soddisfano una condizione .

Ad esempio, contiamo il numero di parole nell'elenco la cui lunghezza supera i 5 caratteri.
long count = words.stream()
        .filter(word -> word.length() > 5)
        .count();

10. Gamma

  • Crea un intervallo di valori.

  • Per creare un intervallo di valori, utilizzare il metodo range() .

Esistono classi speciali per la creazione di flussi di determinati tipi: IntStream , LongStream , DoubleStream e Stream . Queste classi sono utili quando si lavora con tipi numerici primitivi. Per convertire un array in un flusso, utilizzare Arrays.stream() . Ad esempio, creiamo una matrice di numeri da 0 a 10.
int[] numbers = IntStream.range(0, 10).toArray();

11. Abbinamento

  • Corrisponde agli elementi di una raccolta con un predicato (condizione).

  • Metodi come anyMatch() , allMatch() e noneMatch() vengono utilizzati per abbinare gli elementi della raccolta con un predicato e restituire un valore booleano .

Ad esempio, controlliamo i prodotti con un prezzo superiore a 10.
// true when all elements match the predicate
boolean allMatch = products.stream()
        .allMatch(product -> product.getPrice() > 10);

// true when any element matches the predicate
boolean anyMatch = products.stream()
        .anyMatch(product -> product.getPrice() > 10);

// true when no elements match the predicate
boolean noneMatch = products.stream()
        .noneMatch(product -> product.getPrice() > 10);

12. Partecipazione

  • Concatena gli elementi di una raccolta in una stringa.

  • Per unire gli elementi della raccolta in una stringa, utilizzare il metodo Collectors.joining() .

Ad esempio, concateniamo tutte le parole di un elenco in un'unica stringa.
String joinedWords = words.stream()
        .collect(Collectors.joining(" "));
Questo è tutto per gli scenari generali. Esistono altri scenari meno comuni che puoi esplorare da solo:
  • Flussi paralleli;
  • Statistiche;
  • Collezionisti personalizzati.

Vantaggi dei thread

  • Codice più compatto: riduce la quantità di codice necessaria per elaborare la raccolta.

  • Meno variabili intermedie. Le variabili che intervengono possono causare errori. Meno ce ne sono, più facile è evitare errori imprevisti.

  • Codice intuitivo. Alcuni sviluppatori non saranno d'accordo sul fatto che i thread siano più intuitivi rispetto ad altri metodi. Tuttavia, una volta che ci si abitua, diventano molto più intuitivi rispetto ad altri metodi.

Grazie per aver letto. Spero che questo articolo ti sia piaciuto. Esistono molti altri casi in cui è possibile utilizzare i thread non trattati in questo argomento. Sentiti libero di aggiungere qualsiasi scenario comune che mi è sfuggito.

Come valutare l'allocazione di memoria di un oggetto in Java

Fonte: DZone Questo articolo mostra i tre modi più noti per valutare l'allocazione di memoria di un oggetto in Java.

Valutazione della memoria utilizzando Profiler

Il modo più semplice per stimare la memoria di alcuni oggetti è esaminare direttamente la memoria JVM utilizzando un profiler come Visual VM . Pausa caffè #108.  12 usi comuni dei flussi Java, Come valutare l'allocazione della memoria di un oggetto in Java - 2Il problema con questo approccio è che è necessario connettersi a una JVM in esecuzione, cosa che potrebbe non essere possibile per gli ambienti di produzione per motivi di sicurezza.

Valutazione della memoria mediante strumenti

Un altro modo per stimare la memoria allocata per un determinato oggetto è utilizzare Instruments. In termini semplici, dobbiamo creare una classe e compilarla in un JAR. Dopo aver creato il JAR, dobbiamo eseguire la nostra JVM insieme a quel JAR. Puoi scoprire di più su questo metodo qui . Lo svantaggio qui è la necessità di aggiungere un file jar specifico alla JVM, che potrebbe non essere accettabile per la produzione a causa di problemi di sicurezza o correlati.

Valutazione della memoria utilizzando la libreria JOL

Come altra opzione, possiamo utilizzare JOL Library . Si tratta di una libreria molto potente in grado di fornire una stima dettagliata del peso di un oggetto e della memoria allocata da un'istanza dell'oggetto. Per utilizzare la libreria, dobbiamo aggiungere una dipendenza:
<dependency>
    <groupId>org.openjdk.jol</groupId>
    <artifactId>jol-core</artifactId>
    <version>0.16</version>
</dependency>
Dopodiché possiamo usarlo in questo modo:
out.println(GraphLayout.parseInstance(myObject).totalSize() / 1024000d + " MB")

ObjectSizeCalculator dall'archivio Twitter

Il repository GitHub pubblico di Twitter ha una classe di strumenti chiamata ObjectSizeCalculator che può stimare la memoria allocata per una determinata istanza di oggetto. Non richiede molta memoria o tempo per l'utilizzo. Il processo di valutazione richiede pochi secondi, anche per oggetti di grandi dimensioni. Usare questa classe è abbastanza semplice:
ObjectSizeCalculator.getObjectSize(address)
Raccomando questo metodo, ma tieni presente che è supportato solo da Java Hotspot, OpenJDK e TwitterJDK.