Lo que necesita saber sobre el uso de flujo de desarrollo rápido de Java (1) Stream realiza consultas, estadísticas y otras operaciones en la colección List

 Introducción a la corriente

La API de Java 8 agrega una nueva abstracción llamada Stream que le permite manipular datos de forma declarativa. Stream proporciona una abstracción de alto nivel para las operaciones y representaciones de recopilación de Java de una manera intuitiva similar a la consulta de datos de una base de datos con sentencias SQL.
Stream API puede mejorar en gran medida la productividad de los programadores de Java, permitiéndoles escribir código eficiente, limpio y conciso.
Este estilo se refiere a la colección de elementos que se procesarán como un flujo, que se transmite en la canalización y se puede procesar en los nodos de la canalización, como filtrado, clasificación, agregación, etc.
El flujo de elementos es procesado por la operación intermedia en la tubería, y finalmente el resultado del procesamiento anterior es obtenido por la operación terminal.


 ¿Qué es la corriente?

Stream (flujo) es una cola de elementos de una fuente de datos y admite operaciones de agregación

  • Los elementos son objetos de un tipo específico que forman una cola. Stream en Java no almacena elementos, sino que los calcula a pedido.
  • El origen de la  secuencia de origen . Puede ser una colección, una matriz, un canal de E/S, un generador, etc.
  • Las operaciones de agregación  son similares a las instrucciones SQL, como filtrar, asignar, reducir, buscar, unir, ordenar, etc.

A diferencia de las operaciones de colección anteriores, las operaciones de transmisión también tienen dos características básicas:

  • Canalización : las operaciones intermedias devolverán el objeto de flujo en sí. De esta manera, varias operaciones se pueden concatenar en una canalización, al igual que el estilo fluido. Hacerlo permite optimizaciones como la pereza y los cortocircuitos.
  • Iteración interna : en el pasado, el recorrido de la colección se realizaba a través de Iterator o For-Each, y se iteraba fuera de la colección de forma explícita, lo que se denomina iteración externa. Stream proporciona un método de iteración interno, implementado a través del patrón de visitante (Visitor).

¿Por qué necesitas corriente? 

Como aspecto destacado de Java 8, Stream es un concepto completamente diferente de InputStream y OutputStream en el paquete java.io. También es diferente del Stream de StAX para el análisis de XML, ni es el Stream para el procesamiento en tiempo real de big data de Amazon Kinesis. Stream en Java 8 es una mejora de la función de objeto de colección (Colección), que se centra en varias operaciones de agregación (operación agregada) u operaciones de datos masivos (operación de datos masivos) muy convenientes y eficientes en objetos de colección. La API Stream mejora en gran medida la eficiencia de la programación y la legibilidad del programa por medio de la misma expresión Lambda emergente. Al mismo tiempo, proporciona modos en serie y en paralelo para las operaciones de agregación. El modo concurrente puede aprovechar al máximo las ventajas de los procesadores multinúcleo y utilizar el modo paralelo fork/join para dividir tareas y acelerar el proceso de procesamiento. Por lo general, es difícil y propenso a errores escribir código paralelo, pero el uso de Stream API puede escribir fácilmente programas simultáneos de alto rendimiento sin escribir una sola línea de código de subprocesos múltiples. Por lo tanto, java.util.stream, que apareció por primera vez en Java 8, es producto de la influencia integral de una era de lenguaje funcional + multinúcleo.

¿Qué es una operación de agregación?

En las aplicaciones J2EE tradicionales, el código Java a menudo tiene que depender de operaciones de agregación de bases de datos relacionales para lograr cosas como:

  • Gasto medio de los clientes al mes
  • Artículos más caros en venta
  • Pedidos válidos completados esta semana (excepto los no válidos)
  • Tome diez muestras de datos como recomendaciones de la página de inicio

Tales operaciones.

Sin embargo, en la era actual de explosión de datos, cuando las fuentes de datos están diversificadas y los datos son masivos, a menudo es necesario abandonar el RDBMS o realizar estadísticas de datos de nivel superior basadas en los datos devueltos desde la capa inferior. En la API de colección de Java, solo hay una pequeña cantidad de métodos auxiliares y, con mayor frecuencia, los programadores necesitan usar Iterator para atravesar la colección y completar la lógica de la aplicación de agregación relacionada. Este es un enfoque torpe y lejos de ser eficiente. En Java 7, si desea encontrar todas las transacciones cuyo tipo es comestibles, devuelva un conjunto de ID de transacción ordenados en orden descendente de valores de transacción

 generar flujo

