Categorías
Java Java 8 Stream

Concat Streams en Java 8

A menudo, es posible que deba realizar una concatenación o fusionar dos flujos. En la clase Stream, hay un método estático concat () que se puede usar para este propósito.

Fusionar dos corrientes

String[] arr1 = { "a", "b", "c", "d" };
String[] arr2 = { "e", "f", "g" };
Stream<String> stream1 = Stream.of(arr1);
Stream<String> stream2 = Stream.of(arr2);
 
Stream<String> stream3 = Stream.concat(stream1, stream2);
String[] arr = stream3.toArray(String[]::new);
System.out.println(Arrays.toString(arr));

Producción:

[a, b, c, d, e, f, g]

Fusionar una secuencia filtrada con otra

Si necesita filtrar una secuencia antes de realizar la concatenación con otra secuencia:

String[] arr1 = { "abc", "bcd", "cdef", "defgh" };
String[] arr2 = { "af", "fg", "gh" };
Stream<String> stream1 = Stream.of(arr1);
Stream<String> stream2 = Stream.of(arr2);
 
Stream<String> stream3 = Stream.concat(stream1.filter(x -> x.length()<4), stream2);
String[] arr = stream3.toArray(String[]::new);
System.out.println(Arrays.toString(arr));

Producción:

[abc, bcd, af, fg, gh]

Fusión de más de 2 corrientes

String[] arr1 = { "a", "b", "c", "d" };
String[] arr2 = { "e", "f", "g" };
String[] arr3 = { "h", "i", "j" };
Stream<String> stream1 = Stream.of(arr1);
Stream<String> stream2 = Stream.of(arr2);
Stream<String> stream3 = Stream.of(arr3);
 
Stream<String> stream = Stream.concat(Stream.concat(stream1, stream2), stream3);
String[] arr = stream.toArray(String[]::new);
System.out.println(Arrays.toString(arr));

Producción:

[a, b, c, d, e, f, g, h, i, j]

Tenga en cuenta que los elementos devueltos por el método Stream.concat () están ordenados. Por ejemplo, las siguientes dos líneas devuelven el mismo resultado:

Stream.concat(Stream.concat(stream1, stream2), stream3);
Stream.concat(stream1, Stream.concat(stream2, stream3));

Pero el resultado de los dos siguientes es diferente.

Stream.concat(Stream.concat(stream1, stream2), stream3); //[a, b, c, d, e, f, g, h, i, j]
Stream.concat(Stream.concat(stream2, stream1), stream3); //[e, f, g, a, b, c, d, h, i, j]

Utilice Stream.of (…). FlatMap () para fusionar

Para que el código sea más legible, también puede usar el método Stream.of () para fusionar más de dos flujos.

String[] arr1 = { "a", "b", "c", "d" };
String[] arr2 = { "e", "f", "g" };
String[] arr3 = { "h", "i", "j" };
Stream<String> stream1 = Stream.of(arr1);
Stream<String> stream2 = Stream.of(arr2);
Stream<String> stream3 = Stream.of(arr3);
 
//use Stream.of(T... values)
Stream<String> stream = Stream.of(stream1, stream2, stream3).flatMap(x -> x);
 
String[] arr = stream.toArray(String[]::new);
System.out.println(Arrays.toString(arr));

Categorías
Java Java 8 Stream

Recuperar una lista de una secuencia en Java 8

Método 1

String[] arr = { "ab", "bcd", "cdef", "defgh", "efhik", "fghijk", "ghijkl" };
Stream<String> stream = Stream.of(arr);

Método 2

String[] arr = { "ab", "bcd", "cdef", "defgh", "efhik", "fghijk", "ghijkl" };
Stream<String> stream = Stream.of(arr);
 
ArrayList<String> list = stream.filter(x -> x.length() < 5).collect(Collectors.toCollection(ArrayList::new));

Método 3

String[] arr = { "ab", "bcd", "cdef", "defgh", "efhik", "fghijk", "ghijkl" };
Stream<String> stream = Stream.of(arr).parallel();
 
ArrayList<String> list = new ArrayList<String>();
stream.forEach(list::add);

Método 4

Si una secuencia es paralela, se debe usar el método forEachOrdered () en lugar de forEach ().

String[] arr = { "ab", "bcd", "cdef", "defgh", "efhik", "fghijk",	"ghijkl" };
Stream<String> stream = Stream.of(arr).parallel();
 
ArrayList<String> list = new ArrayList<String>();
stream.forEachOrdered(list::add);

Categorías
Java 8 Stream

Contador de Java 8

Antes de Java 8, los desarrolladores a menudo pensaban en diferentes formas de escribir un contador para contar algo, por ejemplo, contar la frecuencia de palabras.

