Explicación detallada y comparación del flujo Stream y el flujo paralelo ParallelStream

Tabla de contenido

Prefacio

1. ¿Qué es la transmisión?

2. Cómo obtener Stream Stream

3. Métodos comúnmente utilizados en transmisiones Stream.

1. forEach (método transversal/final)

2. filtro

3. mapa (conversión de mapeo)

4. recuento (número de recuento/método de terminación)

5. límite (intercepta los primeros elementos)

6. saltar (saltar los primeros elementos)

7. concat (combina flujos de fusión)

8. distinto (filtro)

9. flatMap (mapear, abrir y luego convertir)

10. Clasificación personalizada: ordenado

11. Detectar coincidencias (método final)

12. Buscar elementos (método final)

13. Encuentra los valores máximo y mínimo (método final)

14. Especificación (método final)

15. Colección (método final)

16. iterar

17. echar un vistazo

4. Introducción y comparación de flujos paralelos paralelostream

1. ParallelStream Introducción a las transmisiones paralelas

2. Comparación

Resumir


Prefacio

Recientemente, al ordenar el código en el trabajo, me encontré con una gran cantidad de flujos, por lo que después de consultar una gran cantidad de información y pensar profundamente, decidí realizar un arreglo sistemático, espero que pueda ser de ayuda para todos.


1. ¿Qué es la transmisión?

Java 8 es una versión muy exitosa. Las nuevas incorporaciones en esta versión Stream, que aparecen junto con la misma versión  Lambda , nos brindan una gran comodidad en la operación de colecciones.

Entonces, ¿qué es Stream ?

Stream trata la colección de elementos que se procesarán como una secuencia. Durante el proceso de transmisión, la API Stream se utiliza para operar en los elementos de la secuencia, como filtrado, clasificación, agregación, etc.

La transmisión se puede crear a partir de una matriz o colección, y existen dos tipos de operaciones en las transmisiones:

  • Las operaciones intermedias devuelven una nueva secuencia cada vez y puede haber varias.
  • Para las operaciones de terminal, cada secuencia solo puede realizar una operación de terminal. Una vez completada la operación de terminal, la secuencia no se puede volver a utilizar. Las operaciones terminales producen una nueva colección o valor.

Además, Stream tiene varias características:

  1. Stream no almacena datos, pero los calcula de acuerdo con reglas específicas y generalmente genera los resultados.
  2. Una secuencia no cambia la fuente de datos; generalmente produce una nueva colección o un valor.
  3. Stream tiene una función de ejecución retrasada y las operaciones intermedias solo se ejecutarán cuando se llame a la operación del terminal.
  4. La transmisión no se puede reutilizar. Llamar a una transmisión que ya ha pasado por operaciones de terminal generará una excepción.

2. Cómo obtener Stream Stream

java.util.stream.Stream es una nueva interfaz de transmisión agregada en Java 8. (Ninguno 函数式接口)
Obtener una transmisión es muy simple, existen varias formas comunes:

  • Todas las colecciones de Colección pueden obtener transmisiones (transmisiones secuenciales) a través del método predeterminado de transmisión;
  • Todas las colecciones de Collection pueden obtener transmisiones paralelas a través de paraleloStream
  • El método estático de la interfaz Stream puede obtener el flujo correspondiente a la matriz.
  • El flujo del método estático de Arrays también puede obtener el flujo

Los métodos específicos son los siguientes:

1. Obtenga la transmisión según la colección.

public static void main(String[] args) {
    
	List<String> list = new ArrayList<>();
	Stream<String> stream1 = list.stream();
    
	Set<String> set = new HashSet<>();
	Stream<String> stream2 = set.stream();
    
	Vector<String> vector = new Vector<>();
	// ...
}    

2. Obtenga la transmisión según el mapa

public static void main(String[] args) {
    
	Map<String, String> map = new HashMap<>();
	
	Stream<String> keyStream = map.keySet().stream();
	Stream<String> valueStream = map.values().stream();
	Stream<Map.Entry<String, String>> entryStream = map.entrySet().stream();
}