En Java 8, la interfaz de recopilación tiene dos métodos para generar flujos:

  • stream()  : crea un flujo en serie para una colección.

  • paraleloStream()  : crea un flujo paralelo para una colección.

List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
List<String> filtered = strings.stream().filter(string -> !string.isEmpty()).collect(Collectors.toList());

Métodos en la interfaz de Collector:

1 List toList() Recopilar elementos en el flujo a List
Ejemplo: List<Employee> emps= list.stream().collect(Collectors.toList());

2 Set toSet() Recopile elementos en la secuencia para establecer
Ejemplo: Set<Employee> emps= list.stream().collect(Collectors.toSet());

3 Collection toCollection() Recopilar los elementos en el flujo a la colección creada
Ejemplo: Collection<Employee>emps=list.stream().collect(Collectors.toCollection(ArrayList::new));

4 Conteo largo() Cuenta el número de elementos en el flujo
Ejemplo: conteo largo = list.stream().collect(Collectors.counting());

5 Integer summingInt() Suma los atributos enteros de los elementos en la secuencia
Ejemplo: int total=list.stream().collect(Collectors.summingInt(Employee::getAge));

6 Double averagingInt() Calcula el valor promedio del atributo Integer de los elementos en el flujo
Ejemplo: double avg = list.stream().collect(Collectors.averagingInt(Employee::getAge));

7 IntSummaryStatistics summarizingInt() Recopila los valores estadísticos de los atributos Integer en la secuencia. Tales como: valor promedio
Ejemplo: int SummaryStatisticsiss= list.stream().collect(Collectors.summarizingInt(Employee::getAge));

String join() String une cada cadena en el flujo
Ejemplo: String str= list.stream().map(Employee::getName).collect(Collectors.joining());
Ejemplo: String str= list.stream() . map(Employee::getName).collect(Collectors.joining(",", "{", "}")));
Ejemplo: String str= list.stream().map(Employee::getName).collect ( Collectors.joining(","));
9: Optional maxBy() Seleccione el valor máximo según el comparador
Ejemplo: Optional<Emp>max= list.stream().collect(Collectors.maxBy(Comparator.comparingDouble(Employee: : getSalario)));

10:Opcional minBy() Seleccione el valor mínimo según el comparador
Ejemplo: Opcional<Emp> min = list.stream().collect(Collectors.minBy(Comparator.comparingDouble(Employee::getSalary)));

11:reducing() comienza desde un valor inicial como un acumulador y usa BinaryOperator para combinar elementos en el flujo uno por uno para reducir a un solo valor Ejemplo:
int total=list.stream().collect(Collectors.reducing(0 , Empleado ::getAge, Integer::sum));

12: El tipo devuelto por la función de conversión collectAndThen() envuelve otro recopilador.
Ejemplo de la función de conversión de resultados: int how= list.stream().collect(Collectors.collectorsAndThen(Collectors.toList(), List::size)) ;

13:Map<K, List> groupingBy() Agrupe flujos de acuerdo con un cierto valor de atributo, el atributo es K y el resultado es V Ejemplo
: Map<String, List<Employee>> map= list.stream().collect (Coleccionistas.groupingBy(Empleado::getName));

14:Map<Boolean, List> particióningBy() Partición según verdadero o falso después de la recopilación
Ejemplo: empleados.stream().collect(Collectors.partitioningBy(empleado -> empleado.getSalary()>8000, Collectors.mapping(Employee: :getName,Collectors.counting()))).forEach((Clave, valor)-> System.out.println(Clave+"----"+valor));

15: El método de mapeo () aplicará el resultado a otro colector.
Ejemplo: empleados.stream().collect(Collectors.partitioningBy(empleado -> empleado.getSalary()>8000, Collectors.mapping(Employee::getName,Collectors.toList()))).forEach((Clave, valor) -> System.out.println(Clave+"----"+valor));

16: flatMapping () – similar al método Collectors.mapping (), pero con una granularidad más fina. Ambos toman una función y un parámetro de colector para recolectar elementos, pero la función flatMapping recibe el flujo de elementos y luego realiza operaciones de acumulación a través del colector, que generalmente se usa para procesar flujos anidados en recolectar ()

