Hablar de una colección de .Stream Api

1. ¿Qué es una API de corriente

Java8 proporciona API de flujo permite a los programadores el mismo conjunto de operaciones como el funcionamiento de la base de datos. programadores corriente API Java pueden mejorar en gran medida la productividad de los programadores escribir código eficiente, limpia y sencilla. Al mismo tiempo que proporciona tanto los modos paralelos de serie y la agregación de operación, modelo de concurrencia puede tomar ventaja de procesadores de múltiples núcleos, 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. La API de corriente utilizadas son las siguientes;

+--------------------+       +------+   +------+   +---+   +-------+
| stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
+--------------------+       +------+   +------+   +---+   +-------+

En pocas palabras, la corriente API es un marco de proceso de datos muy eficiente.

2. corriente de varias características

  • El elemento es un tipo particular de objeto, se forma una cola. En Java Stream y no almacena elementos, pero computación bajo demanda.
  • Fuente fuentes de datos. Puede ser un conjunto, las matrices, I / O de canal, el generador y el generador y similares IntStream
  • operación de polimerización similar a la operación de las sentencias SQL, tales como filtro, mapa, reducir, encontrar, partido, ordenados y similares.

3. Corriente del API utilización Liezi

3.1 Corriente Categoría

Se puede crear una corriente de datos de diferentes fuentes. java paquete de recopilación de colecciones, listas, Juegos de estas nuevas clases de flujo () y el método parallelStream (), para la creación de un flujo secuencial (flujos secuenciales) o una corriente concurrente (corrientes paralelas) por estos métodos. corriente concurrente (corrientes paralelas) es más adecuado para su uso en un multiproceso en el presente documento se describe la secuencia de la primera corriente (corrientes secuenciales) al final se describirá con la corriente concurrente (corrientes paralelas),

Arrays.asList("a1", "a2", "a3")
    .stream()
    .findFirst()
    .ifPresent(System.out::println);  // a1
1234

método de flujo de llamadas () del objeto de lista puede devolver un flujo regular de objetos. En el siguiente ejemplo, que no necesitamos para crear un objeto de colección también se puede utilizar la secuencia:

Stream.of("a1", "a2", "a3")
    .findFirst()
    .ifPresent(System.out::println);  // a1
123

método Directamente Stream.of () será capaz de crear un objeto de flujo de un conjunto de objetos,

Además de flujo regular de objetos, JAVA IntStream en 8, LongStream, DoubleStream estas corrientes puede ser procesada, tales como tipos de datos básicos: int, largo, dobles. Por ejemplo: IntStream gama uso () puede sustituir a la tradicional para bucle

IntStream.range(1, 4)
    .forEach(System.out::println);
12

corriente de los tipos básicos (corrientes primitivas) utilizan el tipo de flujo objeto general (flujos de objetos regulares) básicamente el mismo, pero los tipos básicos de corriente (corrientes primitivas) podría utilizar algo especial expresión lambda, como por ejemplo: en lugar de utilizar la función IntFunction, en lugar de utilizar IntPredicate la predicateA, al mismo tiempo 基本类型流(primitive streams)中可以支持一些聚合方法, tales como: suma (), promedio () y similares.

Arrays.stream(new int[] {1, 2, 3})
    .map(n -> 2 * n + 1)
    .average()
    .ifPresent(System.out::println);  // 5.0
1234

mapToInt (), mapToLong (), mapToDouble () rutina puede fluir a través del objeto (corriente objeto regular) de, mapToObj tipo flujo base objeto (corrientes primitivas) en () para completar similares entre el objeto general de la corriente y el tipo de flujo elemental相互转换

IntStream.range(1, 4)
    .mapToObj(i -> "a" + i)
    .forEach(System.out::println);
123

El siguiente ejemplo duplica corriente se asigna primero a la corriente de int, y luego se asigna a una corriente de objeto String:

Stream.of(1.0, 2.0, 3.0)
    .mapToInt(Double::intValue)
    .mapToObj(i -> "a" + i)
    .forEach(System.out::println);

// a1
// a2
// a

3.2 Secuencia API de procesamiento de procedimiento de

Utilizamos la siguiente Liezi para introducir una secuencia de proceso de corriente:

  Stream.of("d2", "a2", "b1", "b3", "c")
                .filter(s -> {
                    System.out.println("filter: " + s);
                    return true;
                });

Imagínese, por encima de la salida de voluntad Liezi lo siguiente:

filter: d2
filter: a2
filter: b1
filter: b3
filter: c

Pero cuando ejecutamos este código, la consola no hace nada de salida. La razón de este fenómeno en términos de la siguiente ocurrirá a continuación. Antes de hablar esta razón primero introducir dos conceptos relacionados Stream, puede ayudar a comprender mejor la API de secuencia:

operación final y operación intermedia

corriente que contiene 中间(intermediate operations)y 最终(terminal operation)dos formas de funcionamiento. 中间操作(intermediate operations)的返回值还是一个stream, Es posible operar el (operaciones intermedias) intermedias en serie por las llamadas de cadena. 最终操作(terminal operation)只能返回void或者一个非stream的结果。En el ejemplo anterior: filtro, mapa, clasificado operación intermedia, y es un forEach operación final. Más operaciones están disponibles en la corriente puede ver doc java. El ejemplo anterior también se denomina cadena llama al flujo de la tubería de funcionamiento.

