notas de estudio de Java 8 Stream

Corriente escriba la ruta completa es: java.util.stream.Stream

Como un importante punto culminante de Java 8, que java.io paquete de InputStream y OutputStream son conceptos completamente diferentes. También es diferente para StAX análisis XML corriente, corriente Amazon Kinesis no es en tiempo real tratamiento de datos de gran tamaño. Corriente 8 Java se ha mejorado en la colección (Collection) la función objetivo, se centra en la colección de varios objetos es operación muy conveniente y eficiente polimerización (operación agregada), o las operaciones de datos en masa (operación de datos a granel). Lambda expresiones Corriente del API a través de la misma emergentes, lo que mejora en gran medida la eficiencia de la programación y la legibilidad del programa. Al mismo tiempo que se ofrece de serie y paralelos modos para ser agregada operación, modelo de concurrencia puede tomar ventaja de los procesadores multi-núcleo, utilizando tenedor / unirse y tarea dividida en paralelo para acelerar el proceso. Por lo general, escribir código paralelo es difícil y propenso a errores, pero utilizando la API corriente sin escribir una sola línea de código multiproceso, puede escribir fácilmente de alto rendimiento programas concurrentes. Así, java.util.stream en Java 8 por primera vez es un producto de los efectos combinados de un lenguaje funcional + época de múltiples núcleos.

Stream no es una colección de elementos, no es una estructura de datos de los datos no se guarda, se trata de algoritmos y cálculos, es más como una versión avanzada del iterador. La versión original del iterador, un usuario puede explícitamente a través de los elementos y realizar una cierta operación; avanzada versiones Stream, siempre que las necesidades de los usuarios para realizar cualquier operación dada de sus elementos contenidos, tales como "filtro cabo longitud mayor que 10 cadena "" Obtener la primera letra de cada cadena "e, implícitamente corriente atravesado internamente convertir los datos en consecuencia.

Stream como un iterador (el iterador), unidireccional, no alternativo, los datos sólo pueden ser atravesados ​​una vez atravesado una vez después de agotada, como el agua fluye desde la parte delantera, se ha ido.

Y mientras que el iterador es diferente y, en funcionamiento corriente paralela, sólo iteradores imperativo, la operación de serialización. Como su nombre sugiere, cuando un modo en serie a través, y luego leer cada artículo para leer el siguiente artículo. Cuando se utiliza para atravesar los datos en paralelo se divide en una pluralidad de segmentos, cada uno de los cuales se procesa en un hilo diferente, y luego da salida a los resultados juntos. paralelismo depende tenedor corriente introducida en Java7 / Join marco (JSR166y) para acelerar la tarea de proceso y de división. Paralela evolución de Java API es básicamente la siguiente manera:

  1. 1,0-1,4 en java.lang.Thread
  2. 5,0 java.util.concurrent
  3. 6.0 Phasers, etc.
  4. El Tenedor 7.0 / marco de Ingreso
  5. 8,0 Lambda

Stream es otra característica importante es la fuente de datos en sí mismo puede ser infinito (?)

configuración de secuencias

Cuando se utiliza una corriente del tiempo, por lo general consta de tres pasos básicos:

Obtiene una fuente de datos (fuente) → → realizar la operación de conversión de datos para obtener los resultados deseados, cada conversión no cambia el objeto Stream original y devuelve un nuevo objeto Stream (no puede haber múltiples conversiones), lo que puede permitir su operación misma que la disposición de cadena, en un conducto, como se muestra en la figura.

1. La Fig conducto de flujo de configuración (Corriente Pipeline)

Hay muchas maneras de generar corriente Fuente:

  • De la colección y matrices
    • Collection.stream ()
    • Collection.parallelStream ()
    • Arrays.stream (T array) o Stream.of ()

    从 BufferedReader

    • java.io.BufferedReader.lines ()
  • fábrica estática
  • java.util.stream.IntStream.range ()
  • java.nio.file.Files.walk ()
  • Edifícate
    • java.util.Spliterator

    otro

    • Random.ints ()
    • BitSet.stream ()
    • Pattern.splitAsStream (java.lang.CharSequence)
    • JarFile.stream ()

Tipo de operación se divide en dos corrientes:

  • Intermedio : una corriente puede ser seguido por cero o más intermedios operaciones. Su objetivo principal es abrir el flujo, lo cierto grado de correlación de datos / filtrado, y luego devuelve un nuevo flujo, utilizar la siguiente operación. Tales operaciones son de un material inerte (la pereza), es decir, sólo se llama a estos métodos, y realmente no empezar a fluir travesía.
  • Terminal : una corriente sólo puede tener un funcionamiento del terminal, cuando se realiza la operación, el flujo sería utilizado "luz", y ya no puede ser operado. Por lo que este debe ser el último flujo de la operación. Implementación de operaciones de la terminal realmente empezará a atravesar la corriente y produce un resultado o un efecto secundario.

