Condivisione della tecnologia

Spiegazione dettagliata dell'API Java Stream: un potente strumento per l'elaborazione efficiente dei dati di raccolta

2024-07-12

한어Русский языкEnglishFrançaisIndonesianSanskrit日本語DeutschPortuguêsΕλληνικάespañolItalianoSuomalainenLatina

introduzione

Java 8 introduce molte nuove funzionalità, le più importanti delle quali sono le espressioni Lambda e l'API Stream. L'API Stream fornisce un metodo efficiente e conciso per elaborare i dati di raccolta, rendendo il codice più conciso e chiaro e ha un'elevata leggibilità e manutenibilità. Questo articolo approfondirà l'uso dell'API Java Stream, inclusi concetti di base, operazioni comuni, elaborazione parallela, casi pratici e best practice.

Sommario

  1. Cos'è l'API Stream
  2. Operazioni di base dell'API Stream
  3. Operazioni avanzate dell'API Stream
  4. Flusso parallelo
  5. Caso pratico dell'API Stream
  6. Best practice per le API di flusso
  7. Domande e soluzioni frequenti
  8. Riassumere

Cos'è l'API Stream

L'API Stream è un'astrazione introdotta in Java 8 per l'elaborazione dei dati di raccolta che consente l'elaborazione dei dati in modo dichiarativo (simile alle istruzioni SQL). L'API Stream fornisce molte operazioni potenti che possono essere utilizzate per filtrare, ordinare, mappare, ridurre e altre operazioni sulle raccolte, semplificando notevolmente il codice.

Caratteristiche

  • programmazione dichiarativa: utilizza l'API Stream per scrivere codice in modo dichiarativo, riducendo il codice boilerplate.
  • chiamata a catena: Le operazioni dell'API Stream possono essere chiamate in catena, migliorando la leggibilità del codice.
  • valutazione pigra: Le operazioni intermedie vengono valutate pigramente e vengono valutate solo quando viene eseguita l'operazione terminale.
  • elaborazione parallela: Supporta l'elaborazione parallela e può sfruttare appieno le CPU multi-core.

Operazioni di base dell'API Stream

Crea flusso

L'API Stream offre una varietà di modi per creare uno stream. Quelli comuni includono quanto segue:

  1. Crea dalla raccolta
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
  • 1
  • 2
  1. Crea da array
String[] array = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(array);
  • 1
  • 2
  1. utilizzoStream.of
Stream<String> stream = Stream.of("a", "b", "c");
  • 1
  1. utilizzoStream.generate
Stream<Double> stream = Stream.generate(Math::random).limit(10);
  • 1
  1. utilizzoStream.iterate
Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(10);
  • 1

operazioni intermedie

Le operazioni intermedie vengono utilizzate per convertire Stream e vengono valutate in modo lento. Le operazioni intermedie comuni includono quanto segue:

  1. filter: utilizzato per filtrare gli elementi.
Stream<String> stream = list.stream().filter(s -> s.startsWith("a"));
  • 1
  1. map: Utilizzato per mappare ciascun elemento al risultato corrispondente.
Stream<String> stream = list.stream().map(String::toUpperCase);
  • 1
  1. flatMap: Utilizzato per convertire ciascun elemento in uno Stream e quindi unirlo in un unico Stream.
Stream<String> stream = list.stream().flatMap(s -> Stream.of(s.split("")));
  • 1
  1. distinct: Utilizzato per rimuovere i duplicati.
Stream<String> stream = list.stream().distinct();
  • 1
  1. sorted: utilizzato per l'ordinamento.
Stream<String> stream = list.stream().sorted();
  • 1
  1. peek: Utilizzato per visualizzare ciascun elemento durante l'elaborazione.
Stream<String> stream = list.stream().peek(System.out::println);
  • 1

Funzionamento del terminale

Le operazioni del terminale vengono utilizzate per avviare il calcolo del flusso e generare risultati. Le operazioni del terminale comuni includono quanto segue:

  1. forEach: esegue un'operazione su ciascun elemento.
list.stream().forEach(System.out::println);
  • 1
  1. collect: converte il flusso in altri moduli.
List<String> result = list.stream().collect(Collectors.toList());
  • 1
  1. reduce: riduce gli elementi nello stream in un valore.
Optional<String> result = list.stream().reduce((s1, s2) -> s1 + s2);
  • 1
  1. toArray:Converte il flusso in un array.
