Java 8 Streams – Transformaciones utilizando stream().map


Continuado con los posts sobre Streams en este post explicaremos como transformar un objeto a otro utilizando la función map. Uno de los usos comunes de esto es transformar objetos a DTO’s así que tomaremos este caso como ejemplo.

Definiendo el modelo

Antes de comenzar a utilizar streams definiremos dos clases a utilizar Person y PersonDto, las cuales serán la clase base de mi aplicación y la clase a la que quiero transformar mi objeto.

Person.java

public class Person {
	private String firstName;
	private String middleName;
	private String lastName;
	private String address;

	public Person() {
	}

	public Person(String firstName, String middleName, String lastName, String address) {
		super();
		this.firstName = firstName;
		this.middleName = middleName;
		this.lastName = lastName;
		this.address = address;
	}

	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getMiddleName() {
		return middleName;
	}

	public void setMiddleName(String middleName) {
		this.middleName = middleName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}

}

PersonDto.java

public class PersonDto {
	private String firstName;
	private String middleName;
	private String lastName;
	private String address;

	public PersonDto() {
	}

	public PersonDto(String firstName, String middleName, String lastName, String address) {
		super();
		this.firstName = firstName;
		this.middleName = middleName;
		this.lastName = lastName;
		this.address = address;
	}

	public String getFirstName() {
		return firstName;
	}

	public void setFirstName(String firstName) {
		this.firstName = firstName;
	}

	public String getMiddleName() {
		return middleName;
	}

	public void setMiddleName(String middleName) {
		this.middleName = middleName;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public String getAddress() {
		return address;
	}

	public void setAddress(String address) {
		this.address = address;
	}
}

Las dos clases anteriores servirán como base para realizar mis transformaciones.

Creando objetos de ejemplo

De el mismo modo que en el post anterior, en este post utilizaremos Java-Faker para generar datos de ejemplo más información de como utilizarlo en el siguiente enlace Java-Faker.

public static List getNames() {
	Faker faker = new Faker();
	List names = new ArrayList();
	for (int i = 0; i <= 1000; i++) {
		names.add(new Person(faker.name().firstName(), faker.name().firstName(), faker.name().lastName(),
				faker.address().fullAddress()));
	}
	return names;
}

El método anterior utiliza un Faker para generar datos de ejemplo y genera 1000 objetos Person con datos aleatorios.

Transformando objetos de la clase Person a PersonDto

Con el código anterior seremos capaces de generar 1000 objetos de tipo Person, el siguiente paso será transformarlos a objetos tipo PersonDto, veamos el siguiente ejemplo:

public static void main(String[] args) {
	List names = getNames();
	List peopleDtos = names.stream().map(person -> {
		return new PersonDto(person.getFirstName(), person.getMiddleName(), person.getLastName(),
				person.getAddress());
	}).collect(Collectors.toList());
	for (PersonDto personDto : peopleDtos) {
		System.out.println("First name: " + personDto.getFirstName());
		System.out.println("Middle name: " + personDto.getMiddleName());
		System.out.println("Last name: " + personDto.getLastName());
		System.out.println("Address: " + personDto.getAddress());
		System.out.println("--------");
	}
}

El código anterior realiza lo siguiente:

