Empresas
Empregos
  • Sobre nós
  • Soluções
    • Publicação de vagas
      Publique sua vaga e receba candidatos qualificados em 48h.
    • Avaliações de candidatos
      Mais de 500 testes técnicos e psicológicos, mais anti-fraude.
    • Headhunting
      Busca executiva personalizada do início ao fim.
    • Folha de Pagamento + EOR
      Dispersão de folha e EOR em mais de 15 países da LATAM.
  • Preços
  • Empregos

0

288
Visualizações
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 Respostas
Responde à pergunta

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 Relatório

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 Relatório
Responde à pergunta
Encontrar trabalhos remotos

Descubra a nova forma de encontrar um emprego!

melhores empregos
Principais categorias de trabalho
Empresas
Postar vaga Preços Comercial
Jurídico
Termos e Condições Política de privacidade
© 2026 PeakU Inc. All Rights Reserved.
Andres GPT
Recomende algumas ofertas para mim
Preciso de ajuda