En Java 8, puede escribir un contador en dos líneas simples. Además, puede aprovechar la computación paralela.

Aquí está el contador de Java 8:

package com.programcreek.java8;
 
import java.util.stream.Collectors;
import java.util.stream.Stream;
import java.util.Map;
 
public class Java8Counter {
	public static void main(String[] args) {
		String[] arr = {"program", "creek", "program", "creek", "java", "web", "program"};
		Stream<String> stream = Stream.of(arr).parallel();
		Map<String, Long> counter = stream.collect(Collectors.groupingBy(String::toString, Collectors.counting()));
		System.out.println(counter.get("creek"));
	}
}

Categorías
Java Java 8 Stream

¿Por qué se compila Stream.max (Integer :: max)?

Considere el siguiente código:

Stream<Integer> stream = Stream.of(1,2,3,4);
int max = stream.max(Math::max).get();
System.out.println(max);

Según el Javadoc de Steam.max (), el argumento del método max () debería ser un Comparador. En el código anterior, la referencia del método es a métodos estáticos de la clase Math. Pero el código se compila sin ningún mensaje de advertencia o error.

Entonces, ¿por qué es Math::max aceptable mientras que aparentemente no es un Comparador?

La razón por la que se compila el código anterior está en el modo en que funciona la nueva funcionalidad lambda en Java 8. Se basa en un concepto que se conoce informalmente como interfaces de «método abstracto único (SAM)». La idea básica es que cualquier interfaz con un método abstracto puede ser implementada automáticamente por cualquier referencia de método o lambda, si la referencia de método o lambda coincide con el SAM en la interfaz.

Si echamos un vistazo a la interfaz de funciones Comparator[1], su método abstracto único se parece a lo siguiente:

public Comparator<T> { 
       int compare(T o1, T o2); 
}

Si un método (max () en este caso) está buscando un Comparador, entonces está esencialmente buscando esta firma:

int xxx(Integer o1, Integer o2);

«xxx» significa que el nombre del método no se utiliza con fines de coincidencia. El método Math.max () tiene la siguiente firma:

int max(int i1, int i2);

Autoboxing permite que esto aparezca como un comparador en un contexto de método. Por lo tanto, Math::max es aceptado. Del mismo modo, también es aceptable un montón de otros métodos, como Integer::max, Integer::min, Math::minetc.

El resultado será incorrecto. En lugar de, Integer::comparedebe usarse como el siguiente:

max = stream.max(Integer::compare).get();
System.out.println(max);

Referencias:
[1]. Javadoc de Comparator en Java 8
[2]. Esta pregunta también es publicada por Fge de Desbordamiento de pila. La respuesta más popular es publicada por David Lloyd.

Categorías
Java 8 Stream

¿Qué es Stream?

La definición de flujo en Java 8 es «una secuencia de elementos de una fuente que admite operaciones agregadas». Los flujos consumen desde una fuente como colecciones, matrices o recursos de E / S. Los flujos admiten las operaciones comunes de los lenguajes de programación funcionales, como mapear, filtrar, reducir, buscar, ordenar, etc.

Un ejemplo motivacional

Es fácil encadenar varios métodos que aceptan la expresión lambda como parámetro. Un cálculo complejo antes de Java 8 se puede escribir en muchas menos líneas.

Aquí hay un muy buen ejemplo motivacional de Java doc.[1]:

int sum = components.stream()
                      .filter(c -> c.getColor() == RED)
                      .mapToInt(c -> c.getWeight())
                      .sum();

En este ejemplo usamos componentes, una colección, como fuente para el flujo, y luego realice una operación de filtro-mapa-reducción en el flujo para obtener la suma de los pesos de los widgets rojos.

Transmisión frente a colección

Las corrientes pueden parecer similares a las colecciones, pero son conceptos muy diferentes. Cuando trabaja con grandes conjuntos de datos, es posible que no desee procesar todo el conjunto de datos como una colección. Hay una metáfora muy buena al comparar la diferencia entre Colección y Stream. [5]. Considere una película almacenada en un DVD en lugar de transmitida a través de Internet. Una película se puede ver como un conjunto de bytes. El DVD se puede ver como una colección de bytes porque contiene todos los elementos de datos. Al ver la película a través de Internet, la película se puede ver como un flujo de bytes. El reproductor de transmisión de video solo necesita tener unos pocos bytes antes de donde el usuario está mirando. De esta manera, el reproductor de video puede comenzar a mostrar la película desde el principio de la transmisión antes de que la mayoría de los datos de la transmisión se hayan calculado siquiera.

