Can I change the inner structure of objects in a HashTable while iterating over it?

Like, the title says. Can I change the inner structure of objects in a HashTable while iterating over its keys? I know I cant change the Map itself, or at least that it is risky to do so, but despite google searches I haven’t found any clear or simple answer as to whether or not it is ok to change the attributes of the objects themselves in the hashmap. My gut feeling says no, since this would probably change the hash, but it would be good to know for certain. I am also interested in replacing the value for the keys while iterating over them. Is this possible?

Apologies if this has been answered a lot of times before.

To be short, will these two methods work as expected?

public class Manager {

private Hashtable<MyClassA, BufferedImage> ht1;
private Hashtable<MyClassB, JSlider> ht2;

private Image method1() {
    for(MyClassB mcb: ht2.keySet()){
       mcb.calculateStuff(ht2.get(mcb).getValue());
       //CalculateStuff() doesnt change anything, but if it takes long, the JSliders might be
       //changed by the user or a timer, resulting in a new hashCode(), and potentially problems.
    }
}

private void method2(){
   for(MyClassA mca: ht1.keySet()){
       mca.changeInnerStructureOfA(); //Changes the fields of the object mca.
       ht1.put(mca.calculateNewImage());
   }
}

Answer

It is not allowed to mutate keys of a hash-based container in any situation, not only while iterating over the container. The reason for this is that any mutation that changes the value of hash function leaves your container in an invalid state, when the hashed key is sitting in the hash bucket that does not correspond to the key’s hash value.

This is the reason behind a strong recommendation of using only immutable classes as keys in hash-based containers.

I am also interested in replacing the value for the keys while iterating over them. Is this possible?

No, this is not possible. In order to replace a key in a container with another key you need to remove the item first, and then re-insert it back with the new key. This, however, would trigger concurrent modification exception.

If you need to replace a significant number of keys, the best approach would be making a new hash container, and populate it with key-vale pairs as you iterate the original container.

If you need to replace only a small number of keys, make a list of objects describing the change (old key, new key, value), populate the list as you iterate then original container, and then walk the list of changes to make the alterations to the original container.

Leave a Reply

Your email address will not be published. Required fields are marked *