Compartir tecnología

Explicación detallada de Java Stream API: una poderosa herramienta para el procesamiento eficiente de datos de recopilación

2024-07-12

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

introducción

Java 8 introduce muchas características nuevas, las más notables son las expresiones Lambda y la API Stream. Stream API proporciona un método eficiente y conciso para procesar datos de recopilación, lo que hace que el código sea más conciso y claro, y tiene una alta legibilidad y mantenibilidad. Este artículo profundizará en el uso de Java Stream API, incluidos conceptos básicos, operaciones comunes, procesamiento paralelo, casos prácticos y mejores prácticas.

Tabla de contenido

  1. ¿Qué es la API de transmisión?
  2. Operaciones básicas de Stream API
  3. Operaciones avanzadas de Stream API
  4. Corriente paralela
  5. Caso práctico de Stream API
  6. Mejores prácticas para las API de transmisión
  7. Preguntas frecuentes y soluciones
  8. Resumir

¿Qué es la API de transmisión?

Stream API es una abstracción introducida en Java 8 para procesar datos de recopilación que permite que los datos se procesen de manera declarativa (similar a las declaraciones SQL). La API Stream proporciona muchas operaciones poderosas que se pueden usar para filtrar, ordenar, mapear, reducir y otras operaciones en colecciones, simplificando enormemente el código.

Características

  • programación declarativa: Utilice Stream API para escribir código de forma declarativa, reduciendo el código repetitivo.
  • llamada en cadena: Las operaciones de Stream API se pueden llamar en cadena, lo que mejora la legibilidad del código.
  • evaluación perezosa: Las operaciones intermedias se evalúan de forma diferida y solo se evalúan cuando se realiza la operación del terminal.
  • procesamiento en paralelo: Admite procesamiento paralelo y puede aprovechar al máximo las CPU de múltiples núcleos.

Operaciones básicas de Stream API

Crear secuencia

La API Stream proporciona una variedad de formas de crear una transmisión. Las más comunes incluyen las siguientes:

  1. Crear desde la colección
List<String> list = Arrays.asList("a", "b", "c");
Stream<String> stream = list.stream();
  • 1
  • 2
  1. Crear desde una matriz
String[] array = {"a", "b", "c"};
Stream<String> stream = Arrays.stream(array);
  • 1
  • 2
  1. usarStream.of
Stream<String> stream = Stream.of("a", "b", "c");
  • 1
  1. usarStream.generate
Stream<Double> stream = Stream.generate(Math::random).limit(10);
  • 1
  1. usarStream.iterate
Stream<Integer> stream = Stream.iterate(0, n -> n + 2).limit(10);
  • 1

operaciones intermedias

Las operaciones intermedias se utilizan para convertir Stream y se evalúan de forma diferida. Las operaciones intermedias comunes incluyen las siguientes:

  1. filter: utilizado para filtrar elementos.
Stream<String> stream = list.stream().filter(s -> s.startsWith("a"));
  • 1
  1. map: Se utiliza para asignar cada elemento al resultado correspondiente.
Stream<String> stream = list.stream().map(String::toUpperCase);
  • 1
  1. flatMap: Se utiliza para convertir cada elemento en un Stream y luego fusionarlo en un Stream.
Stream<String> stream = list.stream().flatMap(s -> Stream.of(s.split("")));
  • 1
  1. distinct: Se utiliza para eliminar duplicados.
Stream<String> stream = list.stream().distinct();
  • 1
  1. sorted: utilizado para clasificar.
Stream<String> stream = list.stream().sorted();
  • 1
  1. peek: Se utiliza para ver cada elemento durante el procesamiento.
Stream<String> stream = list.stream().peek(System.out::println);
  • 1

Operación de terminales

Las operaciones de terminal se utilizan para iniciar el cálculo de Stream y generar resultados. Las operaciones de terminal comunes incluyen las siguientes:

  1. forEach: Realiza una operación en cada elemento.
list.stream().forEach(System.out::println);
  • 1
  1. collect: convierte Stream a otras formas.
List<String> result = list.stream().collect(Collectors.toList());
  • 1
  1. reduce: Reduce los elementos del Stream a un valor.
Optional<String> result = list.stream().reduce((s1, s2) -> s1 + s2);
  • 1
  1. toArray:Convierte Stream en matriz.
String[] array = list.stream().toArray(String[]::new);
  • 1
  1. count: Cuente el número de elementos.