La mayoría operación corriente en alguna forma de expresiones lambda como parámetros, mediante la operación del método especifica un comportamiento específico de la interfaz, el comportamiento de la interfaz de estos métodos son sustancialmente interferir no (no interferente) y sin estado (stateless). El método definido en libre de interferencias (no interferente) son: El método no cambia la corriente de la fuente de datos subyacente, como el ejemplo anterior: expresión lambda no es añadir o elementos de eliminación en myList. No estado definido (Stateless) método: realizar las operaciones son independientes, tales como el ejemplo anterior, la expresión lambda no es dependiente de cambios en las variables externas o estados que pueden ocurrir durante la ejecución.

El sumario anterior de simples superficiales esas observaciones: El valor de retorno es el tipo de operación o el funcionamiento corriente del medio, el valor de retorno es una operación final o una operación de tipo corriente no nula. API de flujo que no altera los datos originales.

Transmitir los siguiente es una interfaz, volvemos valor puede determinar claramente que es la operación intermedia, que es la operación final. Por lo general utilizamos como el filtro operativo, mapa, distinto, más o menos, y similares, son límite de operación intermedia.

public interface Stream<T> extends BaseStream<T, Stream<T>> {
    Stream<T> filter(Predicate<? super T> predicate);
    <R> Stream<R> map(Function<? super T, ? extends R> mapper);
    IntStream mapToInt(ToIntFunction<? super T> mapper);
    LongStream mapToLong(ToLongFunction<? super T> mapper);
    DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);
    <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
    IntStream flatMapToInt(Function<? super T, ? extends IntStream> mapper);
    LongStream flatMapToLong(Function<? super T, ? extends LongStream> mapper);
    DoubleStream flatMapToDouble(Function<? super T, ? extends DoubleStream> mapper);
    Stream<T> distinct();
    Stream<T> sorted();
    Stream<T> sorted(Comparator<? super T> comparator);
    Stream<T> peek(Consumer<? super T> action);
    Stream<T> limit(long maxSize);
    Stream<T> skip(long n);
    void forEach(Consumer<? super T> action);
    void forEachOrdered(Consumer<? super T> action);
    Object[] toArray();
    <A> A[] toArray(IntFunction<A[]> generator);
    T reduce(T identity, BinaryOperator<T> accumulator);
    Optional<T> reduce(BinaryOperator<T> accumulator);
    <U> U reduce(U identity,
                 BiFunction<U, ? super T, U> accumulator,
                 BinaryOperator<U> combiner);
    <R> R collect(Supplier<R> supplier,
                  BiConsumer<R, ? super T> accumulator,
                  BiConsumer<R, R> combiner);
    <R, A> R collect(Collector<? super T, A, R> collector);
    Optional<T> min(Comparator<? super T> comparator);
    Optional<T> max(Comparator<? super T> comparator);
    long count();
    boolean anyMatch(Predicate<? super T> predicate);
    boolean allMatch(Predicate<? super T> predicate);
    boolean noneMatch(Predicate<? super T> predicate);
    Optional<T> findFirst();
    Optional<T> findAny();
    // Static factories
    public static<T> Builder<T> builder() {
        return new Streams.StreamBuilderImpl<>();
    }
    public static<T> Stream<T> empty() {
        return StreamSupport.stream(Spliterators.<T>emptySpliterator(), false);
    }
    public static<T> Stream<T> of(T t) {
        return StreamSupport.stream(new Streams.StreamBuilderImpl<>(t), false);
    }
    @SafeVarargs
    @SuppressWarnings("varargs") // Creating a stream from an array is safe
    public static<T> Stream<T> of(T... values) {
        return Arrays.stream(values);
    }
    public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f) {
        Objects.requireNonNull(f);
        final Iterator<T> iterator = new Iterator<T>() {
            @SuppressWarnings("unchecked")
            T t = (T) Streams.NONE;
            @Override
            public boolean hasNext() {
                return true;
            }
            @Override
            public T next() {
                return t = (t == Streams.NONE) ? seed : f.apply(t);
            }
        };
        return StreamSupport.stream(Spliterators.spliteratorUnknownSize(
                iterator,
                Spliterator.ORDERED | Spliterator.IMMUTABLE), false);
    }
    public static<T> Stream<T> generate(Supplier<T> s) {
        Objects.requireNonNull(s);
        return StreamSupport.stream(
                new StreamSpliterators.InfiniteSupplyingSpliterator.OfRef<>(Long.MAX_VALUE, s), false);
    }
    public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) {
        Objects.requireNonNull(a);
        Objects.requireNonNull(b);
        @SuppressWarnings("unchecked")
        Spliterator<T> split = new Streams.ConcatSpliterator.OfRef<>(
                (Spliterator<T>) a.spliterator(), (Spliterator<T>) b.spliterator());
        Stream<T> stream = StreamSupport.stream(split, a.isParallel() || b.isParallel());
        return stream.onClose(Streams.composedClose(a, b));
    }
    public interface Builder<T> extends Consumer<T> {
        @Override
        void accept(T t);
        default Builder<T> add(T t) {
            accept(t);
            return this;
        }
        Stream<T> build();
    }
}