  1. Obtiene los 1000 objetos tipo Person a transformar
  2. Utilizando Streams se ejecutará lo siguiente:
    1. Crear un stream
    2. Transformar un stream de objetos de tipo Person a uno de tipo PersonDto basado en el lambda que define la transformación
    3. Transformar la respuesta a una lista
  3. Se imprimen los elementos pero ya con una lista tipo PersonDto

Salida:

--------
First name: Kenyatta
Middle name: Vallie
Last name: Runte
Address: Suite 750 049 Darrin Ford, East Minastad, NY 65986
--------
First name: Sedrick
Middle name: Cara
Last name: Willms
Address: 36767 Lueilwitz Pines, Delphineborough, NV 41658
--------
First name: Luisa
Middle name: Merritt
Last name: Weissnat
Address: 00304 Cummings Viaduct, North Elainaland, WI 84019-0601
--------
First name: Aniyah
Middle name: Deangelo
Last name: Keebler
Address: 37324 Prosacco Grove, North Shaniachester, MD 54667
--------
First name: Edwardo
Middle name: General
Last name: Fadel
Address: 54550 Grimes Forge, Trantowborough, VA 14184-7842
--------
First name: Jensen
Middle name: Edd
Last name: Rutherford
Address: 31157 McGlynn Isle, Moiseston, HI 99805
--------
1000 more...

Esto facilita la transformación de objetos de una clase a otra y es muy util al momento de escribir servicios web.

Para enterarte sobre futuros posts te recomendamos seguirnos en nuestras redes sociales: https://twitter.com/geeks_mx y https://www.facebook.com/geeksJavaMexico/.

Autor: Alejandro Agapito Bautista

Twitter: @raidentrance

Contacto:raidentrance@gmail.com

Java 8 Streams – Filtering


Siguiendo con la serie sobre Java Streams, en este post analizaremos como funcionan los filtros sobre Streams de Java a través de ejemplos.

Preparando el ejemplo

Antes de empezar con los ejemplos debemos entender como funciona un api que utilizaremos en nuestros ejemplos su nombre es Java Faker, puedes aprender más sobre la misma en el siguiente enlace java-faker. Este api nos permitirá generar datos de ejemplo.

Problema a resolver

Dado un arreglo de 1000 nombres buscar todos los que inician con la letra A, veamos el código que genera la lista de nombres:

public static List getNames() {
	Faker faker = new Faker();
	List names = new ArrayList();
	for (int i = 0; i <= 1000; i++) {
		names.add(faker.name().firstName());
	}
	return names;
}

Haciendo uso del API Java Faker generamos 1000 nombres aleatorios y los devolvemos.

Resolviendo el problema utilizando streams

Una vez que ya sabemos como obtener la lista de nombres el siguiente paso será implementar la lógica para filtrar solo los nombres que inician con la letra A, veamos el código:

public static void main(String[] args) {
	List names = getNames();
	List collect = names.stream()
			.filter(name -> name.startsWith("A"))
			.collect(Collectors.toList());
	for (String string : collect) {
		System.out.println(string);
	}
}

En el código anterior se puede ver que es muy simple hacer el filtro utilizando una función lambda que determina que registros se permitirán y cuales no, veamos la salida:

Alford
Annetta
Adonis
Alfred
Anais
Ashly
Aletha
Aniya
Alanna
Ahmed
Asa
Arlo
Angeline
Alexzander
Alexane
America
Annabell
Alexandra
Alysson
Amparo
Agustin
Anya
Aurore
Abe
Augustine
Adell
Ava
Alva
Aiyana
Augustus
Asha
Alvera
Abner
Ashlee
Anais
Andrew
Alejandra
Amely
Annalise
Adelia
Alf
Ashley
Aylin
Aubrey
Abelardo
Angelina
Andres
Agustina
Aileen
Alexys
Aglae
Albina
Anais
Alexander
Ashtyn
Adelbert
Alek
Alaina
Asia
Ari
Ara
Arturo
Aubree
Anne
Amelia
Alfreda
Aditya
Anais
Alex
Alexandre
Ara
Alize
Autumn
Anabel
Aletha
Akeem
Antonietta
Aliyah
Abdul
Arturo
Ashton
Abner
Alisa
Abbigail
Amber
Anais
Alanis
Abe
Amiya
Alvah
Alejandrin
Angelita

Solo nombres que contengan la letra A serán mostrados debido al filtro que realizamos.

Para enterarte sobre futuros posts te recomendamos seguirnos en nuestras redes sociales: https://twitter.com/geeks_mx y https://www.facebook.com/geeksJavaMexico/.

Autor: Alejandro Agapito Bautista

Twitter: @raidentrance

Contacto:raidentrance@gmail.com

 

Java 8 Streams – Remueve valores duplicados en una lista


En el ejemplo del día de hoy hablaremos de un problema común, remover datos duplicados de una lista, como sabemos existen muchos algoritmos para hacerlo, en este post hablaremos de dos formas utilizando Streams.

1 Utilizando distinct()

El método distinct nos permitirá remover datos duplicados de una lista.

public static void main(String[] args) {
	List numbers = Arrays.asList(7, 7, 7, 7, 2, 2, 2, 3, 3, 3, 3, 100, 100, 200, 200);
	numbers = numbers.stream().distinct().collect(Collectors.toList());
	System.out.println(numbers);
}

En el código anterior podemos ver que generamos una lista con muchos números duplicados, generamos un stream y hacemos uso de distinct() lo cual removerá datos duplicados y lo devolvemos como lista, con lo anterior tendremos la siguiente salida:

[7, 2, 3, 100, 200]

Como se puede ver, aunque en la lista original tenemos repetidos los números multiples veces, al utilizar distinct() todos los elementos duplicados fueron removidos y tendremos resultados únicos.

2 Utilizando set

Como sabemos existe una estructura de datos en Java llamada Set la cuál solo almacena datos únicos y es muy utilizada para remover datos duplicados, veamos un ejemplo:

public static void main(String[] args) {
	List numbers = Arrays.asList(7, 7, 7, 7, 2, 2, 2, 3, 3, 3, 3, 100, 100, 200, 200);
	Set nums = numbers.stream().collect(Collectors.toSet());
	System.out.println(nums);
}

En el código anterior podemos ver que nuestra entrada es una lista y lo único que hacemos es a través del método collect(..) la transformamos a un Set y con esto aseguramos que la salida será solo datos únicos, veamos la salida:

[2, 3, 100, 7, 200]

Del mismo modo que con el método distinct el resultado es un conjunto de números únicos en la salida, es importante considerar el tipo de colección que queremos utilizar en nuestro problema ya que eso determinará cuál de los dos mecanismos utilizaremos.

Para enterarte sobre futuros posts te recomendamos seguirnos en nuestras redes sociales: https://twitter.com/geeks_mx y https://www.facebook.com/geeksJavaMexico/.

Autor: Alejandro Agapito Bautista

Twitter: @raidentrance

Contacto:raidentrance@gmail.com

Java 8 Streams – Buscando el valor mínimo y máximo


Continuado con los posts sobre Streams en este post explicaremos como realizar búsquedas de el valor mínimo y máximo en una lista de números utilizando Streams.

Solución común

A continuación presentamos la solución común para encontrar el elemento mínimo de una lista:

public static void main(String[] args) {
	List numbers = Arrays.asList(7, 2, 3, 100, 200, 300, 400, 5, 1);
	int min = numbers.get(0);
	for (Integer value : numbers) {
		if (value < min) {
			min = value;
		}
	}
	System.out.println(min);
}

El código anterior devuelve el elemento más pequeño en la lista.

Solución utilizando streams

El siguiente código muestra como realizar la misma búsqueda del número menor utilizando streams.

public static void main(String[] args) {
	List numbers = Arrays.asList(7, 2, 3, 100, 200, 300, 400, 5, 1);
	Integer minValue = numbers.stream().min(Comparator.naturalOrder()).get();
	System.out.println(minValue);
}

Como se puede ver el código anterior es mucho más simple y mucho más fácil de leer, si deseamos buscar el elemento mayor solo debemos ejecutar lo siguiente:

public static void main(String[] args) {
	List numbers = Arrays.asList(7, 2, 3, 100, 200, 300, 400, 5, 1);
	Integer maxValue = numbers.stream().max(Comparator.naturalOrder()).get();
	System.out.println(maxValue);
}

El código anterior mostrará el elemento mayor en la lista.

Para enterarte sobre futuros posts te recomendamos seguirnos en nuestras redes sociales: https://twitter.com/geeks_mx y https://www.facebook.com/geeksJavaMexico/.

Autor: Alejandro Agapito Bautista

Twitter: @raidentrance

Contacto:raidentrance@gmail.com

Java 8 Streams – IntStream (Range y rangeClosed)


En este post continuaremos con la serie de Java8 Streams, el día de hoy explicaremos el funcionamiento de range y rangeClosed.

IntStream

IntStream es un stream (flujo) de valores enteros primitivos, es posible encontrar streams para otros datos primitivos pero todos tienen un comportamiento similar, a continuación se muestran algunos ejemplos explicando la manera tradicional y haciendo uso de Streams:

Problema 1

Imprimir los números del 0 al 9.

Sin utilizar streams

public static void imperativeExample1() {
	for (int i = 0; i< 10; i++) {
		System.out.print(i);
	}
}

En este ejemplo se puede ver que se imprimen los números del 0 al 9 a través de un ciclo for.

Utilizando streams

public static void streamExample2() {
	IntStream.range(0, 10).forEach(System.out::print);
}

Podemos ver que con una línea de código podemos obtener el mismo resultado.

Salida:

Imperative example
0123456789
Stream example
0123456789

La salida anterior muestra que haciendo uso de ambos métodos podemos obtener la misma salida.

Problema 2

Iterar sobre una lista de personas.
Persona.java

public class Person {
	private String name;
	private String lastName;
	private Integer age;

..... Getters / Setters

}

La clase anterior es una simple clase java y no tiene nada que ver con Streams.

Sin utilizar streams

public static void simpleExample2() {
	List people = getPeople();
	for (int i = 0; i < people.size(); i++) {
		System.out.println(people.get(i));
	}
}

Es posible hacer uso de foreach para hacerlo pero en este ejemplo queremos mostrar el uso de IntStream y por esta razón lo compararemos con iterar sobre una lista haciendo uso de un índice.

Utilizando streams

public static void streamExample2() {
	List people = getPeople();
	IntStream.range(0, people.size()).forEach(index -> System.out.println(people.get(index)));
}

Podemos ver que con una línea de código es posible iterar sobre la lista e imprimir el resultado haciendo uso de expresiones lambda. Este ejemplo se utiliza en caso de que la lógica de tu código requiera conocer el índice en el que se encuentra, en otro caso es posible hacerlo del siguiente modo:

public static void streamExample2() {
	List people = getPeople();
	people.forEach(System.out::println);
}

El código anterior iterará sobre la lista e imprimirá cada elemento en pantalla.

En futuros posts hablaremos sobre todas las operaciones que puedes llevar a cabo haciendo uso de streams, para enterarte sobre futuros posts te recomendamos seguirnos en nuestras redes sociales: https://twitter.com/geeks_mx y https://www.facebook.com/geeksJavaMexico/.

Autor: Alejandro Agapito Bautista

Twitter: @raidentrance

Contacto:raidentrance@gmail.com

 

Java 8 Streams – Principios básicos


En este post será el primero de un conjunto de posts donde hablaremos sobre el api de Streams que se agregó en Java 8, a continuación presentaremos los conceptos básicos.

Stream

Un Stream es una abstracción que se enfoca en conjuntos y no en piezas individuales de código permitiendo realizar programación funcional en lugar de hacer programación imperativa. A continuación analizaremos un problema básico y veremos su solución utilizando programación imperativa y como se resolvería a través del uso de Streams.

Analizando el problema

A continuación evaluaremos un problemas y veremos sus soluciones utilizando programación imperativa y después su solución utilizando streams. Esto permitirá entender claramente los beneficios de la misma.

Person.java


public class Person {
	private String name;
	private String lastName;
	private Integer age;