long count = list.stream().count();
  • 1
  1. anyMatchallMatchnoneMatch: Se utiliza para hacer coincidir el juicio.
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: Se utiliza para buscar elementos.
Optional<String> first = list.stream().findFirst();
Optional<String> any = list.stream().findAny();
  • 1
  • 2

Operaciones avanzadas de Stream API

clasificar

usarsortedEl método ordena el Stream y puede pasar un comparador.

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

filtrar

usarfilterMétodo para filtrar elementos en Stream.

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

cartografía

usarmapEl método asigna elementos en el Stream.

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

Estatuto

usarreduceMétodo para reducir los elementos en el Stream.

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

recolectar

usarcollectLos métodos convierten Stream a otras formas.

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

Corriente paralela

Parallel Stream puede aprovechar al máximo las ventajas de las CPU multinúcleo para mejorar la eficiencia del procesamiento de datos.puedo usarparallelStreamEl método crea una secuencia paralela.

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

También puede ser usadoparallelEl método convierte un Stream normal en un Stream paralelo.

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

Cabe señalar que la transmisión en paralelo no siempre es más rápida que la transmisión en serie y debe probarse de acuerdo con la situación específica.

Caso práctico de Stream API

Procesamiento de datos de recopilación

Caso 1: Filtrar y transformar colecciones

Dada una colección de cadenas, filtre las cadenas cuya longitud sea inferior a 3 y convierta las cadenas restantes a mayúsculas.

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: Calcular el promedio

Dado un conjunto de números enteros, calcule el promedio de todos los números enteros.

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

Operaciones de archivos

Caso 3: leer el contenido del archivo

Usando la API de transmisión

Lea el contenido del archivo y envíelo a la consola.

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: Cuente el número de apariciones de palabras

Lea el contenido del archivo y cuente el número de apariciones de cada palabra.

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

Operaciones de base de datos

Caso 5: Procesamiento de resultados de consultas de bases de datos

Digamos que tenemos una tabla de base de datos.users, que contiene camposidnameyage . Podemos utilizar la API Stream para procesar los resultados de la consulta.

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

Mejores prácticas para las API de transmisión

  1. Evite la paralelización innecesaria: Parallel Stream no siempre es más rápido y debe elegirse caso por caso.
  2. Uso adecuado de las operaciones intermedias y operaciones terminales.: Las operaciones intermedias se evalúan de forma diferida y solo se evalúan cuando se realiza la operación del terminal.
  3. Preste atención a la reutilización de Stream: Una vez que se consume una secuencia, no se puede volver a usar. Si necesita reutilizarla, puede considerar convertir la secuencia en una colección antes de usarla.
  4. Utilice el recolector adecuadoCollectorsLa clase ofrece una variedad de coleccionistas y usted puede elegir el coleccionista adecuado según sus necesidades específicas.
  5. Manejar excepciones: Al utilizar Stream API, debe manejar posibles excepciones, especialmente en operaciones de archivos y operaciones de bases de datos.

Preguntas frecuentes y soluciones

La transmisión está cerrada

Una vez que se consume una secuencia, no se puede volver a usar. Si necesita reutilizarla, puede considerar convertir la secuencia en una colección antes de usarla.

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

Problemas de desempeño

Las transmisiones paralelas no siempre son más rápidas que las transmisiones en serie y deben probarse caso por caso.puedo usarForkJoinPoolPara optimizar el rendimiento de Stream paralelo.

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

pérdida de memoria

Cuando utilice Stream API para procesar grandes cantidades de datos, debe prestar atención al problema de las pérdidas de memoria.puedo usarclosemétodo para cerrar la transmisión, o usartry-with-resourcesLa declaración cierra automáticamente la secuencia.

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

Resumir

Este artículo presenta en detalle el uso de Java Stream API, incluidas operaciones básicas, operaciones avanzadas, procesamiento paralelo, casos prácticos y mejores prácticas. Al utilizar racionalmente Stream API, los desarrolladores pueden simplificar enormemente el código, mejorar la legibilidad y el mantenimiento del código y también mejorar la eficiencia del procesamiento de datos. Espero que este artículo le resulte útil para utilizar Stream API en el desarrollo de Java.

Java Stream API es una poderosa herramienta para procesar datos recopilados. Mediante el uso flexible de diversas operaciones, se puede lograr un procesamiento de datos y una computación de transmisión eficientes. Si aún no ha utilizado Stream API, se recomienda aprender y dominar esta poderosa herramienta lo antes posible y aplicarla a su proyecto para mejorar la eficiencia del desarrollo y la calidad del código.