Con el centro de operaciones anteriores y la base de la operación final, nos fijamos en la operación anterior Liezi Liezi encontrará en el medio de la operación y no sólo la operación final. Aquí usted ya sabe la respuesta: Secuencia en operación sólo se activará para ejecutar la acción final.

He aquí un vistazo a los efectos de la adición de una operación final:

Stream.of("d2", "a2", "b1", "b3", "c")
    .filter(s -> {
        System.out.println("filter: " + s);
        return true;
    })
    .forEach(s -> System.out.println("forEach: " + s));

La salida el código de seguridad siguientes resultados:

filter:  d2
forEach: d2
filter:  a2
forEach: a2
filter:  b1
forEach: b1
filter:  b3
forEach: b3
filter:  c
forEach: c

Nos encontraremos con la operación de filtrado después de un elemento de filtración entrará inmediatamente en la siguiente ejecución de paso, en lugar de esperar a la finalización de toda la colección y luego filtrar la operación. (Funcionamiento mapa es también un comportamiento similar)

3,3 y la relación entre la eficiencia de orden steream cadena de ejecución

Stream.of("d2", "a2", "b1", "b3", "c")
    .map(s -> {
        System.out.println("map: " + s);
        return s.toUpperCase();
    })
    .filter(s -> {
        System.out.println("filter: " + s);
        return s.startsWith("A");
    })
    .forEach(s -> System.out.println("forEach: " + s));

Filtrar el mapa y la secuencia de los ajustes anteriores pueden reducir significativamente el número de ejecuciones del mapa, para mejorar la eficiencia;

Stream.of("d2", "a2", "b1", "b3", "c")
    .sorted((s1, s2) -> {
        System.out.printf("sort: %s; %s\n", s1, s2);
        return s1.compareTo(s2);
    })
    .filter(s -> {
        System.out.println("filter: " + s);
        return s.startsWith("a");
    })
    .map(s -> {
        System.out.println("map: " + s);
        return s.toUpperCase();
    })
    .forEach(s -> System.out.println("forEach: " + s));

Ordenando es un operaciones intermedias especiales (operación intermedia), en la colección de elementos necesarios para el proceso de clasificación para guardar el estado del elemento, por lo tanto Sorting es una operación de estado (stateful operación).

En primer lugar, la operación de ordenación (es decir, el primer nivel del conjunto de operaciones) en todo el conjunto de entrada, ya que hay muchas combinaciones entre elementos de entrada de la colección, ejemplo de este modo según la operación anterior se lleva a cabo ocho veces.

Puede ser realizada por medio de reordenamiento de la cadena, mejorar la eficiencia de la corriente. Después de modificar la cadena de la realización de filtro secuencial desde la operación de filtrado, causar la operación de entrada de un solo conjunto de elementos ordenados, puede mejorar en gran medida la eficiencia en el caso en el que una gran cantidad de datos.

3.4 secuencia de multiplexión

Arroyo tiene una característica, es decir, cuando se执行完任何一个最终操作(terminal operation)的时候流就被关闭了

Stream<String> stream =
    Stream.of("d2", "a2", "b1", "b3", "c")
        .filter(s -> s.startsWith("a"));        

stream.anyMatch(s -> true);    // ok
stream.noneMatch(s -> true);   // exception

En la misma corriente en la ejecución del AnyMatch después de la puesta en práctica de noneMatch lanzará la siguiente excepción:

java.lang.IllegalStateException: stream has already been operated upon or closed
    at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
    at java.util.stream.ReferencePipeline.noneMatch(ReferencePipeline.java:459)
    at com.winterbe.java8.Streams5.test7(Streams5.java:38)
    at com.winterbe.java8.Streams5.main(Streams5.java:28)
12345

El problema anterior puede resolverse reutilización mediante la creación de una nueva forma para cada operación de cadena de corriente final (operación terminal), la corriente api se han proporcionado en una stream supplierbase de clase para construir el flujo de operación intermedia ya existe (operaciones intermedias) en una nueva fuente.

Supplier<Stream<String>> streamSupplier =
    () -> Stream.of("d2", "a2", "b1", "b3", "c")
            .filter(s -> s.startsWith("a"));

streamSupplier.get().anyMatch(s -> true);   // ok
streamSupplier.get().noneMatch(s -> true);  // ok
streamSupplier的每个get()方法会构造一个新的stream,我们可以在这个stream上执行期望的最终操作(terminal operation)。

Algunas 3.5 Operaciones avanzadas

3.5.1 Collect (colección) operación

El Collect (colección) es una operación final es útil, se puede convertir los elementos de flujo en otra forma, como por ejemplo; lista, conjunto, mapa. Collector utiliza Collect como un parámetro, que comprende cuatro diferentes de funcionamiento del colector: proveedor (constructor inicial), el acumulador (acumulador), combinador (combinador), finalizador (Terminator). Suena complicado, pero la noticia buena es que java 8 construido por coleccionistas como para recoger una serie de operaciones complejas, por lo que para las operaciones más comunes, que no es necesario recorrer para lograr la clase de colector.