3. Obtenga la secuencia según la matriz.
Si está utilizando una matriz en lugar de una colección o mapeo, debido a que es imposible agregar un método predeterminado al objeto de la matriz, la interfaz Stream proporciona un método estático, que es muy simple usar:

public static void main(String[] args) {
    //使用 Stream.of
	String[] array = { "张三", "李四", "王五"};
	Stream<String> stream = Stream.of(array);
    
    //使用Arrays的静态方法
    Arrays.stream(array)
}

3. Métodos comúnmente utilizados en transmisiones Stream.

Los métodos más utilizados en Stream se muestran en la siguiente figura:

Estos métodos se pueden dividir en dos tipos:

Método retrasado : el tipo de valor de retorno sigue siendo un método del propio tipo de la interfaz Stream, por lo que se admiten llamadas encadenadas. (A excepción del método final, todos los demás métodos son métodos retrasados).

Método de terminación : el tipo de valor de retorno ya no es un método del tipo propio de la interfaz Stream, por lo que ya no se admiten llamadas en cadena como StringBuilder. En esta sección, los métodos finales incluyen los métodos count y forEach.

El método detallado se presenta de la siguiente manera:

1.  forEach (método transversal/final)

Este método se utiliza principalmente para el recorrido y puede simplificar el recorrido del bucle. Los parámetros se pasan a una interfaz funcional: Consumidor

public static void main(String[] args) {
	Stream<String> stream = Stream.of("张三", "李四", "王五");
	stream.forEach(name‐> System.out.println(name));
}

2. filtro

Este método se utiliza principalmente para filtrar. Puede convertir una secuencia en otra secuencia de subconjunto a través del método de filtro para realizar el filtrado relacionado requerido.

public static void main(String[] args) {

        //创建一个流
        Stream<String> stream = Stream.of("张三", "李四", "王五", "赵六", "田七");

        //对流中元素过滤,过滤出姓张的人
        Stream<String> stream2 = stream.filter(name -> {
            return name.startsWith("张");
        });
        
        //遍历过滤后的流
        stream2.forEach(name -> System.out.println(name));


    }

3. mapa (conversión de mapeo)

Si se requiere conversión de mapeo, puede usar el método de mapa para mapear elementos de una secuencia a otra secuencia. Esta interfaz requiere un parámetro de interfaz funcional de función. El único método abstracto en la interfaz funcional java.util.stream.Function es

R apply(T t);

Esto puede convertir un tipo T en un tipo R, y esta acción de conversión se denomina "mapeo". La forma de utilizar el mapa es la siguiente:

    /**
     * stream流的map方法
     * map方法可以将流中的元素映射到另一个流中
     * map方法的参数是一个Function函数式接口
     */
    public void mapTest(){

        //创建一个流,里面是字符串类型的整数
        Stream<String> stream1 = Stream.of("21", "32", "25", "57", "42");
        
        //把stream1流中的整数全部转成int类型
        Stream<Integer> stream2 = stream1.map((String s) -> {
            return Integer.parseInt(s);
        });

        //遍历
        stream2.forEach((i)-> System.out.println(i));


    }

4. recuento (número de recuento/método de terminación)

Similar al método de tamaño en la Colección, la secuencia Stream proporciona el método de conteo para contar el número de elementos que contiene. Este método devuelve un valor largo que representa el número de elementos (ya no es un valor int como una colección). El uso básico es el siguiente:

public class Demo09StreamCount {
    
	public static void main(String[] args) {
        
		Stream<String> original = Stream.of("张三", "李四", "王五",“张三丰”);
        
        //筛选姓张的人
		Stream<String> result = original.filter(s ‐> s.startsWith("张"));
        
        //输出个数
		System.out.println(result.count()); // 输出:2
	}
}

5. límite (intercepta los primeros elementos)