En la operación de conmutación una pluralidad de veces (operación intermedia) para una corriente, corriente de cada elemento de cada conversión, y se ejecuta repetidamente, de manera que la complejidad de tiempo es N (conversiones) para el bucle donde todas las operaciones son Resumiendo no fuera de él? De hecho no es el caso, las operaciones de conversión son perezosos, e integrarán operación de conversión múltiple cuando la operación de terminales, una vez que el ciclo se ha completado. Podemos entender un simple ejemplo, corriente, hay un conjunto de funciones operativas, cada operación de conversión es convertir la función en esta colección, el ciclo de cobro correspondiente corriente cuando está en funcionamiento la terminal, y luego realizar todas las funciones de cada elemento .

Otra operación se denomina  corto-circuito . Se utiliza para hacer referencia a:

  • Para una operación intermedia, si se acepta que un infinito (infinito / sin límites) de Stream, pero vuelve una nueva corriente limitada.
  • Para una operación de terminal, si se trata de aceptar un flujo infinito, pero el resultado puede ser calculado por un tiempo limitado.

Cuando se opera un infinito corriente, y el deseo de completar la operación dentro de un tiempo limitado, usted tiene una operación de cortocircuito en la tubería es una condición necesaria pero no suficiente.

3. Una lista de operación ejemplar de la corriente de

corriente () Obtiene la fuente de artículos pequeños actual, filtro y operación intermedia es mapToInt, el filtrado y la conversión, una suma final () del terminal de operación de datos, para cumplir con los requisitos para el peso de todos los pequeños artículos sumadas.

corriente de uso detallado

En pocas palabras, el uso de la corriente es implementar un filtro-map-reducir el proceso para producir un resultado final o causar un efecto secundario (efecto secundario).

uso específico

1. Crear una corriente común

1,1 Collection bajo corriente () y parallelStream () método

List<String> list = new ArrayList<>();
Stream<String> stream = list.stream(); //获取一个顺序流
Stream<String> parallelStream = list.parallelStream(); //获取一个并行流

método 1.2 matrices en la corriente (), el flujo en la matriz a una

Integer[] nums = new Integer[10];
Stream<Integer> stream = Arrays.stream(nums);

1.3 Secuencia en método estático: de (), iterate (), generar ()

Stream<Integer> stream1 = Stream.of(1,2,3,4,5);
/**
* iterate第一个参数是种子,第二个参数为元素值的生成过程,也就是
* 除第一个位置(0位置)之后的元素值都由前一个元素值作为输入参数(x),
* limit是限制Stream长度
*/
Stream<Integer> stream2 = Stream.iterate(0, x -> x + 2).limit(6);
stream2.forEach(System.out::println);//0 2 4 6 8 10
		
Stream<Double> stream3 = Stream.generate(Math::random).limit(2);
stream3.forEach(System.out::println);//0.4196684476746345 0.9268584030269439

método 1.4 BufferedReader.lines (), cada fila contenido a una corriente

BufferedReader reader = new BufferedReader(new FileReader("F:\\test_stream.txt"));
Stream<String> lineStream = reader.lines();
lineStream.forEach(System.out::println);

1,5 método Uso Pattern.splitAsStream (), las cadenas se separan en corrientes

Pattern pattern = Pattern.compile(",");
Stream<String> stringStream = pattern.splitAsStream("a,b,c,d");
stringStream.forEach(System.out::println);

2. El funcionamiento de la corriente intermedia

2.1 Detección de la rebanada
        filtro: filtros de ciertos elementos de flujo de
        límite (n): Get n elementos
        de salto (n): n elemento, con límite (n) omitir pueden pestaña implementado
        distinta: en flujo elemental por hashCode () y equals () deduplicación elemento

Stream<Integer> stream = Stream.of(6, 4, 6, 7, 3, 9, 8, 10, 12, 14, 14);
 
Stream<Integer> newStream = stream.filter(s -> s > 5) //6 6 7 9 8 10 12 14 14
        .distinct() //6 7 9 8 10 12 14
        .skip(2) //9 8 10 12 14
        .limit(2); //9 8
newStream.forEach(System.out::println);