	public Person() {

	}

	public Person(String name, String lastName, Integer age) {
		super();
		this.name = name;
		this.lastName = lastName;
		this.age = age;
	}

	public String getName() {
		return name;
	}

	public void setName(String name) {
		this.name = name;
	}

	public String getLastName() {
		return lastName;
	}

	public void setLastName(String lastName) {
		this.lastName = lastName;
	}

	public Integer getAge() {
		return age;
	}

	public void setAge(Integer age) {
		this.age = age;
	}

	@Override
	public String toString() {
		return "Person [name=" + name + ", lastName=" + lastName + ", age=" + age + "]";
	}

}

Como se puede ver esta es un POJO tradicional solo con los atributos nombre, apellido y edad, nada relacionado con streams.

Problema a resolver

Obtener a las 5 primeras personas que tengan menos de 18 años.

Solución imperativa

public void imperativeSolution() {
	List people = getPeople();
	List minor = new ArrayList();
	int counter = 0;
	for (Person person : people) {
		if (person.getAge() < = 18) {
			minor.add(person);
			counter++;
			if (counter == 5) {
				break;
			}
		}
	}
	System.out.println(minor);
}

Asumamos que existe un método llamado getPeople() que devuelve una lista de objetos de tipo Person, en el código anterior podemos ver que es necesario crear dos listas, definir un contador, iterar sobre la lista de personas, validar la edad, incrementar el counter, validar si ya tenemos 5 personas e imprimir el resultado.

Solución utilizando streams

public void streamsSolution() {
	List people = getPeople();
	List minor = people.stream().filter(person -> person.getAge() <= 18).limit(5)
			.collect(Collectors.toList());
	System.out.println(minor);
}

Antes de analizar el código a simple vista notamos que es mucho más sencillo, como podemos ver todo lo que hicimos en el método anterior se puede hacer en 3 líneas de código con lo siguiente llamamos al método getPeople(), filtramos a las personas con edad menor a 18 años, limitamos la respuesta a 5 valores, lo devolvemos como colección y lo imprimimos.

¿Cómo utilizar Streams?

Para utilizar streams debes seguir los siguientes pasos:

