0

MapConstants.java: Contains four Maps: MAP1 and it's ReversedMap, MAP2 and it's ReverseMap. The ReverseMap is created as per this answer

import com.google.common.collect.ImmutableMap;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

public class MapConstants {

    public static final ImmutableMap<String, String> MAP1 = ImmutableMap.<String, String>builder()
            .put("FD1", "FDG")
            .build();

    public static final Map<String, List<String>> REVERSE_MAP1 =
            MAP1.entrySet()
                    .stream()
                    .collect(Collectors.groupingBy(Map.Entry::getValue, Collectors.mapping(Map.Entry::getKey, Collectors.toList())));


    public static final ImmutableMap<String, String> MAP2 = ImmutableMap.<String, String>builder()
            .put("FD2", "FDG")
            .build();

    public static final Map<String, List<String>> REVERSE_MAP2 =
            MAP2.entrySet()
                    .stream()
                    .collect(Collectors.groupingBy(Map.Entry::getValue, Collectors.mapping(Map.Entry::getKey, Collectors.toList())));

}

I add the same key from both reverse Map to a new list exampleList using addAll() method.

Reproducible example - Not the exact one I'm facing, we don't use psvm but this is good representation of how to reproduce it.

import ..REVERSE_MAP1;
import ..REVERSE_MAP2;

public class Test {

    public void main(String[] args){

        System.out.println("REVERSE_MAP1 BEFORE list.addAll" + REVERSE_MAP1);
        //Returns {FDG=[FD1]}

        final List<String> exampleList = REVERSE_MAP1.get("FDG");
        exampleList.addAll(REVERSE_MAP2.get("FDG"));

        System.out.println("REVERSE_MAP1 AFTER list.addAll" + REVERSE_MAP1);
        //Returns {FDG=[FD1, FD2]}
        
    }
} 

Why does REVERSE_MAP1's list have value from REVERSE_MAP2 when I'm trying to addAll() to exampleList? My expectation is that only exampleList should change and not REVERSE_MAP1's values for FDG key.

Apologies if you're unable to reproduce it, we deploy our code on AWS so I haven't taken the trouble to create a new project with psvm as I've copy-pasted above, because when running UTCs in my existing one that calls our methods, my print statements show me that the Map appears to be changing.

ababuji
  • 1,683
  • 2
  • 14
  • 39
  • 2
    You're obtaining a reference to the list in the map and then mutating it. If you don't want that, you need to copy the list when you retrieve it. – Ingo Bürk Sep 10 '21 at 04:47
  • 3
    Do you realise that `final List exampleList = REVERSE_MAP1.get("FDG");` does not create a new copy of the list? That line literally says "`exampleList` refers to the _same object_ as `REVERSE_MAP1.get("FDG")`". – Sweeper Sep 10 '21 at 04:50
  • I see.. Do I delete my question? – ababuji Sep 10 '21 at 04:56

1 Answers1

3

This line:

final List<String> exampleList = REVERSE_MAP1.get("FDG");

obtains a reference to the list that is contained in REVERSE_MAP_1. It's not a new object. Its the same object that is still in REVERSE_MAP_1. So when you call:

exampleList.addAll(REVERSE_MAP2.get("FDG"));

You are literally adding more data to the list object in REVERSE_MAP_1. If you want a new list that contains the data from REVERSE_MAP_1 and REVERSE_MAP_2 without changing the data in either of those maps, you need to create a new list.

final List<String> exampleList = new ArrayList<>();
exampleList.addAll(REVERSE_MAP1.get("FDG"));
exampleList.addAll(REVERSE_MAP2.get("FDG"));

Edit:

I don't think your reverse maps are immutable. But even if they were, the List objects that are contained in your reverse maps are definitely not immutable. As evidenced by your example, you can easily add (or remove) data from the lists that are stored in the reverse maps.

Doug Forrest
  • 486
  • 2
  • 6