Java Convertir un List a un Map usando Stream

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 +
               '}';
   }
}

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