El método de límite se utiliza principalmente para interceptar la secuencia y solo utilizar la primera n. El parámetro es de tipo largo. Si la longitud actual de la colección es mayor que el parámetro, será interceptado; de lo contrario, no se realizará ninguna operación. El uso básico es el siguiente:

public class Demo10StreamLimit {
    
	public static void main(String[] args) {
		Stream<String> original = Stream.of("张三", "李四", "王五");
        
        //截取前两个元素
		Stream<String> result = original.limit(2);
        
		System.out.println(result.count()); // 2
	}
}

6. saltar (saltar los primeros elementos)

El método de omisión se utiliza principalmente para omitir los primeros elementos y obtener una nueva secuencia después de la intercepción. Si la longitud actual de la secuencia es mayor que n, omita la primera n; de lo contrario, obtendrá una secuencia vacía de longitud 0. El uso básico es el siguiente:

public class Demo11StreamSkip {
    
	public static void main(String[] args) {
        
		Stream<String> original = Stream.of("张三", "李四", "王五");
        
        //跳过前两个,返回一个新的流
		Stream<String> result = original.skip(2);
		
        System.out.println(result.count()); // 1
	}
}

7. concat (combina flujos de fusión)

Si hay dos transmisiones y desea fusionarlas en una sola, puede usar el método estático concat de la interfaz Stream:

public class Demo12StreamConcat {
    
	public static void main(String[] args) {
		Stream<String> streamA = Stream.of("张三");
		Stream<String> streamB = Stream.of("李四");

        //合并成一个新的流
		Stream<String> result = Stream.concat(streamA, streamB);
	}
}

8. distinto (filtro)

Eliminar elementos duplicados de la secuencia (use código hash y métodos iguales para comparar)

public class Demo11StreamSkip {
    
	public static void main(String[] args) {
        
		Stream<String> original = Stream.of("张三", "张三", "李四");

        //筛选重复元素
		Stream<String> result = original.distinct();
		System.out.println(result.count()); // 2
	}
}

9. flatMap (mapear, abrir y luego convertir)

FlatMap tiene una función similar al mapa. Pasa una interfaz funcional de función internamente. La diferencia con el mapa es que abrirá los elementos en la secuencia y los combinará en una nueva secuencia.

// map和flatMap的练习
public class StreamDemo {

    @Test
    public void test(){
        List<String> list = Arrays.asList("aa","bb","cc","dd");

        // 1 map输出全为大写
        list.stream().map(s -> s.toUpperCase()).forEach(System.out::println);

        // 2.map流里还有其他流,需要两个遍历才可以得到里面内容
        Stream<Stream<Character>> streamStream = list.stream().map(StreamDemo::fromStringToStream);
        streamStream.forEach(s -> {
            s.forEach(System.out::println);
        });

        // 3.flatMap流里还有其他流,使用flatMap可以直接把里面的流打开,一次遍历即可
        Stream<Character> characterStream = list.stream().flatMap(StreamDemo::fromStringToStream);
        characterStream.forEach(System.out::println);
        
    }

    /**
     *  将字符串中的多个字符构成的集合转换为对应的stream
     * @param str
     * @return
     */
    public static Stream<Character> fromStringToStream(String str){
        ArrayList<Character> list = new ArrayList();

        // 将字符串转成字符数组,并遍历加入list集合
        for(Character c : str.toCharArray()){
            list.add(c);
        }

        // 返回list集合的stream流
        return list.stream();
    }
}

10. Clasificación personalizada: ordenado

Este método se utiliza principalmente para ordenar y puede utilizar la interfaz Comparator para una clasificación personalizada. El uso específico es el siguiente:

    public void test2(){

        List<Integer> integers = List.of(124, 2, 15, 12, 51, -5, 5);
        
        // 按照自然排序
        integers.stream().sorted().forEach(System.out::println);

        List<Integer> integers2 = List.of(124, 2, 15, 12, 51, -5, 5);
        // 定制排序(大到小),需要传入Comparator接口(如果流中的是引用类型,只能用定制排序)
        
        // 简写:integers2.stream().sorted((e1,e2) -> e2-e1).forEach(System.out::println);
        integers2.stream().sorted((e1,e2) -> {
            return e2-e1;
        }).forEach(System.out::println);
    }