Use coleccionistas pueden generar fácilmente List, Set y Mapa objetos.

List<Person> filtered =
    persons
        .stream()
        .filter(p -> p.name.startsWith("P"))
        .collect(Collectors.toList());

Como se puede ver en la demo anterior, la corriente se convierte en la lista es muy simple, si desea convertir Conjunto, a continuación, sólo tiene que utilizar Collectors.toSet () sobre ella.

3.5.2 operación FlatMap

Ya sabemos: Se puede convertir un flujo de objetos en otro objeto a través del método de mapa. Sin embargo, hay un método que utiliza un mapa escena limitado sólo el mapeo entre sí objeto existente objeto específico. Si un objeto se puede asignar a una variedad de objetos, o asignada en un objeto que no existe todavía. Este es el aparece método de propósito flatMap.

método FlatMap convierte cada elemento del objeto de flujo a otro objeto en otro elemento en la corriente, es posible transformar cada objeto en la corriente a cero, uno o más. operación flatMap devuelve el objeto después de la corriente contiene la transformación. '

A Liezi da a continuación:

 String[] words = new String[]{"Hello","World"};
        List<String> a = Arrays.stream(words)
                .map(word -> word.split(""))
                .flatMap(Arrays::stream)
                .distinct()
                .collect(toList());
        a.forEach(System.out::print);

El método de recibir un parámetro de tipo flatMap Stream, puede verse a partir de su nombre, el papel de este método es la de integrar en una pluralidad Corriente Corriente (no nuestra otorgada, genera un mapa).

3.5.3 Reducir la operación

reduce操作可以将stream中所有元素组合起来得到一个元素, JAVA8 reducir el apoyo de los tres métodos diferentes.

  1. Se reducirá en un reglas de comparación
persons
    .stream()
    .reduce((p1, p2) -> p1.age > p2.age ? p1 : p2)
    .ifPresent(System.out::println);
  1. Reducir un segundo valor de identificación de operación recibida y un ACC operador binario como parámetros, este método puede reducir la corriente de todo el nombre del usuario y la edad se ha resumido para dar un nuevo usuario.
Person result =
persons
.stream()
.reduce(new Person("", 0), (p1, p2) -> {
p1.age += p2.age;
p1.name += p2.name;
return p1;
});

System.out.format("name=%s; age=%s", result.name, result.age);
// name=MaxPeterPamelaDavid; age=76
1234567891011
  1. Reducir el tercer método, tres parámetros: a (valor de identidad) valor designado, un acumulador de operador binario (BiFunction acumulador), una combinación binaria de métodos. Dado que el parámetro identificador no se limita estrictamente al tipo de persona, así que podemos usar este método para conseguir reducir la edad general del usuario.
   Integer ageSum = persons
       .stream()
       .reduce(0, (sum, p) -> sum += p.age, (sum1, sum2) -> sum1 + sum2);

   System.out.println(ageSum);  // 76
   12345

El resultado del cálculo es de 76 años, mediante la adición de salida de depuración, podemos comprender en detalle lo que está sucediendo motor de ejecución.

   Integer ageSum = persons
       .stream()
       .reduce(0,
           (sum, p) -> {
               System.out.format("accumulator: sum=%s; person=%s\n", sum, p);
               return sum += p.age;
           },
           (sum1, sum2) -> {
               System.out.format("combiner: sum1=%s; sum2=%s\n", sum1, sum2);
               return sum1 + sum2;
           });

   // accumulator: sum=0; person=Max
   // accumulator: sum=18; person=Peter
   // accumulator: sum=41; person=Pamela
   // accumulator: sum=64; person=David
   12345678910111213141516

Se puede ver en el resultado de la depuración, el acumulador hizo todo el trabajo, primero se obtiene el valor indicado un valor de 0 y un máximo de usuarios, en el siguiente valor de la suma continua de tres pasos debido al incremento acumulado de tamaño en el último paso resumen edad aumentó a 76.

Tenga en cuenta que lo anterior no es el combinador de salida de depuración se lleva a cabo por la misma corriente arriba paralelo.

   Integer ageSum = persons
       .parallelStream()
       .reduce(0,
           (sum, p) -> {
               System.out.format("accumulator: sum=%s; person=%s\n", sum, p);
               return sum += p.age;
           },
           (sum1, sum2) -> {
               System.out.format("combiner: sum1=%s; sum2=%s\n", sum1, sum2);
               return sum1 + sum2;
           });

   // accumulator: sum=0; person=Pamela
   // accumulator: sum=0; person=David
   // accumulator: sum=0; person=Max
   // accumulator: sum=0; person=Peter
   // combiner: sum1=18; sum2=23
   // combiner: sum1=23; sum2=12
   // combiner: sum1=41; sum2=35
   12345678910111213141516171819