2.2 Mapeo de        
        mapa: la recepción de una función como un parámetro, la función se aplica a cada elemento, y lo asigna a un nuevo elemento.
        flatMap: la recepción de una función como un parámetro, el valor de cada corriente se sustituyen con otra corriente, entonces todos los flujos conectado a una corriente.

List<String> list = Arrays.asList("a,b,c", "1,2,3");
 
//将每个元素转成一个新的且不带逗号的元素
Stream<String> s1 = list.stream().map(s -> s.replaceAll(",", ""));
s1.forEach(System.out::println); // abc  123
 
Stream<String> s3 = list.stream().flatMap(s -> {
    //将每个元素转换成一个stream
    String[] split = s.split(",");
    Stream<String> s2 = Arrays.stream(split);
    return s2;
});
s3.forEach(System.out::println); // a b c 1 2 3

2.3 Ordenar
        ordenadas (): orden natural, la corriente que se logra mediante los elementos de interfaz Comparable
        ordenados (Comparador com): Organización personalizada personalizada Comparador secuenciador  

List<String> list = Arrays.asList("aa", "ff", "dd");
//String 类自身已实现Compareable接口
list.stream().sorted().forEach(System.out::println);// aa dd ff
 
Student s1 = new Student("aa", 10);
Student s2 = new Student("bb", 20);
Student s3 = new Student("aa", 30);
Student s4 = new Student("dd", 40);
List<Student> studentList = Arrays.asList(s1, s2, s3, s4);
 
//自定义排序:先按姓名升序,姓名相同则按年龄升序
studentList.stream().sorted(
        (o1, o2) -> {
            if (o1.getName().equals(o2.getName())) {
                return o1.getAge() - o2.getAge();
            } else {
                return o1.getName().compareTo(o2.getName());
            }
        }
).forEach(System.out::println);

2,4 Consumo
        peek: Como en el mapa, se pueden obtener en cada flujo elemental. Sin embargo, un mapa que se reciba la expresión de función, el valor de retorno; se recibe la expresión vistazo al consumidor, no hay valor de retorno.

Student s1 = new Student("aa", 10);
Student s2 = new Student("bb", 20);
List<Student> studentList = Arrays.asList(s1, s2);
 
studentList.stream()
        .peek(o -> o.setAge(100))
        .forEach(System.out::println);   
 
//结果:
Student{name='aa', age=100}
Student{name='bb', age=100}            

Resumen: peek sin recibir una expresión lambda valor de retorno puede hacer parte de la producción, un trabajo externo. recibir un mapa con un valor de retorno expresión lambda, después de la Corriente del tipo genérico de asignación de parámetros para convertir el tipo de la expresión lambda devoluciones.

flujo de la operación 3. terminación

3.1 partidos operación de polimerización
        allmatch: la recepción de un predicado función devuelve verdadero si la corriente cuando cada elemento coincide con la afirmación, falso en caso contrario
        cuando se recibe un funciones de predicado, cada elemento cuando la corriente no se ajusta a la afirmación: noneMatch devuelve true, de lo contrario devuelve false
        AnyMatch: recibir una funciones de predicado, siempre que hay una corriente que satisface los elementos afirmación devuelve true, de lo contrario devuelve false
        la findFirst: un primer elemento de la corriente de retorno
        findAny: cualquier flujo elemental en el retorno
        recuento: flujo de retorno el número total de elementos
        max: el valor máximo de los elementos en el flujo de retorno
        min: elemento de flujo de retorno mínimo

List<Integer> list = Arrays.asList(1, 2, 3, 4, 5);
 
boolean allMatch = list.stream().allMatch(e -> e > 10); //false
boolean noneMatch = list.stream().noneMatch(e -> e > 10); //true
boolean anyMatch = list.stream().anyMatch(e -> e > 4);  //true
 
Integer findFirst = list.stream().findFirst().get(); //1
Integer findAny = list.stream().findAny().get(); //1
 
long count = list.stream().count(); //5
Integer max = list.stream().max(Integer::compareTo).get(); //5
Integer min = list.stream().min(Integer::compareTo).get(); //1