String[] array = list.stream().toArray(String[]::new);
  • 1
  1. count: conta il numero di elementi.
long count = list.stream().count();
  • 1
  1. anyMatchallMatchnoneMatch: Utilizzato per abbinare il giudizio.
boolean anyMatch = list.stream().anyMatch(s -> s.startsWith("a"));
boolean allMatch = list.stream().allMatch(s -> s.startsWith("a"));
boolean noneMatch = list.stream().noneMatch(s -> s.startsWith("a"));
  • 1
  • 2
  • 3
  1. findFirstfindAny: Utilizzato per trovare elementi.
Optional<String> first = list.stream().findFirst();
Optional<String> any = list.stream().findAny();
  • 1
  • 2

Operazioni avanzate dell'API Stream

ordinare

utilizzosortedIl metodo ordina lo Stream e può passare in un comparatore.

List<String> list = Arrays.asList("b", "c", "a");
List<String> sortedList = list.stream().sorted().collect(Collectors.toList());
// 逆序排序
List<String> sortedListDesc = list.stream().sorted(Comparator.reverseOrder()).collect(Collectors.toList());
  • 1
  • 2
  • 3
  • 4

filtro

utilizzofilterMetodo per filtrare gli elementi in Stream.

List<String> list = Arrays.asList("a", "b", "c");
List<String> filteredList = list.stream().filter(s -> s.startsWith("a")).collect(Collectors.toList());
  • 1
  • 2

Mappatura

utilizzomapIl metodo mappa gli elementi nello Stream.

List<String> list = Arrays.asList("a", "b", "c");
List<String> mappedList = list.stream().map(String::toUpperCase).collect(Collectors.toList());
  • 1
  • 2

Statuto

utilizzoreduceMetodo per ridurre gli elementi nello Stream.

List<String> list = Arrays.asList("a", "b", "c");
String result = list.stream().reduce("", (s1, s2) -> s1 + s2);
  • 1
  • 2

raccogliere

utilizzocollectI metodi convertono Stream in altri moduli.

List<String> list = Arrays.asList("a", "b", "c");
List<String> collectedList = list.stream().collect(Collectors.toList());
Set<String> collectedSet = list.stream().collect(Collectors.toSet());
String joinedString = list.stream().collect(Collectors.joining(","));
  • 1
  • 2
  • 3
  • 4

Flusso parallelo

Parallel Stream può sfruttare appieno i vantaggi delle CPU multi-core per migliorare l'efficienza dell'elaborazione dei dati.poter usareparallelStreamIl metodo crea un flusso parallelo.

List<String> list = Arrays.asList("a", "b", "c");
List<String> parallelList = list.parallelStream().map(String::toUpperCase).collect(Collectors.toList());
  • 1
  • 2

Può anche essere usatoparallelIl metodo converte un flusso normale in un flusso parallelo.

List<String> list = Arrays.asList("a", "b", "c");
List<String> parallelList = list.stream().parallel().map(String::toUpperCase).collect(Collectors.toList());
  • 1
  • 2

Va notato che il flusso parallelo non è sempre più veloce del flusso seriale e deve essere testato in base alla situazione specifica.

Caso pratico dell'API Stream

Elaborazione dati raccolti

Caso 1: filtra e trasforma le raccolte

Data una raccolta di stringhe, filtrare le stringhe la cui lunghezza è inferiore a 3 e convertire le stringhe rimanenti in maiuscolo.

List<String> list = Arrays.asList("a", "ab", "abc", "abcd");
List<String> result = list.stream()
    .filter(s -> s.length() >= 3)
    .map(String::toUpperCase)
    .collect(Collectors.toList());
System.out.println(result); // 输出:[ABC, ABCD]
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
Caso 2: Calcolare la media

Dato un insieme di numeri interi, calcolare la media di tutti i numeri interi.

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
OptionalDouble average = list.stream()
    .mapToInt(Integer::intValue)
    .average();
average.ifPresent(System.out::println); // 输出:3.0
  • 1
  • 2
  • 3
  • 4
  • 5

Operazioni sui file

Caso 3: leggere il contenuto del file

Utilizzando l'API Stream

Leggere il contenuto del file e inviarlo alla console.

try (Stream<String> lines = Files.lines(Paths.get("example.txt"))) {
    lines.forEach(System.out::println);
} catch (IOException e) {
    e.printStackTrace();
}
  • 1
  • 2
  • 3
  • 4
  • 5
