How to properly add a value change listener to Map.Entry?

Tags: , , ,



A project im working on currently requires me to ensure that while looping through a map’s entries, if Entry.setValue is called, it would trigger a value change event. I see I can try something like adding a listener into an extension of the Map class on the .put method. My question is, would entries being changed trigger a listener in the map’s put method? Or would I be forced to extend the Map.Entry class and stick listener logic into its setValue method?

Apologies in advance if this question is dumb – im new to using Maps in this way and a lot of the information ive seen so far has only lead to extending the Map itself, which seems easiest but i dont know if it would cover my case.

Answer

In a pinch you could use the PropertyChangeSupport class. It makes managing propertyChanges very easy. There’s not much to tell here. The parties that want to listen for changes register their listener with the map. Then when the map modifies the value, the support fires off an event to all the listeners. The values that are returned in the Event class may be altered to ones chosing.

import java.beans.PropertyChangeEvent;
import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;
import java.util.HashMap;

public class PropertyChangeDemo implements PropertyChangeListener {
    
    public static void main(String[] args) {
        // leave static context of main
        new PropertyChangeDemo().start();
    }
    public void start() {
        MyMap<String,Integer> map = new MyMap<>();
        map.addMapListener(this);
        map.put("B",20);
        map.put("B",99);
        map.put("A",44);
        
        map.entrySet().forEach(System.out::println);
    }

Prints

source = map
oldValue = null
newValue = 20
source = map
oldValue = 20
newValue = 99
source = map
oldValue = null
newValue = 44
A=44
B=99

A listener for demonstration.

    public void propertyChange(PropertyChangeEvent pce) {
        System.out.println("source = " + pce.getPropertyName());
        System.out.println("oldValue = " + pce.getOldValue());
        System.out.println("newValue = " + pce.getNewValue());
    }
    
}

The modified class

class MyMap<K,V> extends HashMap<K,V> {
        
    private PropertyChangeSupport ps = new PropertyChangeSupport(this);

    // method to add listener
    public void addMapListener(PropertyChangeListener pcl) {
        ps.addPropertyChangeListener(pcl);
    }
    
    @Override 
     public V put(K key, V value) {
        V ret = super.put(key,value);
        ps.firePropertyChange("map", ret, value);
        return ret;
    }
}   

Note: There may be issues that have been missed in this simple solution. Testing should be conducted before put into production use. For one, there are many different ways to set an Entry's value. This only does it when put is invoked, either by the user or indirectly by the map itself.



Source: stackoverflow