17:filtering(): similar al método Stream filter(), que se usa para filtrar elementos de entrada, a menudo se usa junto con groupingBy/partitioningBy

18:toUnmodifiableMap(): reúne elementos en un mapa no modificable. Las direcciones de los objetos en el mapa no se pueden modificar. Si los objetos en él admiten la modificación, en realidad se pueden modificar.

19:toUnmodifiableSet(): reúne elementos en un HashSet no modificable

20:toMap()—reúne elementos en un mapa

21:toConcurrentMap(): reúne elementos en un concurrentHashMap que admita la concurrencia

22:toUnmodifiableList()—reúne elementos en una ArrayList no modificable

23: groupingByConcurrent(): similar a groupingBy(), pero su colección agregada es concurrentHashMap admite la simultaneidad, lo que puede mejorar la eficiencia de la agrupación de secuencias paralelas

24:teing(): devuelve un colector que consta de dos colectores aguas abajo. Cada elemento pasado al recopilador resultante es procesado por recopiladores posteriores, cuyos resultados luego se fusionan en el resultado final mediante la función de fusión especificada.
Admite la recopilación de flujos utilizando dos colectores separados y luego fusionando los resultados utilizando la bifunción proporcionada.
 

 ejemplo completo

1. Crear clases de entidad y datos

Usuario.java

import java.math.BigDecimal;
 
/**
 * 用户信息实体类 by 青冘
 **/
public class User
{
    private int id; //用户ID
    private String name; //用户名称
    private String sex; //性别
    private int age; //年龄
    private String department; //部门
    private BigDecimal salary; //薪资
 
    //构造方法
    public User(int id,String name,String sex,int age,String department,BigDecimal salary)
    {
        this.id = id;
        this.name = name;
        this.sex = sex;
        this.age = age;
        this.department = department;
        this.salary = salary;
    }
    public int getId()
    {
        return id;
    }
    public void setId(int id)
    {
        this.id = id;
    }
    public String getName()
    {
        return name;
    } 
    public void setName(String name)
    {
        this.name = name;
    } 
    public String getSex()
    {
        return sex;
    } 
    public void setSex(String sex)
    {
        this.sex = sex;
    } 
    public int getAge()
    {
        return age;
    } 
    public void setAge(int age)
    {
        this.age = age;
    } 
    public String getDepartment()
    {
        return department;
    } 
    public void setDepartment(String department)
    {
        this.department = department;
    } 
    public BigDecimal getSalary()
    {
        return salary;
    } 
    public void setSalary(BigDecimal salary)
    {
        this.salary = salary;
    } 
    @Override
    public String toString()
    {
        return "ID:" + this.id + " 名称:" + this.name +  " 性别:" + this.sex
                + " 年龄:" + this.age + " 部门:" + this.department + " 薪资:" + this.salary + "元";
    }
}

UserService.clase

import com.pjb.streamdemo.entity.User;
import java.math.BigDecimal;
import java.util.ArrayList;
import java.util.List;
 
/**
 * 用户信息业务逻辑类 by 青冘
 **/
public class UserService
{
    /**
     * 获取用户列表
     */
    public static List<User> getUserList()
    {
        List<User> userList = new ArrayList<User>();
        userList.add(new User(1, "青冘的博客_01", "男", 32, "研发部", BigDecimal.valueOf(1600)));
        userList.add(new User(2, "青冘的博客_02", "男", 30, "财务部", BigDecimal.valueOf(1800)));
        userList.add(new User(3, "青冘的博客_03", "女", 20, "人事部", BigDecimal.valueOf(1700)));
        userList.add(new User(4, "青冘的博客_04", "男", 38, "研发部", BigDecimal.valueOf(1500)));
        userList.add(new User(5, "青冘的博客_05", "女", 25, "财务部", BigDecimal.valueOf(1200)));
        return userList;
    }
}

2. Método de consulta

2.1 paraCada()

Use forEach() para iterar a través de los datos de la lista.

/**
 * 使用forEach()遍历列表信息 by 青冘
 */
@Test
public void forEachTest()
{
    //获取用户列表
    List<User> userList = UserService.getUserList();
 
    //遍历用户列表
    userList.forEach(System.out::println);
}

La declaración transversal anterior es equivalente a la siguiente declaración:

userList.forEach(user -> {System.out.println(user);});

