Java Convertir un List a un Map usando Stream
Tabla de contenido
En este articulo veras como convertir un list a un map utilizando para ello un Stream disponible desde Java 8.
¿Cómo convertir un List a un Map? #
Partiendo de una lista de productos creamos un Stream
public class Product {
private long id;
private String name;
private String type;
private Double price;
public Product() {
}
public Product(long id, String name, String type, double price) {
this.id = id;
this.name = name;
this.type = type;
this.price = price;
}
//...
@Override
public String toString() {
return "Product{" +
"id=" + id +
", name='" + name + '\'' +
", type='" + type + '\'' +
", price=" + price +
'}';
}
}
Creamos un Stream a partir de la lista de productos:
List<Product> productList = new ArrayList<>();
productList.add(new Product(1, "SONY", "TV", 100));
productList.add(new Product(2, "SAMSUNG", "MOBILE", 200));
Stream<Product> productStream = productList.stream();
Usar Collectors.toMap para crear un map #
Luego haces uso del Stream y “Collectors.toMap” en donde para este ejemplo usamos el id del producto como key del mapa y el nombre como valor.
// Stream to Map with Collectors.toMap
Map<Long, String> productMap = productStream.collect(Collectors.toMap(Product::getId, Product::getName));
productMap.forEach((k,v)->System.out.println("key : " + k + " value : " + v));
// output:
// key : 1 value : SONY
// key : 2 value : SAMSUNG
// key : 3 value : LG
Usar Function.identity() para guardar el objeto en el value del mapa #
Si deseas usar como valor el mismo objeto puedes hacer uso de Function.identity() para el value
Map<Long, Product> productMapObject = productList.stream().collect(Collectors.toMap(Product::getId, Function.identity()));
// output
// key : 1 value : Product{id=1, name='SONY', type='TV', price=100.0}
// key : 2 value : Product{id=2, name='SAMSUNG', type='MOBILE', price=200.0}
// key : 3 value : Product{id=3, name='LG', type='MOBILE', price=300.0}
Evitando Duplicate key #
Si en en la lista y por consiguiente en el Stream tienes dos elementos con un mismo id recibirás un IllegalStateException al tratar de crear el mapa.
// Exception
productList.add(new Product(2, "SAMSUNG DUPLICATE", "MOBILE", 200));
try {
Map<Long, String> productMapError = productList.stream().collect(Collectors.toMap(Product::getId, Product::getName));
} catch (IllegalStateException e) {
e.printStackTrace();
}
El error de salida es confuso porque muestra el valor del mapa en vez de mostrar el key que se duplica.
Exception in thread "main" java.lang.IllegalStateException: Duplicate key SAMSUNG
//..
Para resolver esto pasas una función que realice el tratamiento de los duplicados para evitar este error:
// handle duplicate ids
Map<Long, String> list =
productStream
.collect(Collectors.toMap(
Product::getId,
Product::getName,
(oldValue, newValue) -> oldValue
));
// output:
// key : 1 value : SONY
// key : 2 value : SAMSUNG
// key : 3 value : LG
Básicamente lo que haces es decidir entre los dos valores duplicados con cual deseas quedarte. (oldValue, newValue) -> oldValue
Creando un mapa ordenado #
Para crear un mapa ordenado solo debes indicarle al Stream el ordenamiento que deseas pasándole el comparador que deseas, en este ejemplo ordenamos con el key (id del producto).
// creating a ordered map
Map orderMap = productList.stream()
.sorted(Comparator.comparingLong(Product::getId).reversed())
.collect(
Collectors.toMap(
Product::getId, Product::getName,
(oldValue, newValue) -> oldValue, // if duplicate
LinkedHashMap::new // use LinkedHashMap to keep order
));
orderMap.forEach((k, v) -> System.out.println("key : " + k + " value : " + v));
// output:
// key : 3 value : LG
// key : 2 value : SAMSUNG
// key : 1 value : SONY
Conclusión: usando Stream para colectar el resultado con Collectors.toMap creas un mapa a partir de tu lista.
Luego puedes evitar duplicados pasándole una función para decidir qué hacer en caso de encontrarse con un key duplicado. .