operación de reducción de 3,2
        Opcional <T> reducir (BinaryOperator < T> acumulador): La primera vez a través de, la ACC con el primer argumento de la primera corriente primaria, el segundo parámetro es una segunda elemento de flujo elemento; una segunda ejecución, el primer parámetro es una función de la primera consecuencia de la ejecución, el segundo parámetro es el tercer elemento de la corriente, y así sucesivamente.
        T reducir (identidad T, BinaryOperator < T> acumulador): El proceso anterior es la misma, pero cuando la primera representación, el primer parámetro es una función de acumulador de Identidad, y el primer elemento del segundo parámetro es el flujo.
        <U> U reducir (U identidad , BiFunction <U, super T, U?> Acumulador, BinaryOperator <U> combinador): flujo en serie (corriente), el método con el segundo método de la misma, es decir, la tercera combinador de parámetros no funcionará. En corrientes paralelas (parallelStream), sabemos que el flujo es tenedor unirse a una pluralidad de hilos de ejecución, cada hilo de flujo de ejecución en este momento segundo método que se acaba reducir (identidad, acumulador), como la función combinador tercer parámetro , cada hilo aspirado resultado de la ejecución como un nuevo flujo, a continuación, utilizar el primer método a reducir (acumulador) estatuto de flujo.
 

//经过测试,当元素个数小于24时,并行时线程数等于元素个数,当大于等于24时,并行时线程数为16
List<Integer> list = Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24);
 
Integer v = list.stream().reduce((x1, x2) -> x1 + x2).get(); //相当于把所有数求和
System.out.println(v);   // 300
 
Integer v1 = list.stream().reduce(10, (x1, x2) -> x1 + x2);
System.out.println(v1);  //310
 
Integer v2 = list.stream().reduce(0,
        (x1, x2) -> {
            System.out.println("stream accumulator: x1:" + x1 + "  x2:" + x2);
            return x1 - x2;
        },
        (x1, x2) -> {
            System.out.println("stream combiner: x1:" + x1 + "  x2:" + x2);
            return x1 * x2;
        });
System.out.println(v2); // -300
 
Integer v3 = list.parallelStream().reduce(0,
        (x1, x2) -> {
            System.out.println("parallelStream accumulator: x1:" + x1 + "  x2:" + x2);
            return x1 - x2;
        },
        (x1, x2) -> {
            System.out.println("parallelStream combiner: x1:" + x1 + "  x2:" + x2);
            return x1 * x2;
        });
System.out.println(v3); //197474048

3.3 operación de recogida de
        cobro revertido: la recepción de un ejemplo de colector, los elementos se recogieron en otra estructura de datos.
        Collector <T, A, R> es una interfaz con los siguientes 5 métodos abstractos:
            Proveedor Proveedor <A> (): crea un contenedor de resultado A
            BiConsumer <A, T> ACC con (): interfaces de consumidor, el primer parámetro un recipiente a, el segundo parámetro es el elemento de flujo T.
            BinaryOperator <A> combinador (): interfaz de la función, la función de este parámetro como un método para mantenerse al día con el combinador parámetro (el Reducir) añadido y los resultados de las sub-procesos para ejecutar en paralelo en la corriente (la operación de la función A recipiente acumulador) se combinan.
            Función <A, R> finalizador ( ): interfaz de la función, los parámetros son: un recipiente A, el tipo de retorno es: El método del resultado final deseado collect R.
            Set <> Características características (): devuelve un conjunto de conjunto inmutable para indicar las características del colector. Hay tres características:
                la concurrente: Indica soportes de colector de concurrencia. (Hay otro documento oficial que describe temporalmente no explorar, así que no se produzca demasiado traducción)
                sin ordenar: indica que los elementos originales de flujo de secuencia de operación de recolección no se conservan.
                IDENTITY_FINISH: parámetro representa finalizador simplemente identifica que puede ser ignorada.

3.3.1 biblioteca de herramientas colector: Colectores

Student s1 = new Student("aa", 10,1);
Student s2 = new Student("bb", 20,2);
Student s3 = new Student("cc", 10,3);
List<Student> list = Arrays.asList(s1, s2, s3);
 
//装成list
List<Integer> ageList = list.stream().map(Student::getAge).collect(Collectors.toList()); // [10, 20, 10]
 
//转成set
Set<Integer> ageSet = list.stream().map(Student::getAge).collect(Collectors.toSet()); // [20, 10]
 
//转成map,注:key不能相同,否则报错
Map<String, Integer> studentMap = list.stream().collect(Collectors.toMap(Student::getName, Student::getAge)); // {cc=10, bb=20, aa=10}
 
//字符串分隔符连接
String joinName = list.stream().map(Student::getName).collect(Collectors.joining(",", "(", ")")); // (aa,bb,cc)
 