11. Detectar coincidencias (método final)

Este tipo de método devolverá un valor booleano, el cual se clasifica de la siguiente manera:

- Si todos los partidos: allMatch

- Si coincide con al menos uno: anyMatch

- Si no hay coincidencia: noneMatch


    public void test3(){
        List<Integer> integers = List.of(124, 2, 15, 12, 51, -5, 5);
        // 判断是否全部大于5
        boolean b = integers.stream().allMatch(i -> i > 5);
        // 结束输出false
        System.out.println(b);


        List<Integer> integers2 = List.of(124, 2, 15, 12, 51, -5, 5);
        // 检测是否匹配至少一个元素
        boolean b1 = integers2.stream().anyMatch(i -> i > 5);
        // 输出true
        System.out.println(b1);



        List<Integer> integers3 = List.of(124, 2, 15, 12, 51, -5, 5);
        // 检查是否没有匹配的元素
        boolean b2 = integers3.stream().noneMatch(i -> i > 1000);
        // 输出true,全部不匹配
        System.out.println(b2);
    }

12. Buscar elementos (método final)

Encuentra el primer elemento: findFirst, devuelve tipo opcional

Encuentre uno de los elementos: findAny, devuelva tipo opcional

public void test4(){
        List<Integer> integers = List.of(124, 2, 15, 12, 51, -5, 5);
        
        // 输出第一个元素
        Optional<Integer> first = integers.stream().findFirst();
        
        // 输出结果是Optional[124]
        System.out.println(first);

        
        List<Integer> integers2 = List.of(124, 2, 15, 12, 51, -5, 5);
        
        // 返回其中一个元素
        Optional<Integer> any = integers2.stream().findAny();
        System.out.println(any);
    }

13. Encuentra los valores máximo y mínimo (método final)

máx(comparador c)

mín(comparador c)


    public void test5(){
        List<Person> list = new ArrayList<>();
        list.add(new Person("张三",25,3000));
        list.add(new Person("李四",27,2545));
        list.add(new Person("王五",35,4515));
        list.add(new Person("赵六",55,9877));

        //查找年龄最大的人
        Optional<Person> max = list.stream().max((e1, e2) -> e1.getAge() - e2.getAge());
        //返回赵六,55岁年龄最大
        System.out.println(max.get());

      }

14. Especificación (método final)

reducir (identidad T, BinaryOperator) El primer parámetro es el valor inicial y el segundo parámetro es una interfaz funcional.

El parámetro reduce(BinaryOperator) es una interfaz funcional


    public void test(){
        List<Integer> integers = List.of(1, 2, 3, 4, 5, 6, 7, 8, 9, 10);
        
        // 求集合里数字的和(归约)reduce第一个参数是初始值。
        Integer sum = integers.stream().reduce(0, Integer::sum);
        System.out.println(sum);
      

        List<Person> list = new ArrayList<>();
        list.add(new Person("张三",25,3000));
        list.add(new Person("李四",27,2545));
        list.add(new Person("王五",35,4515));
        list.add(new Person("赵六",55,9877));
        
        // 求所有人的工资和(归约)
        // 不用方法引用写法:Optional<Integer> reduce = list.stream().map(person -> person.getSalary()).reduce((e1, e2) -> e1 + e2);
        Optional<Integer> reduce = list.stream().map(Person::getSalary).reduce(Integer::sum);
        
        // 输出Optional[19937]
        System.out.println(reduce);

    }

15. Colección (método final)