La diferencia entre Stream y Collection se puede detallar de la siguiente manera:

  1. Una secuencia proporciona una interfaz para un conjunto secuenciado de valores de un tipo de elemento específico. Sin embargo, a diferencia de las colecciones, las transmisiones en realidad no almacenan elementos. Los elementos se calculan bajo demanda. Los flujos se pueden ver como colecciones construidas de forma perezosa, cuyos valores se calculan cuando se necesitan.
  2. Las operaciones de transmisión no cambian su fuente. En cambio, devuelven nuevos flujos que almacenan el resultado.
  3. Posiblemente ilimitado. Las colecciones tienen un tamaño finito, pero las transmisiones no. Las operaciones de cortocircuito como limit (n) o findFirst () pueden permitir que los cálculos en flujos infinitos se completen en un tiempo finito.
  4. Consumible. Durante la vida útil de una transmisión, los elementos de la transmisión solo se visitan una vez. Si desea volver a visitar el mismo elemento en la transmisión, deberá volver a generar una nueva transmisión en función de la fuente.

Operaciones intermedias frente a operaciones de terminal

Las operaciones intermedias transforman un flujo en otro, mientras que las operaciones terminales producen un resultado. Cuando se ejecuta una operación de terminal en una secuencia, la secuencia ya no se puede utilizar.

Por ejemplo, en el siguiente código:

list.stream().filter(s -> s.length() > 2).count();

filter () es una operación intermedia y count () es una operación terminal. Cuando se llama a count (), ya no podemos usar la secuencia.

Las operaciones intermedias de uso frecuente incluyen: filtro, mapa, diferenciado, ordenado, salto, límite, mapa plano

Las operaciones de la terminal de uso frecuente incluyen:

  1. forEach, toArray, recopilar, reducir, contar, min, max
  2. findFirst, findAny, anyMatch, allMatch, noneMatch

Referencias:
1. Documento Java de Stream
2. Mónada en programación funcional
3. Procesamiento de datos con secuencias de Java SE 8, parte 1
4. paquete java.util.stream
5. Java 8 Lambdas en acción, por Raoul-Gabriel Urma, Mario Fusco y Alan Mycroft

Categorías
Java 8 Stream

5 formas de crear una secuencia en Java 8

1. De matrices

String[] arr = { "program", "creek", "program", "creek", "java", "web",
		"program" };
stream = Stream.of(arr);
stream = Stream.of("program", "creek", "program", "creek", "java",
		"web", "program");
String[] stringArr = {"a", "b", "c", "d"};
Stream<String> stream = Arrays.stream(stringArr);

2. De colecciones

// from collection
List<String> list = new ArrayList<String>();
list.add("java");
list.add("php");
list.add("python");
stream = list.stream();

3. Utilice Stream.generate ()

// generate()
Stream<String> stream = Stream.generate(() -> "test").limit(10);
String[] strArr = stream.toArray(String[]::new);
System.out.println(Arrays.toString(strArr));

4. Utilice Stream.iterate ()

// iterate()
Stream<BigInteger> bigIntStream = Stream.iterate(BigInteger.ZERO, n -> n.add(BigInteger.ONE)).limit(10);
BigInteger[] bigIntArr = bigIntStream.toArray(BigInteger[]::new);
System.out.println(Arrays.toString(bigIntArr));

5. De API populares

// stream method from APIs
String sentence = "Program creek is a Java site.";
Stream<String> wordStream = Pattern.compile("W").splitAsStream(sentence);
String[] wordArr = wordStream.toArray(String[]::new);
System.out.println(Arrays.toString(wordArr));

Categorías
Java 8 Stream

Transformar Stream usando Steam.map ()

El método map () es una operación intermedia. Devuelve una secuencia que consta de los resultados de aplicar la función dada a cada elemento de la secuencia.

El siguiente código devuelve un flujo de enteros, que son el resultado de aplicar el método String.length ().

import java.util.ArrayList;
import java.util.List;
import java.util.stream.Stream;
 
 
public class Java8Map {
 
	public static void main(String[] args) {
		List<String> list = new ArrayList<String>();
		list.add("java");
		list.add("php");
		list.add("python");
 
		//map function
		Stream<Integer> stream = list.stream().map(p -> p.length());
 
		Integer[] lengthArr = stream.toArray(Integer[]:: new);
 
		for(int a: lengthArr){
			System.out.println(a);
		}
	}
}

Categorías
Java 8 Stream

Transformar flujo usando Stream.filter ()

El método filter () es una operación intermedia. Devuelve una secuencia que consta de los elementos de esta secuencia que coinciden con la condición dada.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
 
 
public class Java8Filter<T> {
 