Resultados de la:

2.2 filtro (T -> booleano)

Use filter() para filtrar datos de lista

Obtenga la lista de usuarios cuyo departamento es "Departamento de I+D".

/**
 * 使用filter()过滤列表信息 by 青冘
 */
@Test
public void filterTest()
{
    //获取用户列表
    List<User> userList = UserService.getUserList();
 
    //获取部门为“研发部”的用户列表
    userList = userList.stream().filter(user -> user.getDepartment() == "研发部").collect(Collectors.toList());
 
    //遍历用户列表
    userList.forEach(System.out::println);
}

Resultados de la:

2.3 encontrarCualquiera() y encontrarPrimero()

Utilice findAny() y findFirst() para obtener el primer dato.

Obtenga la información del usuario cuyo nombre de usuario es "Blog_02 de Qingli" y devuelva un valor nulo si no se encuentra.

/**
 * 使用findAny()获取第一条数据 by 青冘
 */ 
@Test
public void findAnytTest()
{
        //获取用户列表
	    List<User> userList = UserService.getUserList();
	 
	    //获取用户名称为“青冘的博客_02”的用户信息,如果没有找到则返回null
	    User user = userList.stream().filter(u -> u.getName().equals("青冘的博客_02")).findAny().orElse(null);
	 
	    //打印用户信息
	    System.out.println(user);
}

Resultados de la: 

Nota: Tanto findFirst() como findAny() obtienen el primer elemento de datos de la lista, pero findAny() opera y los elementos devueltos son inciertos. Llamar a findAny() varias veces para la misma lista puede devolver un valor diferente. Use findAny() para un rendimiento más eficiente. Si hay menos datos, en el caso de serial, generalmente se devolverá el primer resultado, si es en el caso de paralelo (parallelStream), no se puede garantizar que sea el primer resultado.
 

2.4mapa (T -> R) y mapa plano (T -> Corriente)

Use map() para mapear cada elemento T en la secuencia a R (similar a la conversión de tipo).

Use flatMap() para mapear cada elemento T en la secuencia en una secuencia y luego conecte cada secuencia en una secuencia.

Utilice el método map() para obtener la columna de nombre en la lista de usuarios.

/**
 * 使用map()获取列元素 by 青冘
 */
@Test
public void mapTest()
{
    //获取用户列表
    List<User> userList = UserService.getUserList();
 
    //获取用户名称列表
    List<String> nameList = userList.stream().map(User::getName).collect(Collectors.toList());
    //或者:List<String> nameList = userList.stream().map(user -> user.getName()).collect(Collectors.toList());
 
    //遍历名称列表
    nameList.forEach(System.out::println);
}

El resultado devuelto es un tipo de matriz, escrito de la siguiente manera:

//数组类型
String[] nameArray = userList.stream().map(User::getName).collect(Collectors.toList()).toArray(new String[userList.size()]);

Resultados de la:

Utilice flatMap() para concatenar cada elemento de la secuencia en una secuencia.

/**
 * 使用flatMap()将流中的每一个元素连接成为一个流 by 青冘
 */
@Test
public void flatMapTest()
{
    //创建城市
    List<String> cityList = new ArrayList<String>();
    cityList.add("北京;上海;深圳;");
    cityList.add("广州;武汉;杭州;");
 
    //分隔城市列表,使用 flatMap() 将流中的每一个元素连接成为一个流。
    cityList = cityList.stream()
            .map(city -> city.split(";"))
            .flatMap(Arrays::stream)
            .collect(Collectors.toList());
 
    //遍历城市列表
    cityList.forEach(System.out::println);
}

Resultados de la:

2.5 distinto()

Utilice el método distinto() para eliminar datos duplicados.

Obtenga una lista de departamentos y elimine datos duplicados.

/**
 * 使用distinct()去除重复数据 by 青冘
 */
@Test
public void distinctTest()
{
    //获取用户列表
    List<User> userList = UserService.getUserList();
 
    //获取部门列表,并去除重复数据
    List<String> departmentList = userList.stream().map(User::getDepartment).distinct().collect(Collectors.toList());
 
    //遍历部门列表
    departmentList.forEach(System.out::println);
}

Resultados de la:

2.6 límite (n larga) 和 skip (n larga)

El método limit(long n) se usa para devolver los primeros n datos, y el método skip(long n) se usa para omitir los primeros n datos.