//聚合操作
//1.学生总数
Long count = list.stream().collect(Collectors.counting()); // 3
//2.最大年龄 (最小的minBy同理)
Integer maxAge = list.stream().map(Student::getAge).collect(Collectors.maxBy(Integer::compare)).get(); // 20
//3.所有人的年龄
Integer sumAge = list.stream().collect(Collectors.summingInt(Student::getAge)); // 40
//4.平均年龄
Double averageAge = list.stream().collect(Collectors.averagingDouble(Student::getAge)); // 13.333333333333334
// 带上以上所有方法
DoubleSummaryStatistics statistics = list.stream().collect(Collectors.summarizingDouble(Student::getAge));
System.out.println("count:" + statistics.getCount() + ",max:" + statistics.getMax() + ",sum:" + statistics.getSum() + ",average:" + statistics.getAverage());
 
//分组
Map<Integer, List<Student>> ageMap = list.stream().collect(Collectors.groupingBy(Student::getAge));
//多重分组,先根据类型分再根据年龄分
Map<Integer, Map<Integer, List<Student>>> typeAgeMap = list.stream().collect(Collectors.groupingBy(Student::getType, Collectors.groupingBy(Student::getAge)));
 
//分区
//分成两部分,一部分大于10岁,一部分小于等于10岁
Map<Boolean, List<Student>> partMap = list.stream().collect(Collectors.partitioningBy(v -> v.getAge() > 10));
 
//规约
Integer allAge = list.stream().map(Student::getAge).collect(Collectors.reducing(Integer::sum)).get(); //40

3.3.2 Collectors.toList () de análisis sintáctico

//toList 源码
public static <T> Collector<T, ?, List<T>> toList() {
    return new CollectorImpl<>((Supplier<List<T>>) ArrayList::new, List::add,
            (left, right) -> {
                left.addAll(right);
                return left;
            }, CH_ID);
}
 
//为了更好地理解,我们转化一下源码中的lambda表达式
public <T> Collector<T, ?, List<T>> toList() {
    Supplier<List<T>> supplier = () -> new ArrayList();
    BiConsumer<List<T>, T> accumulator = (list, t) -> list.add(t);
    BinaryOperator<List<T>> combiner = (list1, list2) -> {
        list1.addAll(list2);
        return list1;
    };
    Function<List<T>, List<T>> finisher = (list) -> list;
    Set<Collector.Characteristics> characteristics = Collections.unmodifiableSet(EnumSet.of(Collector.Characteristics.IDENTITY_FINISH));
 
    return new Collector<T, List<T>, List<T>>() {
        @Override
        public Supplier supplier() {
            return supplier;
        }
 
        @Override
        public BiConsumer accumulator() {
            return accumulator;
        }
 
        @Override
        public BinaryOperator combiner() {
            return combiner;
        }
 
        @Override
        public Function finisher() {
            return finisher;
        }
 
        @Override
        public Set<Characteristics> characteristics() {
            return characteristics;
        }
    };
 
}

conclusión

En resumen, las características Stream se pueden resumir de la siguiente manera:

  • No es una estructura de datos
  • No tiene memoria interna, es sólo con la operación de la tubería de la Fuente (estructuras de datos, matrices, la función del generador, el canal IO) para obtener los datos.
  • Nunca modificar los datos subyacentes de su estructura de datos encapsulados. Por ejemplo, la corriente de las operaciones de filtro puede generar un flujo de filtrado no contiene nuevos elementos, en lugar de la eliminación de aquellos elementos de la fuente.
  • Corriente todas las operaciones deben basarse en una expresión lambda como un parámetro
  • No es compatible con acceso indexado
  • Puede solicitar el primer elemento, pero no puede solicitar la segunda, tercera o última. Sin embargo, por favor refiérase al siguiente elemento.
  • Es fácil de generar una matriz o lista
  • inertización
  • Muchos operación Stream es retardo hacia atrás, hasta que finalmente calcular la cantidad de datos tendrá que empezar.
  • siempre serán inertizados operaciones intermedias.
  • paralelismo
  • Cuando se parallelized una corriente, no hay necesidad de escribir código multiproceso, la totalidad de sus operaciones realizadas de forma automática en paralelo.
  • Puede ser ilimitada
    • Establecer un tamaño fijo, Stream no es necesario. límite (n) y la operación de cortocircuito FindFirst () en la operación de este tipo puede hacerse rápidamente y corriente ilimitada.

 

Artículo de referencia:

uso detallado de Java 8 corriente

Flujos de Java API 8 Comentarios

Java 8 Stream y mapear la diferencia vistazo

Publicado 61 artículos originales · ganado elogios 9 · Vistas a 30000 +

Supongo que te gusta

Origin blog.csdn.net/qq_33204444/article/details/105029559
Recomendado
Clasificación