↓Ir al contenido

Java Convertir un List a un Map usando Stream

·3 mins

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. .

Mira este código completo en Github #