recopilar (Collector c): convierte la transmisión a otras formas y recibe una implementación de la interfaz de Collector


    public void test(){
        List<Person> list = new ArrayList<>();
        list.add(new Person("张三",25,3000));
        list.add(new Person("李四",27,2545));
        list.add(new Person("王五",35,4515));
        list.add(new Person("赵六",55,9877));

        // 把年龄大于30岁的人,转成一个list集合
        List<Person> collect = list.stream().filter(person -> person.getAge() > 30).collect(Collectors.toList());

        // 遍历输出(输出王五和赵六)
        for (Person person : collect) {
            System.out.println(person);
        }


        List<Person> list2 = new ArrayList<>();
        list.add(new Person("张三",25,3000));
        list.add(new Person("李四",27,2545));
        list.add(new Person("王五",35,4515));
        list.add(new Person("张三丰",55,9877));

        // 把姓张的人,转成Set集合
        Set<Person> set = list2.stream().filter(person -> person.getName().startsWith("张")).collect(Collectors.toSet());

        // 输出张三和张三丰
        set.forEach(System.out::println);

    }

16. iterar

Puede utilizar Stream.iterate para crear valores de flujo, el llamado flujo infinito.

//Stream.iterate(initial value, next value)
	Stream.iterate(0, n -> n + 1)
                .limit(5)
                .forEach(x -> System.out.println(x));

//输出0 1 2 3 4

17. echar un vistazo

Lo que recibe peek es una función de consumidor. La operación de peek consumirá cada elemento en la secuencia de acuerdo con la lógica proporcionada por la función de consumidor y también puede cambiar algunos atributos dentro del elemento.


    public void test(){
        List<String> collect = Stream.of("one", "two", "three", "four")
                .filter(e -> e.length() > 3)
                .peek(e -> System.out.println("查看刚过滤出的值:" + e))
                .map(String::toUpperCase)
                .peek(e -> System.out.println("查看转大写之后的值:" + e))
                .collect(Collectors.toList());


        // 遍历过滤后的集合
        for (String s : collect) {
            System.out.println(s);
        }
    }

El resultado es el siguiente:

查看刚过滤出的值:three
查看转大写之后的值:THREE
查看刚过滤出的值:four
查看转大写之后的值:FOUR
----------
THREE
FOUR

4. Introducción y comparación de flujos paralelos paralelostream

1. ParallelStream Introducción a las transmisiones paralelas

Este artículo solo proporciona una breve introducción y comparación, y los artículos posteriores proporcionarán una introducción y un análisis detallados de los flujos paralelos de paraleloStream.

Java 8 proporciona una clase Stream que puede procesar datos de recopilación de manera más conveniente. El método paraleloStream () puede aprovechar al máximo las ventajas de las CPU de múltiples núcleos y utilizar subprocesos múltiples para acelerar el procesamiento de datos de recopilación.

El código fuente del método paraleloStream() es el siguiente:

    /**
     * @return a possibly parallel {@code Stream} over the elements in this
     * collection
     * @since 1.8
     */
    default Stream<E> parallelStream() {
        return StreamSupport.stream(spliterator(), true);
    }

2. Comparación

Stream es un flujo secuencial y el subproceso principal realiza operaciones en el flujo en orden, mientras que paraleloStream es un flujo paralelo y el flujo es operado internamente por múltiples subprocesos en ejecución paralela, pero la premisa es que el procesamiento de datos en el flujo tiene sin requisitos secuenciales. Por ejemplo, al filtrar números impares en una colección, los dos procesos son diferentes:

Si la cantidad de datos en el flujo es lo suficientemente grande, los flujos paralelos pueden acelerar el procesamiento.

Además de crear secuencias paralelas directamente, también puede parallel()convertir secuencias secuenciales en secuencias paralelas:

Optional<Integer> findFirst = list.stream().parallel().filter(x->x>6).findFirst();

Resumir

Este artículo presenta en detalle los conceptos básicos y el uso de Stream y, finalmente, presenta brevemente paraleloStream y su comparación con Stream. A través del contenido anterior, básicamente hemos resuelto los conceptos y métodos comunes que se pueden encontrar en el trabajo. Espero que este artículo pueda ser útil para los lectores.

Supongo que te gusta

Origin blog.csdn.net/Clearlove_S7/article/details/129971803
Recomendado
Clasificación