Saltar a contenido

Flujos

Los Streams fueron introducidos en Java 8 para abrir la puerta a la programación funcional al igual que con las expresiones lambda. No hay que confundirlos con los streams de entrada/salida, como un buffer stream de entrada o un fichero de salida.

La API Stream permite manipular las colecciones como nunca antes. Nos permite realizar operaciones sobre la colección, como por ejemplo, buscar, filtrar, reordenar, etc. Pero no nos permite manipular los elementos individualmente en el flujo, sino que se trata el flujo como un todo, a menudo agregando o reduciendo los datos, o quizás contando o agrupando elementos de alguna manera.

Conceptos básicos

Hay que tener en cuenta:

  • Con Streams podemos utilizar cualquier clase que implemente la interfaz Collection como si fuese un Stream con la ventaja que nos ofrecen las expresiones lambda.
  • Con streams hay que tener en cuenta que la fuente o colección que utilicemos no se puede modificar y no debe afectar al estado de la misma.
  • Cada operación dentro del stream debe verse como una operación independiente que opera sobre el argumento (colección).

A través del API Stream podemos trabajar sobre colecciones como si estuviéramos realizando sentencias SQL pero de una manera limpia y clara, evitando bucles y algoritmos que ralentizan los programas e incluso hacen que el código se torne inmanejable.

Los Streams diseñan un flujo de trabajo que se ejecuta de forma unitaria item a item.

Flujo de trabajo

Clases relativas a flujos

Empty Stream

Podemos usar el método empty() en el caso de la creación de una secuencia vacía:

Stream<String> streamEmpty = Stream.empty();

Stream de una Colección (Collection)

También podemos crear un flujo de cualquier tipo de Colección (Collection, List, Set):

List<String> lista = Arrays.asList("a", "b", "c");
Stream<String> streamDeLista = lista.stream();

Stream de Array

También podemos crear un stream a partir de un array existente o de parte de una matriz:

String[] arr = new String[]{"a", "b", "c"};
Stream<String> streamDeArrayFull = Arrays.stream(arr);

Stream.builder()

Cuando se usa el constructor, el tipo deseado debe especificarse adicionalmente en la parte derecha de la instrucción; de lo contrario, el método build() creará una instancia de Stream:

Stream streamBuilder = Stream.builder().add("a").add("b").add("c").build();
Stream.generate() El método generate() acepta un Supplier<T> para la generación de elementos. Como el flujo resultante es infinito, el desarrollador debe especificar el tamaño deseado, o el método generate() funcionará hasta que alcance el límite de memoria:
Stream<String> streamGenerated = Stream.generate(() -> "valor").limit(10);

Stream.iterate()

Otra forma de crear un flujo infinito es usando el método iterate():

Stream<Integer> streamIterated = Stream.iterate(40, n -> n + 2).limit(20);

Stream de Primitivos

Java 8 ofrece la posibilidad de crear streams a partir de tres tipos primitivos: int, long y double. Como Stream es una interfaz genérica y no hay forma de usar primitivas como parámetro de tipo con genéricos, se crearon tres nuevas interfaces especiales: IntStream, LongStream, DoubleStream.

IntStream intStream = IntStream.range(1, 3);
LongStream longStream = LongStream.rangeClosed(1, 3);

Stream de String

También podemos usar String como fuente para crear un flujo con la ayuda del método chars() de la clase String. Dado que no hay una interfaz para CharStream en JDK, en su lugar usamos IntStream para representar un flujo de caracteres.

IntStream streamDeChars = "abc".chars();