Home > Blockchain >  How can I make a new HashMap that won't change the one I've copied from?
How can I make a new HashMap that won't change the one I've copied from?

Time:03-13

So in my program, I have a HashMap called the Item Database, which stores data about all of the items. However, these items can have modifiers (in this case, the stat multiplier is important). Whenever I change an item to just one specific item drop, it ends up changing the base item from the HashMap.

For example, whenever a player creates a Katana, it does something like this.

HashMap<String, CustomItem> db = new HashMap<String, CustomItem>();
db.putAll(ItemDatabase.database);
CustomItem ci = db.get("KATANA");

From there, modifiers are applied via a getBukkitItem function on the CustomItem ci, basically multiplying a lot of the stats on that CustomItem and applying it.

baseHealth = (int) ((abbaseHealth / 100.0) * multiplier);

and other stats like that.

However, whenever I make changes to this new CustomItem, it also applies to the ItemDatabase hashmap. This means that whenever somebody makes another Katana, those multiplied stats become the new base stats to be multiplied.

Any ideas on how to solve this?

TL;DR Whenever I'm changing a variable I got from a HashMap (db), that change also applies to the HashMap (itemdb). This happens even if the HashMap (db) it's from, is a copy of another HashMap (itemdb)

Edit: So far I have tried the method above, and using .clone() on a HashMap and casting it back to HashMap. Unfortunately I'm not really sure what else to try.

CodePudding user response:

you should create a new object of deep clone. Using orika framework like below.

MapperFactory mapperFactory = new DefaultMapperFactory.Builder().build();

@Test
public void t() {

    Map<Integer, User> map = new HashMap<>();

    User one = new User();
    one.setName("one");

    System.out.println(one);
    User two = new User();
    two.setName("two");

    System.out.println(two);
    map.put(1,one);
    map.put(2,two);



    TypeBuilder<Map<Integer,User>> typeBuilder = new TypeBuilder<Map<Integer,User>>() {

    };
    Type<Map<Integer,User>> type = typeBuilder.build();
    Map<Integer,User> copyMap = mapperFactory.getMapperFacade().mapAsMap(map, type,type);

    System.out.println(copyMap.get(1));
    System.out.println(copyMap.get(2));

}

CodePudding user response:

You need to make new CustomItem instances. If you only make a copy of the Map, you’re just copying the references stored in the Map; they’ll still refer to the same CustomItem instances.

You can make this easier by adding a copy constructor or clone() method to CustomItem. Example of a copy constructor:

public class CustomItem {
    public CustomItem(CustomItem other) {
        this.name = other.name;
        this.baseHealth = other.baseHealth;
        this.multiplier = other.multiplier;
        // Don't want two instances to refer to the same List!
        this.inventoryList = new ArrayList<>(other.inventoryList);
        // etc.
    }
}

Example of a clone() method:

public class CustomItem
implements Cloneable {

    @Override
    public CustomItem clone()() {
        try {
            CustomItem copy = (CustomItem) super.clone();

            // Don't want two instances to refer to the same List!
            copy.inventoryList = new ArrayList<>(copy.inventoryList);
            // etc.

            return copy;
        } catch (CloneNotSupportedException e) {
            throw new RuntimeException(e);
        }
    }
}

Once you have a way to copy CustomItem instances, you need to use it in your new Map:

Map<String, CustomItem> newMap = new HashMap<>();

for (Map.Entry<String, CustomItem> entry : db) {
    String key = entry.getKey();
    CustomItem value = entry.getValue()

    value = value.clone();

    newMap.put(key, value);
}

A shorter way:

Map<String, CustomItem> newMap = new HashMap<>(db);
newMap.replaceAll((k, v) -> v.clone());
  • Related