La operación anterior ejecutado por la corriente de forma paralela, se obtiene además se lleva a cabo una acción completamente diferente. método combinador se llama en una corriente paralela en. Esto es porque el acumulador se invoca en paralelo, y por lo tanto la necesidad de que el combinador sumando operación se acumule.

4. Corriente paralela

Con el fin de mejorar la eficiencia cuando un gran número de entradas, la corriente se puede realizar en el uso de liberación paralelo. 并行流(Parallel Streams)通过ForkJoinPool.commonPool() 方法获取一个可用的ForkJoinPool。这个ForkJoinPool使用5个线程(实际上是由底层可用的物理cpu核数决定的).

ForkJoinPool commonPool = ForkJoinPool.commonPool();
System.out.println(commonPool.getParallelism());    // 3
On my machine the common pool is initialized with a parallelism of 3 per default. This value can be decreased or increased by setting the following JVM parameter:
123

En mi máquina por defecto para cada inicialización pública Piscina 3 en paralelo, este valor puede ser modificado mediante el ajuste de los parámetros de JVM:

-Djava.util.concurrent.ForkJoinPool.common.parallelism=5
1

Collections中包含parallelStream()方法,通过这个方法能够为Collections中的元素创建并行流。另外也可以调用stream的parallel()方法将一个顺序流转变为一个并行流的拷贝。

Con el fin de comprender el funcionamiento de la aplicación de corrientes paralelas, el siguiente ejemplo muestra la información para el hilo de ejecución actual.

Arrays.asList("a1", "a2", "b1", "c2", "c1")
    .parallelStream()
    .filter(s -> {
        System.out.format("filter: %s [%s]\n",
            s, Thread.currentThread().getName());
        return true;
    })
    .map(s -> {
        System.out.format("map: %s [%s]\n",
            s, Thread.currentThread().getName());
        return s.toUpperCase();
    })
    .forEach(s -> System.out.format("forEach: %s [%s]\n",
        s, Thread.currentThread().getName()));
1234567891011121314

Aplicación de los resultados son los siguientes:

filter:  b1 [main]
filter:  a2 [ForkJoinPool.commonPool-worker-1]
map:     a2 [ForkJoinPool.commonPool-worker-1]
filter:  c2 [ForkJoinPool.commonPool-worker-3]
map:     c2 [ForkJoinPool.commonPool-worker-3]
filter:  c1 [ForkJoinPool.commonPool-worker-2]
map:     c1 [ForkJoinPool.commonPool-worker-2]
forEach: C2 [ForkJoinPool.commonPool-worker-3]
forEach: A2 [ForkJoinPool.commonPool-worker-1]
map:     b1 [main]
forEach: B1 [main]
filter:  a1 [ForkJoinPool.commonPool-worker-3]
map:     a1 [ForkJoinPool.commonPool-worker-3]
forEach: A1 [ForkJoinPool.commonPool-worker-3]
forEach: C1 [ForkJoinPool.commonPool-worker-2]
123456789101112131415

Mediante el análisis de resultados de depuración, podemos entender mejor lo que un hilo ejecuta lo que fluyen operaciones. Desde la salida anterior, podemos ver flujo paralelo utiliza en todos los temas disponibles ForkJoinPool proporcionado para realizar diversas operaciones fluyan. Como no determinar qué hilo que realiza operaciones en flujo paralelo, por lo que el código anterior se ejecuta repetidamente, el resultado de la impresión será diferente.

Expansión ejemplo anterior, operación de ordenación añadido

Arrays.asList("a1", "a2", "b1", "c2", "c1")
    .parallelStream()
    .filter(s -> {
        System.out.format("filter: %s [%s]\n",
            s, Thread.currentThread().getName());
        return true;
    })
    .map(s -> {
        System.out.format("map: %s [%s]\n",
            s, Thread.currentThread().getName());
        return s.toUpperCase();
    })
    .sorted((s1, s2) -> {
        System.out.format("sort: %s <> %s [%s]\n",
            s1, s2, Thread.currentThread().getName());
        return s1.compareTo(s2);
    })
    .forEach(s -> System.out.format("forEach: %s [%s]\n",
        s, Thread.currentThread().getName()));
12345678910111213141516171819

resultados de la ejecución son las siguientes:

filter:  c2 [ForkJoinPool.commonPool-worker-3]
filter:  c1 [ForkJoinPool.commonPool-worker-2]
map:     c1 [ForkJoinPool.commonPool-worker-2]
filter:  a2 [ForkJoinPool.commonPool-worker-1]
map:     a2 [ForkJoinPool.commonPool-worker-1]
filter:  b1 [main]
map:     b1 [main]
filter:  a1 [ForkJoinPool.commonPool-worker-2]
map:     a1 [ForkJoinPool.commonPool-worker-2]
map:     c2 [ForkJoinPool.commonPool-worker-3]
sort:    A2 <> A1 [main]
sort:    B1 <> A2 [main]
sort:    C2 <> B1 [main]
sort:    C1 <> C2 [main]
sort:    C1 <> B1 [main]
sort:    C1 <> C2 [main]
forEach: A1 [ForkJoinPool.commonPool-worker-1]
forEach: C2 [ForkJoinPool.commonPool-worker-3]
forEach: B1 [main]
forEach: A2 [ForkJoinPool.commonPool-worker-2]
forEach: C1 [ForkJoinPool.commonPool-worker-1]
123456789101112131415161718192021

Los resultados de la aplicación parece bastante extraña operación parece algo simplemente realizan de forma secuencial en el hilo principal. En efecto, una especie operación en flujo paralelo usando un nuevo método de JAVA 8: Arrays.parallelSort (). JAVA doc se describe en Arrays.parallelSort (): la longitud de la matriz a ser ordenados para determinar la operación de ordenación se lleva a cabo secuencialmente o en paralelo. doc java describe como sigue:

If the length of the specified array is less than the minimum granularity, then it is sorted using the appropriate Arrays.sort method.
1

Volviendo al ejemplo del capítulo anterior, que ya sabemos método combinador sólo puede ser invocado en corrientes paralelas, veamos esos hilos que realmente se invoca:

List<Person> persons = Arrays.asList(
    new Person("Max", 18),
    new Person("Peter", 23),
    new Person("Pamela", 23),
    new Person("David", 12));

persons
    .parallelStream()
    .reduce(0,
        (sum, p) -> {
            System.out.format("accumulator: sum=%s; person=%s [%s]\n",
                sum, p, Thread.currentThread().getName());
            return sum += p.age;
        },
        (sum1, sum2) -> {
            System.out.format("combiner: sum1=%s; sum2=%s [%s]\n",
                sum1, sum2, Thread.currentThread().getName());
            return sum1 + sum2;
        });
12345678910111213141516171819

resultados de la ejecución son las siguientes:

accumulator: sum=0; person=Pamela; [main]
accumulator: sum=0; person=Max;    [ForkJoinPool.commonPool-worker-3]
accumulator: sum=0; person=David;  [ForkJoinPool.commonPool-worker-2]
accumulator: sum=0; person=Peter;  [ForkJoinPool.commonPool-worker-1]
combiner:    sum1=18; sum2=23;     [ForkJoinPool.commonPool-worker-1]
combiner:    sum1=23; sum2=12;     [ForkJoinPool.commonPool-worker-2]
combiner:    sum1=41; sum2=35;     [ForkJoinPool.commonPool-worker-2]
1234567

Puede ser visto desde la salida de la consola acumulador combinador y las operaciones se ejecutan en hilos paralelos están disponibles.

Para resumir: Cuando una gran cantidad de entrada de datos, flujos paralelos puede traer grandes mejoras de rendimiento. Debe recordarse, sin embargo, una serie de operaciones en paralelo, tales como: reducir, cálculo adicional necesario (operación combinada) a cobro revertido, pero en el orden de la corriente, no se requiere la combinación de estas operaciones.

Además, se sabe que 所有的parallel stream操作共享一个jvm范围内的ForkJoinPool, por lo que debe tener cuidado para evitar el bloqueo de la puesta en práctica de un lento flujo de las operaciones de rutas en paralelo, ya que estas operaciones pueden llevar a otras partes de sus aplicaciones dependen de corrientes paralelas serán las operaciones de respuesta lentos.

5. Liezi detallada

package com.csx.demo.spring.boot.lambda;

import java.util.*;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.ForkJoinPool;
import java.util.function.Consumer;
import java.util.function.Predicate;
import java.util.stream.Collectors;

import static java.util.stream.Collectors.*;

public class LambdaStreamDemo {

