如何正确地向 Map.Entry 添加值更改侦听器?
How to properly add a value change listener to Map.Entry?
我目前正在进行的一个项目要求我确保在遍历地图条目时,如果调用 Entry.setValue,它将触发值更改事件。我看到我可以尝试在 .put 方法上将侦听器添加到 Map class 的扩展中。我的问题是,正在更改的条目是否会触发地图的 put 方法中的侦听器?或者我会被迫扩展 Map.Entry class 并将侦听器逻辑粘贴到它的 setValue 方法中吗?
如果这个问题很愚蠢,请提前致歉 - 我是第一次以这种方式使用地图,到目前为止我看到的很多信息只会导致扩展地图本身,这似乎最简单但我不知道它是否会涵盖我的情况。
Map.Entry
只是一个接口,因此当然可以编写它的实现来触发值更改事件到注册到它的 EventListener
实例或拥有 Map
个实例。
但我看不出有什么方法可以说服接口 Map
的任何现有实现使用 Entry
的新实现…
所以你最终会创建自己的 Map
实现…
或者,当您需要更改侦听器时 仅在对条目集进行迭代 时,您可以只覆盖 [=13] 的适当实现的方法 Map.entrySet()
=] 它将 return 一个 Set
的调整版本——调整的方式是方法 Set.iterator()
return 是一个 Iterator
实例,其方法 Iterator.next()
return 是您对 Map.Entry
的实现(包装原始的并委托给它的方法......)其 setValue()
方法将触发值更改侦听器。
像这样:
public class MyMapEntry<K,V> implements Map.Entry<K,V>
{
private final Map.Entry<K,V> m_Wrapped;
private final ChangeListener m_Listener;
public MyMapEntry( ChangeListener listener, Map.Entry<K,V> entry )
{
m_Listener = listener;
m_Wrapped = entry;
}
@Override
public void setValue( V value )
{
var event = new ChangeEvent( this, getValue(), value );
m_Wrapped.setValue( value );
m_Listener.sendEvent( event );
}
… // Implement the other methods
}
public class MyIterator<Map.Entry<K,V>> implements Iterator<Map.Entry<K,V>>
{
private final Iterator<Map.Entry<K,V>> m_Iterator;
private final ChangeListener m_Listener;
public MyIterator( final ChangeListener listener, final Iterator<Map.Entry<K,V>> iterator )
{
m_Listener = listener;
m_Iterator = iterator;
}
public Map.Entry<K,V> next()
{
return new MyMapEntry( m_Listener, m_Iterator.next() );
}
… // Delegation for all the other methods
}
public class MySet<Map.Entry<K,V>> extends HashSet<Map.Entry<K,V>>
{
private final ChangeListener m_Listener;
public MySet( ChangeListener listener, Set<Map.Entry<K,V>> other )
{
super( other );
m_Listener = listener;
}
@Override
public Iterator<Map.Entry<K,V>> iterator()
{
return new MyIterator( m_Listener, super.iterator() );
}
}
public class MyMap<K,V> extends HashMap<K,V>
{
private final ChangeListener m_Listener;
public MyMap( ChangeListener listener )
{
super();
m_Listener = listener;
}
@Override
public Set<Map.Entry<K,V>> entrySet()
{
return new MySet( listener, super.entrySet() );
}
}
这段代码只是一个草图,在运行之前可能需要一些修复!
您复制 MyMap.entrySet()
中的条目集会将您的条目集与原始条目集分离;对地图 结构 的更改将不再反映到您的集合中 – 但对键值和值值的更改仍然会影响您的集合。
在紧要关头,您可以使用 PropertyChangeSupport
class。它使管理 propertyChanges
变得非常容易。这里没什么好说的。想要监听变化的各方向地图注册他们的监听器。然后当映射修改值时,支持向所有侦听器触发事件。事件 class 中返回的值可能会更改为选择的值。
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);
}
版画
source = map
oldValue = null
newValue = 20
source = map
oldValue = 20
newValue = 99
source = map
oldValue = null
newValue = 44
A=44
B=99
用于演示的监听器。
public void propertyChange(PropertyChangeEvent pce) {
System.out.println("source = " + pce.getPropertyName());
System.out.println("oldValue = " + pce.getOldValue());
System.out.println("newValue = " + pce.getNewValue());
}
}
修改后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;
}
}
注意:这个简单的解决方案可能遗漏了一些问题。投入生产使用前应进行测试。首先,有许多不同的方法可以设置 Entry's
值。这仅在 put
被用户或地图本身间接调用时执行。
我目前正在进行的一个项目要求我确保在遍历地图条目时,如果调用 Entry.setValue,它将触发值更改事件。我看到我可以尝试在 .put 方法上将侦听器添加到 Map class 的扩展中。我的问题是,正在更改的条目是否会触发地图的 put 方法中的侦听器?或者我会被迫扩展 Map.Entry class 并将侦听器逻辑粘贴到它的 setValue 方法中吗?
如果这个问题很愚蠢,请提前致歉 - 我是第一次以这种方式使用地图,到目前为止我看到的很多信息只会导致扩展地图本身,这似乎最简单但我不知道它是否会涵盖我的情况。
Map.Entry
只是一个接口,因此当然可以编写它的实现来触发值更改事件到注册到它的 EventListener
实例或拥有 Map
个实例。
但我看不出有什么方法可以说服接口 Map
的任何现有实现使用 Entry
的新实现…
所以你最终会创建自己的 Map
实现…
或者,当您需要更改侦听器时 仅在对条目集进行迭代 时,您可以只覆盖 [=13] 的适当实现的方法 Map.entrySet()
=] 它将 return 一个 Set
的调整版本——调整的方式是方法 Set.iterator()
return 是一个 Iterator
实例,其方法 Iterator.next()
return 是您对 Map.Entry
的实现(包装原始的并委托给它的方法......)其 setValue()
方法将触发值更改侦听器。
像这样:
public class MyMapEntry<K,V> implements Map.Entry<K,V>
{
private final Map.Entry<K,V> m_Wrapped;
private final ChangeListener m_Listener;
public MyMapEntry( ChangeListener listener, Map.Entry<K,V> entry )
{
m_Listener = listener;
m_Wrapped = entry;
}
@Override
public void setValue( V value )
{
var event = new ChangeEvent( this, getValue(), value );
m_Wrapped.setValue( value );
m_Listener.sendEvent( event );
}
… // Implement the other methods
}
public class MyIterator<Map.Entry<K,V>> implements Iterator<Map.Entry<K,V>>
{
private final Iterator<Map.Entry<K,V>> m_Iterator;
private final ChangeListener m_Listener;
public MyIterator( final ChangeListener listener, final Iterator<Map.Entry<K,V>> iterator )
{
m_Listener = listener;
m_Iterator = iterator;
}
public Map.Entry<K,V> next()
{
return new MyMapEntry( m_Listener, m_Iterator.next() );
}
… // Delegation for all the other methods
}
public class MySet<Map.Entry<K,V>> extends HashSet<Map.Entry<K,V>>
{
private final ChangeListener m_Listener;
public MySet( ChangeListener listener, Set<Map.Entry<K,V>> other )
{
super( other );
m_Listener = listener;
}
@Override
public Iterator<Map.Entry<K,V>> iterator()
{
return new MyIterator( m_Listener, super.iterator() );
}
}
public class MyMap<K,V> extends HashMap<K,V>
{
private final ChangeListener m_Listener;
public MyMap( ChangeListener listener )
{
super();
m_Listener = listener;
}
@Override
public Set<Map.Entry<K,V>> entrySet()
{
return new MySet( listener, super.entrySet() );
}
}
这段代码只是一个草图,在运行之前可能需要一些修复!
您复制 MyMap.entrySet()
中的条目集会将您的条目集与原始条目集分离;对地图 结构 的更改将不再反映到您的集合中 – 但对键值和值值的更改仍然会影响您的集合。
在紧要关头,您可以使用 PropertyChangeSupport
class。它使管理 propertyChanges
变得非常容易。这里没什么好说的。想要监听变化的各方向地图注册他们的监听器。然后当映射修改值时,支持向所有侦听器触发事件。事件 class 中返回的值可能会更改为选择的值。
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);
}
版画
source = map
oldValue = null
newValue = 20
source = map
oldValue = 20
newValue = 99
source = map
oldValue = null
newValue = 44
A=44
B=99
用于演示的监听器。
public void propertyChange(PropertyChangeEvent pce) {
System.out.println("source = " + pce.getPropertyName());
System.out.println("oldValue = " + pce.getOldValue());
System.out.println("newValue = " + pce.getNewValue());
}
}
修改后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;
}
}
注意:这个简单的解决方案可能遗漏了一些问题。投入生产使用前应进行测试。首先,有许多不同的方法可以设置 Entry's
值。这仅在 put
被用户或地图本身间接调用时执行。