Caso 4: contare il numero di occorrenze delle parole

Leggi il contenuto del file e conta il numero di occorrenze di ogni parola.

try (Stream<String> lines = Files.lines(Paths.get("example.txt"))) {
    Map<String, Long> wordCount = lines
        .flatMap(line -> Arrays.stream(line.split("\W+")))
        .collect(Collectors.groupingBy(String::toLowerCase, Collectors.counting()));
    wordCount.forEach((word, count) -> System.out.println(word + ": " + count));
} catch (IOException e) {
    e.printStackTrace();
}
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8

Operazioni sulla base di dati

Caso 5: elaborazione dei risultati delle query del database

Diciamo che abbiamo una tabella di databaseusers, contenente campiidnameEage . Possiamo utilizzare l'API Stream per elaborare i risultati della query.

List<User> users = queryDatabase();
List<String> names = users.stream()
    .filter(user -> user.getAge() > 18)
    .map(User::getName)
    .collect(Collectors.toList());
System.out.println(names);
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6

Best practice per le API di flusso

  1. Evita la parallelizzazione non necessaria: Parallel Stream non è sempre più veloce e dovrebbe essere scelto caso per caso.
  2. Uso corretto delle operazioni intermedie e delle operazioni terminali: Le operazioni intermedie vengono valutate pigramente e vengono valutate solo quando viene eseguita l'operazione terminale.
  3. Prestare attenzione alla riusabilità di Stream: una volta consumato, uno stream non può essere utilizzato nuovamente. Se è necessario riutilizzarlo, è possibile convertire lo stream in una raccolta prima di utilizzarlo.
  4. Usa il raccoglitore giustoCollectorsLa classe offre una varietà di collezionisti ed è possibile scegliere il collezionista appropriato in base alle esigenze specifiche.
  5. Gestire le eccezioni: quando si utilizza l'API Stream, è necessario gestire le possibili eccezioni, in particolare nelle operazioni sui file e sul database.

Domande e soluzioni frequenti

Il flusso è chiuso

Una volta consumato, uno stream non può essere utilizzato nuovamente. Se è necessario riutilizzarlo, è possibile convertirlo in una raccolta prima di utilizzarlo.

List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
stream.forEach(System.out::println);
stream.forEach(System.out::println); // 会抛出IllegalStateException
  • 1
  • 2
  • 3
  • 4

Problemi di prestazione

I flussi paralleli non sono sempre più veloci dei flussi seriali e devono essere testati caso per caso.poter usareForkJoinPoolPer ottimizzare le prestazioni del flusso parallelo.

ForkJoinPool customThreadPool = new ForkJoinPool(4);
customThreadPool.submit(() ->
    list.parallelStream().forEach(System.out::println)
).get();
  • 1
  • 2
  • 3
  • 4

perdita di memoria

Quando si utilizza l'API Stream per elaborare grandi quantità di dati, è necessario prestare attenzione al problema delle perdite di memoria.poter usareclosemetodo per chiudere lo stream o utilizzaretry-with-resourcesL'istruzione chiude automaticamente lo Stream.

try (Stream<String> lines = Files.lines(Paths.get("example.txt"))) {
    lines.forEach(System.out::println);
} catch (IOException e) {
    e.printStackTrace();
}
  • 1
  • 2
  • 3
  • 4
  • 5

Riassumere

Questo articolo introduce in dettaglio l'uso dell'API Java Stream, comprese le operazioni di base, le operazioni avanzate, l'elaborazione parallela, i casi pratici e le procedure consigliate. Utilizzando razionalmente l'API Stream, gli sviluppatori possono semplificare notevolmente il codice, migliorare la leggibilità e la manutenibilità del codice e anche migliorare l'efficienza dell'elaborazione dei dati. Spero che questo articolo ti sia utile per l'utilizzo dell'API Stream nello sviluppo Java.

Java Stream API è un potente strumento per l'elaborazione dei dati di raccolta. Attraverso l'uso flessibile di varie operazioni, è possibile ottenere un'elaborazione efficiente dei dati e uno streaming computing. Se non hai ancora utilizzato l'API Stream, ti consigliamo di apprendere e padroneggiare questo potente strumento il prima possibile e di applicarlo al tuo progetto per migliorare l'efficienza dello sviluppo e la qualità del codice.