/**
 * limit(long n)方法用于返回前n条数据
 * skip(long n)方法用于跳过前n条数据 by 青冘
 */
@Test
public void limitAndSkipTest()
{
    //获取用户列表
    List<User> userList = UserService.getUserList();
 
    //获取用户列表,要求跳过第1条数据后的前3条数据
    userList = userList.stream()
            .skip(1)
            .limit(3)
            .collect(Collectors.toList());
 
    //遍历用户列表
    userList.forEach(System.out::println);
}

Resultados de la:

3. Métodos estadísticos

3.1 reducir((T, T) -> T) 和 reducir(T, (T, T) -> T)

Use reduce((T, T) -> T) y reduce(T, (T, T) -> T) para combinar elementos en una secuencia, como sumar, producir, maximizar, etc.

  @Test
    public void testReduce() {
       	    Stream<Integer> stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7, 8});
	         //求集合元素之和
		    System.out.println("求集合元素之和");
	        Integer result = stream.reduce(0, Integer::sum);
	        	        System.out.println(result);
	        stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
	        //求和
	        System.out.println("求和");
	        stream.reduce((i, j) -> i + j).ifPresent(System.out::println);
	        stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
	        //求最大值
	        System.out.println("求最大值");
	        stream.reduce(Integer::max).ifPresent(System.out::println);
	        stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
	        //求最小值
	        System.out.println("求最小值");
	        stream.reduce(Integer::min).ifPresent(System.out::println);
	        stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
	        //做逻辑
	        System.out.println("做逻辑");
	        stream.reduce((i, j) -> i > j ? j : i).ifPresent(System.out::println);
	        stream = Arrays.stream(new Integer[]{1, 2, 3, 4, 5, 6, 7});
	        //求逻辑求乘机
	        System.out.println("求逻辑求乘机");
	        int result2 = stream.filter(i -> i % 2 == 0).reduce(1, (i, j) -> i * j);

	        Optional.of(result2).ifPresent(System.out::println);
    }

Resultados de la:

3.2 mapToInt(T -> int) 、mapToDouble(T -> doble) 、mapToLong(T -> largo) 

int sumVal = userList.stream().map(User::getAge).reduce(0,Integer::sum); el método de cálculo de la suma de elementos implica el costo del boxeo, y el método map(User::getAge) cambia después de que se convierte en un tipo Stream, y cada Integer debe desempaquetarse en un tipo primitivo y luego sumarse mediante el método sum, lo que afecta en gran medida la eficiencia. Para resolver este problema, Java 8 introduce concienzudamente flujos numéricos IntStream, DoubleStream y LongStream Los elementos en este flujo son todos tipos de datos primitivos, a saber, int, double y long. 

La secuencia se convierte en una secuencia numérica:

  • mapToInt(T -> int) : devuelve IntStream
  • mapToDouble(T -> doble) : devuelve DoubleStream
  • mapToLong (T -> largo) : devuelve LongStream
 //用户列表中年龄的最大值、最小值、总和、平均值
    int maxVal = userList.stream().mapToInt(User::getAge).max().getAsInt();
    int minVal = userList.stream().mapToInt(User::getAge).min().getAsInt();
    int sumVal = userList.stream().mapToInt(User::getAge).sum();
    double aveVal =  userList.stream().mapToInt(User::getAge).average().getAsDouble(); 

3.3 contar() y contar()

Los datos de la lista se pueden contar usando counting() y count().

 //统计研发部的人数,使用 counting()方法进行统计
    Long departCount = userList.stream().filter(user -> user.getDepartment() == "研发部").collect(Collectors.counting());
 
    //统计30岁以上的人数,使用 count()方法进行统计(推荐)
    Long ageCount = userList.stream().filter(user -> user.getAge() >= 30).count();

3.4 summingInt(), summingLong(), summingDouble()

Para calcular la suma, toma un argumento de función.

//计算年龄总和
int sumAge = userList.stream().collect(Collectors.summingInt(User::getAge));

3.5 promediandoInt(),promediandoLargo(),promediandoDoble()

utilizado para calcular el promedio

//计算平均年龄
double aveAge = userList.stream().collect(Collectors.averagingDouble(User::getAge));

Supongo que te gusta

Origin blog.csdn.net/qq_17798399/article/details/125743944
Recomendado
Clasificación