Empresas
Empleos
  • Sobre nosotros
  • Soluciones
    • Publicación de vacantes
      Publica tu vacante y recibe candidatos calificados en 48h.
    • Evaluación de candidatos
      500+ pruebas técnicas y psicológicas, más anti-fraude.
    • Headhunting
      Búsqueda ejecutiva a la medida de principio a fin.
    • Nómina + EOR
      Dispersión de nómina y EOR en más de 15 países de LATAM.
  • Precios
  • Empleos

0

289
Vistas
How to combine Map values from parent Map with java 8 stream

I have a map inside a map which looks like this:

Map<String, Map<Integer, BigDecimal>> mapInMap; //--> with values like: 
/*
"a": (1: BigDecimal.ONE),
"b": (2: BigDecimal.TEN),
"c": (1: BigDecimal.ZERO)
*/

And I would like to combine the inner maps by expecting the following result:

Map<Integer, BigDecimal> innerMapCombined; //--> with values:
/*
1: BigDecimal.ZERO,
2: BigDecimal.TEN
*/

This is my solution with predefining the combined map and using forEach:

Map<Integer, BigDecimal> combined = new HashMap<>();
mapInMap.forEach((str, innerMap) -> {
  innerMap.forEach(combined::putIfAbsent);
});

But this will ignore (1: BigDecimal.ZERO).

Could you provide a 1-line solution with java 8 stream?

over 4 years ago · Santiago Trujillo
2 Respuestas
Responde la pregunta

0

The issue with your problem is that as soon as you initialize your maps, and add the duplicate keys on the inner maps, you will rewrite those keys, since those maps do not accept duplicated keys. Therefore, you need to first change this:

Map<String, Map<Integer, BigDecimal>> mapInMap;

to a Map that allows duplicated keys, for instance Multimap from Google Guava:

Map<String, Multimap<Integer, BigDecimal>> mapInMap = new HashMap<>();

where the inner maps are created like this:

 Multimap<Integer, BigDecimal> x1 = ArrayListMultimap.create();
 x1.put(1, BigDecimal.ONE);
 mapInMap.put("a", x1);

Only now you can try to solve your problem using Java 8 Streams API. For instance:

Map<Integer, BigDecimal> map = multiMap.values()
                                       .stream()
                                       .flatMap(map -> map.entries().stream())
                                       .collect(Collectors.toMap(Map.Entry::getKey,
                                                                 Map.Entry::getValue, 
                                                                 (v1, v2) -> v2));

The duplicate keys conflicts are solved using mergeFunction parameter of the toMap method. We explicitly express to take the second value (v1, v2) -> v2 in case of duplicates.

over 4 years ago · Santiago Trujillo Denunciar

0

Problem:

To address why your current solution doesn't work is because Map#putIfAbsent method only adds and doesn't replace a value in a map if is already present.


Solution using for-each:

Map#put is a way to go, however its limitation is that you cannot decide whether you want to keep always the first value for such key, calculate a new one or use always the last value. For such reason I recommend to use either a combination of Map#computeIfPresent and Map#putIfAbsent or better a method that does all that at once which is Map#merge(K, V, BiFunction) with a BiFunction remappingFunction:

remappingFunction - the function to recompute a value if present

Map<Integer, BigDecimal> resultMap = new HashMap<>();
for (Map<Integer, BigDecimal> map: mapInMap.values()) {
    for (Map.Entry<Integer, BigDecimal> entry: map.entrySet()) {
         resultMap.merge(entry.getKey(), entry.getValue(), (l, r) -> r);
    }
}

Solution using Stream API:

To rewrite it in the Stream-alike solution, the approach would be identical. The only difference is the declarative syntax of Stream API, however, the idea is very same.

Just flatMap the structure and collect to a map with a Collector.toMap(Function, Function, BinaryOperator using BinaryOperator mergeFunction to merge duplicated keys.

mergeFunction - a merge function, used to resolve collisions between values associated with the same key, as supplied to Map.merge(Object, Object, BiFunction)

Map<Integer, BigDecimal> resultMap = mapInMap.values().stream()
    .flatMap(entries -> entries.entrySet().stream())
    .collect(Collectors.toMap( Map.Entry::getKey, Map.Entry::getValue, (l, r) -> r));

Note: @dreamcrash also deserves a credit for his good Stream API answer in terms of speed.


Result:

{1=1, 2=10} is the result when you pring out such map (note that BigDecimal is printed as a number). This output matches your expected output.

1=BigDecimal.ZERO
2=BigDecimal.TEN

Notice the similarities between Map#merge(K, V, BiFunction) and Collector.toMap(Function, Function, BinaryOperator that use a very similar approach to the same result.

over 4 years ago · Santiago Trujillo Denunciar
Responde la pregunta
Encuentra empleos remotos

¡Descubre la nueva forma de encontrar empleo!

Top de empleos
Top categorías de empleo
Empresas
Publicar vacante Precios Comercial
Legal
Términos y condiciones Política de privacidad
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomiéndame algunas ofertas
Necesito ayuda