	public static void main(String[] args) {
		List<String> list = new ArrayList<String>();
		list.add("java");
		list.add("php");
		list.add("python");
		list.add("lisp");
		list.add("c++");
 
		//filter function
		Stream<String> stream = list.stream().filter(p -> p.length() > 3);
		String[] arr = stream.toArray(String[]::new);
 
		System.out.println(Arrays.toString(arr));
	}
}

Categorías
Java 8 Stream

Reducir ejemplos de transmisión

La API de Java 8 Stream contiene un conjunto de operaciones de terminal (como promedio, suma, mínimo, máximo y recuento) que devuelven un valor al combinar los elementos de un flujo. Estas operaciones de terminal se denominan operaciones de reducción. Además de esas operaciones de terminal, el JDK también proporciona el método de reducción de propósito general: reduce(), que describe esta publicación.

1. Un ejemplo simple de Stream.reduce ()

Suponiendo que tengamos un flujo de palabras como el siguiente:

List<String> list = new ArrayList<String>();
list.add("java");
list.add("php");
list.add("python");
list.add("perl");
list.add("c");
list.add("lisp");
list.add("c#");
Stream<String> wordStream = list.stream();

Podemos usar el siguiente código para obtener la suma de la longitud de las cadenas en la lista:

int s = wordStream.map(s -> s.length())
		.mapToInt(Integer::new)
		.sum();
 
System.out.println(s);

También podemos usar el método Stream.reduce (), que es un método más general:

Stream<Integer> lengthStream = wordStream.map(s -> s.length());
Optional<Integer> sum = lengthStream.reduce((x, y) -> x + y);
sum.ifPresent(System.out::println);

Stream.reduce () toma la entrada como una función acumuladora que toma dos parámetros: un resultado parcial de la reducción (en este caso, la suma de todos los enteros procesados ​​hasta ahora) y el siguiente elemento del flujo (en este caso, un entero ). Devuelve un nuevo resultado parcial. En este caso, la función de acumulador es una expresión lambda que suma dos valores enteros y devuelve un valor entero.

En lugar de usar la expresión lambda, también puede usar:

Optional<Integer> sum = lengthStream.reduce(Integer::sum);

2. Proporcionar valor de identificación

En el ejemplo anterior, el método reduce () devuelve un Opcional. Podemos evitar el tipo opcional, dándole un valor inicial 0:

Stream<Integer> lengthStream = wordStream.map(s -> s.length());
int sum = lengthStream.reduce(0, (x, y) -> x + y);
System.out.println(sum);

El valor inicial debe ser un valor para la función de reducción. Para todo t, reduce (identidad, t) y reduce

Además, también podemos escribir el método reduce () como el siguiente:

int s = wordStream.reduce(0, (x, y) -> x + y.length(), (x, y) -> x + y);
System.out.println(s);

Los tres parámetros son identificador, reductor y combinador.
– identidad – valor de identidad para la función de combinación
– reductor – función para combinar dos resultados
– combinador – función para agregar un elemento adicional a un resultado.

Referencia:
1. Stream.reduce ()

Categorías
Java 8 Stream

Recopilar ejemplos de resultados de transmisión

En lugar de reducir un flujo a un valor, también podemos recopilar los resultados. Podemos recopilar resultados en una matriz, un conjunto / lista o un mapa utilizando el método Stream.collect ().

1. Recopile los resultados de la transmisión en una matriz

List<String> list = new ArrayList<String>();
list.add("java");
list.add("php");
list.add("python");
Stream<String> wordStream = list.stream();
 
Stream<Integer> lengthStream = wordStream.map(s -> s.length());
Integer[] lenArr = lengthStream.toArray(Integer[]::new);
System.out.println(Arrays.toString(lenArr));

* Tenga en cuenta que toArray () es una operación de terminal. Después de que se invoca toArray (), la secuencia ya no está disponible para su uso.

2. Recopile los resultados de la transmisión en una lista / conjunto

Recopile resultados para enumerarlos usando Collectors.toList().

List<Integer> intList= lengthStream.collect(Collectors.toList());

Recopile resultados para enumerarlos usando Collectors.toSet().

Set<Integer> intSet= lengthStream.collect(Collectors.toSet());

También podemos especificar el tipo de conjunto a usar así:

TreeSet<Integer> intSet= lengthStream.collect(Collectors.toCollection(TreeSet::new));

3. Recopile los resultados de la transmisión en el mapa

List<String> list = new ArrayList<String>();
list.add("java");
list.add("php");
list.add("python");
Stream<String> wordStream = list.stream();
 
// to map
Map<String, Integer> map = wordStream.collect(Collectors.toMap(Function.identity(), s->s.length()));
System.out.println(map);

Producción:

{python=6, java=4, php=3}