  1. Iniciar con una implementación concreta
    1. Arrays
    2. Set
    3. List
    4. Map
    5. etc
  2. Llamar al método stream() de la implementación, esto devolverá un stream el cual podrás utilizar en tu aplicación, recuerda que es posible concatenar multiples streams. A un conjunto de streams los llamaremos una abstracción.Captura de pantalla 2018-05-17 a las 11.40.43 a.m.
  3. Contaremos con las siguientes operaciones dentro de los streams:
    1. Filter
    2. Map
    3. Reduce
  4. El siguiente paso será ir del stream al caso concreto de nuevo, esto significa volver al Int, List, Object, Optional, etc. Algunas de las operaciones que podemos llevar a cabo para hacerlo son las siguientes:
    1. sum()
    2. collect(Collectors.toList())
    3. average()
    4. collect(Collectors.groupingBy())
    5. etc

Esto solo es el primer post sobre streams, en futuros posts hablaremos sobre todas las operaciones que puedes llevar a cabo haciendo uso de streams, para enterarte sobre futuros posts te recomendamos seguirnos en nuestras redes sociales: https://twitter.com/geeks_mx y https://www.facebook.com/geeksJavaMexico/.

Autor: Alejandro Agapito Bautista

Twitter: @raidentrance

Contacto:raidentrance@gmail.com