I have a class that describes a city, this class has three fields one of which is the country code
public class Ciudad{
private String codigoPais;
private String codigo;
private String nombre;
//getters y setters
}
I have a method that from one lista
of Cities builds a mapa
where the key ( key
) is the country code and the value ( value
) is one mapa
of the cities that belong to the same country
public class MapStream {
public static void main(String[] args) {
List<Ciudad> ciudades = new ArrayList<>();
ciudades.add(new Ciudad("57", "1", "Bogota"));
ciudades.add(new Ciudad("57", "2", "Medellin"));
ciudades.add(new Ciudad("58", "3", "Bucaramanga"));
ciudades.add(new Ciudad("58", "4", "Cali"));
ciudades.add(new Ciudad("59", "1", "Pereira"));
ciudades.add(new Ciudad("59", "2", "Ibague"));
ciudades.add(new Ciudad("70", "10", "Cartagena"));
Map<String, Map<String, String>> mapCiudades = construirMapaCiudades(ciudades);
}
public static Map<String, Map<String, String>> construirMapaCiudades(List<Ciudad> ciudades){
Map<String, Map<String, String>> map = new HashMap<>();
Map<String, String> tempCiudades;
for(Ciudad ciudad : ciudades){
tempCiudades = !map.containsKey(ciudad.getCodigoPais()) ? new HashMap<>() : map.get(ciudad.getCodigoPais());
tempCiudades.put(ciudad.getCodigo(), ciudad.getNombre());
map.put(ciudad.getCodigoPais(), tempCiudades);
}
return map;
}
}
The output of this code is the following
{57={1=Bogota, 2=Medellin}, 58={3=Bucaramanga, 4=Cali}, 59={1=Pereira, 2=Ibague}, 70={10=Cartagena}}
As can be seen, there is a country (57) with a map of two cities.
I have looked at examples where from the lista
of cities a is built Map<String, String>
usingstream
Map<String, String> mapCiudades = ciudades.stream.collect(Collectors.toMap(Ciudad::getCodigo, Ciudad::getNombre));
My question is, is there a way to simplify the method code by construirMapaCiudades
making use of stream
and having the same output.
You can simplify the logic a bit by using collectors and the static factory of the Map interface as follows:
The collector receives two
Function
, one for the creation of the key and another for the creation of the value, here we simplify as Function is a functional interface we use the lambda notation in both cases.Regarding the factory methods, as in the case of
Map.of
, I recommend you review this other answer of mine about it .Both a and b refer to the current item in the city list.
This implementation assumes that there are no duplicate countries.
With duplicate countries
Assuming there are duplicate countries as in your example, we would proceed to use a grouping collector, to merge the duplicate countries first, specifying that we want to group by country code.
So that the entire entity does not remain on the right side of the map, we pass another collector as a second parameter that will serve us as if it were a typical mapper. Our second parameter must be a collector and not a function like in the previous case; This second collector is called the downstream collector , and the Oracle tutorials define it as:
Do not try to replace it with the function
(b) -> Map.of(b.getCodigo(), b.getNombre()))
or you will get an error.So we could leave your program in:
As you might expect the output will always be the same in both methods producing: