I can not understand how my data is updated in all three maps?

public void updateMap(Map<String, WagonFinalInfo> map, String wagons, String rates, String tariffs) { List<WagonRateAndTariff> listRateAndTariff = PrepareDateForInsert.fillListForUpdate(wagons, rates, tariffs); Map<String, WagonFinalInfo> oldMap = new HashMap<>(map); Map<String, WagonFinalInfo> tempNewMap = new HashMap<>(map); logger.debug("listRateAndTariff: {}", listRateAndTariff); for (Map.Entry<String, WagonFinalInfo> _map: tempNewMap.entrySet()) { for (WagonRateAndTariff list : listRateAndTariff) { if (_map.getKey().equals(list.getNumberOfWagon())) { if (_map.getValue().getRate() != (Double) list.getRate()) { _map.getValue().setRate(list.getRate()); } if (_map.getValue().getTariff() != (Double) list.getTariff()) { _map.getValue().setTariff(list.getTariff()); } } } } newMapWagonFinalInfo.putAll(tempNewMap); logger.info("newMapWagonFinalInfo: {}, oldMap: {}", newMapWagonFinalInfo, oldMap); classHandlerInsertRateOrTariff.insertDate(newMapWagonFinalInfo, oldMap); calculateYield(newMapWagonFinalInfo); } 

tempNewMap like in tempNewMap , and on the output I get updates both in map and in oldMap , which should not be. How does this happen?

  • 2
    Well, just when you create a new map based on an existing new HashMap <> (map), then all elements are put into the new map too. But these are all the same elements, not their copies - Roman Danilov
  • Yes, I did it through for-each, when put added to a new one, anyway. - Vladislav
  • one
    Well it through for-each in the designer and happens. You understand that you put links to objects, and not the objects themselves? Thus, you have the same objects in three maps - Roman Danilov
  • I understand, but I had a stupor right now, how could I get the data from the map, put it to newMap, and work with newMap? - Vladislav
  • I tried it and clone, the result is the same - Vladislav

2 answers 2

Because collections store references to objects, not objects themselves. If you create two mappings

 Map<String, SomeClass> first = new HashMap<>(); Map<String, SomeClass> second = new HashMap<>(); 

add object to first

 first.put('A', new SomeClass()); 

and then "copy" to the second

 second.put('A', first.get('A')); 

then only one object remains in the heap, and a link to this object is added to the second display. To avoid this, you need to perform a deep copy , that is, create a new object on the heap and transfer the data from the old one to it. A good practice is to entrust the function of copying an object to itself. This is usually done by declaring a copy constructor :

 class WagonFinalInfo { // Поля и другие конструкторы ... public WagonFinalInfo(WagonFinalInfo source) { setNumberOfWagons(source.getNumberOfWagons()); // Копирование остальных данных ... } // Аксессоры и другие методы ... } 

And then in the place of the code where you need to create a copy of the display

 Map<String, WagonFinalInfo> newMap = oldMap.entrySet() .stream() .collect(Collectors.toMap(Map.Entry::getKey, WagonFinalInfo::new)); 

I suspect that in your case, streams can be useful not only for creating a deep copy, but also for reducing the entire wordy method with a quadratic algorithm to a couple of lines with significantly lower memory consumption and processor cycles.

  • Sergey, thank you very much for the detailed response, very useful information. - Vladislav
  • I tried to do something for myself, did not work out ((After a change in the tempo map, the values ​​change and in the main one ((new underlines, until the designer did public WagonFinalInfo (Map.Entry <String, WagonFinalInfo> source)) {) Vladislav

I had to do it like this

 for (Map.Entry<String, WagonFinalInfo> _map: oldMap.entrySet()) { String numberWagon = new String(_map.getKey()); WagonFinalInfo wagonFinalInfo = new WagonFinalInfo( _map.getValue().getNumberOfWagon(), _map.getValue().getCountCircleDays(), _map.getValue().getDistanceEmpty(), _map.getValue().getCurrentNameOfStationOfWagon(), _map.getValue().getCurrentKeyOfStationOfWagon(), _map.getValue().getNameOfStationDepartureOfWagon(), _map.getValue().getKeyOfStationDepartureOfWagon(), _map.getValue().getRoute(), _map.getValue().getCargo(), _map.getValue().getCargoType()); tempNewMap.put(numberWagon, wagonFinalInfo); } 

I never thought that I would face this :) And the fact that copying a map is not so easy

  • 2
    For the purity of the code, you can use the copy constructor. Those. transfer the WagonFinalInfo object to your constructor, and already in the constructor do a parse of the fields, which will facilitate your method with business logic - Roman Danilov