    public static void main(String[] args) {

        List<Person> javaProgrammers = new ArrayList<Person>() {
            {
                add(new Person("Elsdon", "Jaycob", "Java programmer", "male", 43, 2000));
                add(new Person("Tamsen", "Brittany", "Java programmer", "female", 23, 1500));
                add(new Person("Floyd", "Donny", "Java programmer", "male", 33, 1800));
                add(new Person("Sindy", "Jonie", "Java programmer", "female", 32, 1600));
                add(new Person("Vere", "Hervey", "Java programmer", "male", 22, 1200));
                add(new Person("Maude", "Jaimie", "Java programmer", "female", 27, 1900));
                add(new Person("Shawn", "Randall", "Java programmer", "male", 30, 2300));
                add(new Person("Jayden", "Corrina", "Java programmer", "female", 35, 1700));
                add(new Person("Palmer", "Dene", "Java programmer", "male", 33, 2000));
                add(new Person("Addison", "Pam", "Java programmer", "female", 34, 1300));
            }
        };

        List<Person> phpProgrammers = new ArrayList<Person>() {
            {
                add(new Person("Jarrod", "Pace", "PHP programmer", "male", 34, 1550));
                add(new Person("Clarette", "Cicely", "PHP programmer", "female", 23, 1200));
                add(new Person("Victor", "Channing", "PHP programmer", "male", 32, 1600));
                add(new Person("Tori", "Sheryl", "PHP programmer", "female", 21, 1000));
                add(new Person("Osborne", "Shad", "PHP programmer", "male", 32, 1100));
                add(new Person("Rosalind", "Layla", "PHP programmer", "female", 25, 1300));
                add(new Person("Fraser", "Hewie", "PHP programmer", "male", 36, 1100));
                add(new Person("Quinn", "Tamara", "PHP programmer", "female", 21, 1000));
                add(new Person("Alvin", "Lance", "PHP programmer", "male", 38, 1600));
                add(new Person("Evonne", "Shari", "PHP programmer", "female", 40, 1800));
            }
        };

        //----------------------forEach使用----------------------
        //所有程序员的姓名
        //forEach方法接收一个Consumer参数,来遍历处理每个对象
        System.out.println("---------------------forEach使用--------------------------");
        System.out.println("java programmer:");
        javaProgrammers.forEach((person) -> {
            System.out.println(person.getFirstName() + "." + person.getLastName());
        });
        System.out.println("php programmer:");
        phpProgrammers.forEach((person) -> {
            System.out.println(person.getFirstName() + "." + person.getLastName());
        });
        //给所有程序员的薪水上涨5%
        System.out.println("给程序员加薪 5% :");
        Consumer<Person> giveRaise = e -> e.setSalary(e.getSalary() / 100 * 5 + e.getSalary());
        javaProgrammers.forEach(giveRaise);
        phpProgrammers.forEach(giveRaise);

        //-----------------------filter使用---------------------
        //过滤器的使用
        //显示月薪超所1400美元的php程序员
        System.out.println("---------------------filter使用--------------------------");
        System.out.println("显示月薪超过1400的php程序员:");
        phpProgrammers.stream()
                .filter(p -> p.getSalary() > 1400)
                .forEach(person -> System.out.println(person.getFirstName() + "." + person.getLastName()));
        //定义filters,这些定义的Filter可以重用
        Predicate<Person> ageFilter = (p) -> (p.getAge() > 25);
        Predicate<Person> salaryFilter = (p) -> (p.getSalary() > 1400);
        Predicate<Person> genderFilter = (p) -> ("female".equals(p.getGender()));
        System.out.println("下面是年龄大于24岁且月薪在$1,400以上的女PHP程序员:");
        phpProgrammers.stream()
                .filter(ageFilter)
                .filter(salaryFilter)
                .filter(genderFilter)
                .forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
        //重用filters
        System.out.println("下面是年龄大于24岁的女性 Java programmers:");
        javaProgrammers.stream()
                .filter(ageFilter)
                .filter(genderFilter)
                .forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
        //-----------------------------map的使用---------------------------------
        System.out.println("---------------------map的使用--------------------------");
        System.out.println("将 PHP programmers 的 first name 拼接成字符串:");
        String phpDevelopers = phpProgrammers
                .stream()
                .map(Person::getFirstName)
                .collect(joining(" ; "));

        System.out.println("将 Java programmers 的 first name 存放到 Set:");
        Set<String> javaDevFirstName = javaProgrammers
                .stream()
                .map(Person::getFirstName)
                .collect(toSet());

        System.out.println("将 Java programmers 的 first name 存放到 TreeSet:");
        TreeSet<String> javaDevLastName = javaProgrammers
                .stream()
                .map(Person::getLastName)
                .collect(toCollection(TreeSet::new));
        //------------------------------limit使用--------------------------------------
        System.out.println("---------------------limit使用使用--------------------------");
        System.out.println("最前面的3个Java programmers:");
        javaProgrammers.stream()
                .limit(3)
                .forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
        System.out.println("最前面的3个女性 Java programmers:");
        javaProgrammers.stream()
                .filter(genderFilter)
                .limit(3)
                .forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
        //------------------------------sort排序使用---------------------------------------
        System.out.println("---------------------sort排序使用--------------------------");
        System.out.println("根据name排序,并显示前5个 Java programmers:");
        List<Person> sortedJavaProgrammers = javaProgrammers
                .stream()
                .sorted(Comparator.comparing(Person::getFirstName))
                .limit(5)
                .collect(toList());
        sortedJavaProgrammers.forEach((p) -> System.out.printf("%s %s; ", p.getFirstName(), p.getLastName()));
        //------------------------------max和min方法的使用-------------------------------
        System.out.println("---------------------max和min方法使用--------------------------");
        System.out.println("工资最低的 Java programmer:");
        Person pears = javaProgrammers
                .stream()
                .min(Comparator.comparingInt(Person::getSalary))
                .get();
        System.out.printf("Name: %s %s; Salary: $%,d.", pears.getFirstName(), pears.getLastName(), pears.getSalary());

        System.out.println("工资最高的 Java programmer:");
        Person person = javaProgrammers
                .stream()
                .max(Comparator.comparingInt(Person::getSalary))
                .get();
        System.out.printf("Name: %s %s; Salary: $%,d.", person.getFirstName(), person.getLastName(), person.getSalary());

        //-------------------------Stream的一些高级用法------------------------
        System.out.println("---------------------Stream的一些高级用法--------------------");
        System.out.println("---------------------Collect的用法--------------------");

        System.out.println("---------------------将Stream转为List--------------------");
        List<Person> list = javaProgrammers.stream()
                .filter(p -> p.getSalary() > 1000)
                .collect(Collectors.toList());

        System.out.println("---------------------将Stream转为Set--------------------");
        Set<Person> set = javaProgrammers.stream()
                .filter(p -> p.getSalary() > 1000)
                .collect(Collectors.toSet());

        System.out.println("---------------------将Stream转为(线程非安全)Map--------------------");
        System.out.println("---------------------将Java程序员按照性别不同分组--------------------");
        Map<String, List<Person>> listMap = javaProgrammers.stream()
                .filter(p -> p.getSalary() > 1000)
                .collect(groupingBy(Person::getGender));
        System.out.println("---------------------将Stream转为(线程安全)Map--------------------");
        System.out.println("---------------------将Java程序员按照性别不同分组--------------------");
        ConcurrentMap<String, List<Person>> listConcurrentMap = javaProgrammers.stream()
                .filter(p -> p.getSalary() > 1000)
                .collect(groupingByConcurrent(Person::getGender));
        //将一个stream转换为map,我们必须指定map的key和value如何映射。要注意的是key的值必须是唯一性的,
        // 否则会抛出IllegalStateException,但是可以通过使用合并函数(可选)绕过这个IllegalStateException异常:

        //将age作为key,firstName作为value,如果遇到age相同,则将两个age相同的Person的FirstName相加作为Value
        Map<Integer, String> integerStringMap = javaProgrammers.stream()
                .collect(toMap(
                        p -> p.getAge(),
                        p -> p.getFirstName(),
                        (value1, value2) -> value1 + value2
                ));

        System.out.println("--------------------计算平均值--------------------");
        Double avAge = javaProgrammers.stream()
                .collect(averagingInt(p -> p.getAge()));
        System.out.println("java程序员平均年龄:"+avAge);

        System.out.println("--------------------获取Java程序员age的统计信息--------------------");
        System.out.println("-------------包括年龄最大值,最小值和平均值等----------------");
        IntSummaryStatistics collect = javaProgrammers.stream()
                .collect(summarizingInt(p -> p.getAge()));

        System.out.println("--------------------获取Java程序员的FirstName进行拼接--------------------");
        String s = javaProgrammers.stream()
                .map(p -> p.getFirstName())
                .collect(joining("and", "preFix", "endFix"));

        //也可以通过Collector.of()方法创建了一个自定义的collector,我们必须给这个collector提供四种功能:
        // supplier, accumulator, combiner,finisher.
        System.out.println("-------------------可以自定义collector--------------------");

        //-------------------flatMap的使用----------------------
        System.out.println("-------------------flatMap的使用----------------------");
        String[] words = new String[]{"Hello","World"};
        List<String> a = Arrays.stream(words)
                .map(word -> word.split(""))
                .flatMap(Arrays::stream)
                .distinct()
                .collect(toList());
        a.forEach(System.out::print);

        //------------------reduce的使用-----------------
        System.out.println("-------------------reduce的使用----------------------");
        javaProgrammers.stream()
                .reduce((p1,p2)->p1.getAge()>p2.getAge()?p1:p2)
                .ifPresent(System.out::println);
        //这个方法直接返回的是Person,将所有Java程序员的年龄相加起来,在set到传进去的Person中
        Person reduce = javaProgrammers.stream()
                .reduce(new Person("", "", "", "", 10, 0), (p1, p2) -> {
                    p1.setAge(p1.getAge() + p2.getAge());
                    p1.setFirstName(p1.getFirstName() + p2.getFirstName());
                    return p1;
                });
        System.out.println(reduce);
        //这个方法返回一个标量值,注意,这种方式中第三个参数是不会被执行的,只有当并行模式下,第三个参数才会被执行
        Integer ageSum = javaProgrammers
                .stream()
                .reduce(0,
                        (sum, p) -> {
                            System.out.format("accumulator: sum=%s; person=%s\n", sum, p.getFirstName());
                            return sum += p.getAge();
                        },
                        (sum1, sum2) -> {
                            System.out.format("combiner: sum1=%s; sum2=%s\n", sum1, sum2);
                            return sum1 + sum2;
                        });
        System.out.println(ageSum);
        //--------------------------------Streams 还可以是并行的(parallel)-------------
        ForkJoinPool commonPool = ForkJoinPool.commonPool();
        System.out.println(commonPool.getParallelism());

    }
}

6. Un resumen breve

  • Puede (List, Set y Map, etc.) será el método del flujo de corriente, corriente puede ser obtenido por métodos Stream.of objeto de colección, también se puede obtener por el método IntStream.range corriente;
  • operación corriente en el medio de la operación y la operación final, los rendimientos operación intermedia una operación final corriente, corriente se cerrará, sólo para realizar la operación dará lugar a las operaciones intermedias finales; Secuencia API que no altera los datos originales
  • los ajustes apropiados pueden mejorar la eficiencia de la orden de ejecución
  • Proveedor puede ser multiplexado Stream.

7. Referencias

  • https://blog.csdn.net/m0_37556444/article/details/84975355

  • https://ifeve.com/stream/

Supongo que te gusta

Origin www.cnblogs.com/54chensongxia/p/12449920.html
Recomendado
Clasificación