是否有带有 Java 侦听器的 Map 实现?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/9846365/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-16 08:30:25  来源:igfitidea点击:

Is there a Map implementation with listeners for Java?

java

提问by Julio Faerman

I would like a Map implementation in which i could add listeners for put() events.

我想要一个 Map 实现,在其中我可以为 put() 事件添加侦听器。

Is there anything like that in the standard or any 3rd party libraries?

标准库或任何 3rd 方库中是否有类似的东西?

采纳答案by Amir Pashazadeh

I'm not aware of any standard or 3rd party, but it is easy, just create a class which wraps another Map and implements the Map interface:

我不知道任何标准或 3rd 方,但很容易,只需创建一个包装另一个 Map 并实现 Map 接口的类:

public class MapListener<K, V> implements Map<K, V> {

    private final Map<K, V> delegatee;

    public MapListener(Map<K, V> delegatee) {
        this.delegatee = delegatee;
    }

    // implement all Map methods, with callbacks you need.

}

回答by Will Hartung

Season to taste. This is representative, not normative. Of course it has issues.

调味。这是具有代表性的,而不是规范的。当然它有问题。

public class ListenerMap extends HashMap {

    public static final String PROP_PUT = "put";
    private PropertyChangeSupport propertySupport;

    public ListenerMap() {
        super();
        propertySupport = new PropertyChangeSupport(this);
    }

    public String getSampleProperty() {
        return sampleProperty;
    }

    @Override
    public Object put(Object k, Object v) {
        Object old = super.put(k, v);
        propertySupport.firePropertyChange(PROP_PUT, old, v);
        return old;
    }

        public void addPropertyChangeListener(PropertyChangeListener listener) {
        propertySupport.addPropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        propertySupport.removePropertyChangeListener(listener);
    }
}

回答by uaarkoti

What you are essentially asking for is a Cache which can provide event notification. There are some products out there like Infinispan that already provide that for you but without knowing your use case its hard to recommend.

您本质上要求的是一个可以提供事件通知的缓存。有一些像 Infinispan 这样的产品已经为你提供了这些,但在不知道你的用例的情况下很难推荐。

If you want a simple ObservableMap it should be easy to implement. You simply have to create an Observer pattern. You can find an example here.

如果你想要一个简单的 ObservableMap,它应该很容易实现。你只需要创建一个观察者模式。您可以在此处找到示例。

回答by gil.fernandes

Here is a working example of a map which fires property change events on put and remove. The implementation is divided in two classes:

这是一个地图的工作示例,它在放置和删除时触发属性更改事件。实现分为两类:

ListenerModel

监听器模型

Contains the methods related to adding and removing the change listeners and also a method for firing the property changes.

包含与添加和删除更改侦听器相关的方法以及触发属性更改的方法。

ListenerMap

监听器映射

Extends ListenerModel and implementes the java.util.Map interface by delegation. It fires the property changes in the put and remove method only. It would make sense to fire the properties on other methods like e.g. clear(), putAll().

扩展 ListenerModel 并通过委托实现 java.util.Map 接口。它仅触发 put 和 remove 方法中的属性更改。在其他方法上触发属性是有意义的,例如 clear()、putAll()。

ListenerModel

监听器模型

import java.beans.PropertyChangeListener;
import java.beans.PropertyChangeSupport;

public class ListenerModel {

    private final PropertyChangeSupport changeSupport = new PropertyChangeSupport(this);

    public void addPropertyChangeListener(PropertyChangeListener listener) {
        changeSupport.addPropertyChangeListener(listener);
    }

    public void removePropertyChangeListener(PropertyChangeListener listener) {
        changeSupport.removePropertyChangeListener(listener);
    }

    protected void firePropertyChange(String propertyName, Object oldValue, Object newValue) {
        changeSupport.firePropertyChange(propertyName, oldValue, newValue);
    }
}

ListenerMap

监听器映射

import java.util.*;

public class ListenerMap<K, V> extends ListenerModel implements Map<K, V> {

    public static final String PROP_PUT = "put";

    public static final String REMOVE_PUT = "remove";

    private Map<K, V> delegate = new LinkedHashMap<>();

    @Override
    public void clear() {
        delegate.clear();
    }

    @Override
    public boolean containsKey(Object key) {
        return delegate.containsKey(key);
    }

    @Override
    public boolean containsValue(Object value) {
        return delegate.containsValue(value);
    }

    @Override
    public Set<Entry<K, V>> entrySet() {
        return delegate.entrySet();
    }

    @Override
    public V get(Object key) {
        return delegate.get(key);
    }

    @Override
    public boolean isEmpty() {
        return delegate.isEmpty();
    }

    @Override
    public Set<K> keySet() {
        return delegate.keySet();
    }

    @Override
    public V put(K key, V value) {
        V oldValue = delegate.put(key, value);
        firePropertyChange(PROP_PUT, oldValue == null ? null : new AbstractMap.SimpleEntry<>(key, oldValue),
                new AbstractMap.SimpleEntry<>(key, value));
        return oldValue;
    }

    @Override
    public void putAll(Map<? extends K, ? extends V> m) {
        delegate.putAll(m);
    }

    @Override
    public V remove(Object key) {
        V oldValue = delegate.remove(key);
        firePropertyChange(REMOVE_PUT, oldValue == null ? null : new AbstractMap.SimpleEntry<>(key, oldValue),
                null);
        return oldValue;
    }

    @Override
    public int size() {
        return delegate.size();
    }

    @Override
    public Collection<V> values() {
        return delegate.values();
    }
}

Here is a JUnit 4 test:

这是一个 JUnit 4 测试:

import org.junit.Before;
import org.junit.Test;

import java.beans.PropertyChangeListener;
import java.util.Map;

import static org.hamcrest.core.Is.is;
import static org.hamcrest.core.IsNull.nullValue;
import static org.junit.Assert.assertThat;

/**
 * Created by Gil on 01/07/2017.
 */
public class ListenerMapTest {

    private ListenerMap<String, String> map;

    @Before
    public void setUp() throws Exception {
        map = new ListenerMap<>();
    }

    @Test
    public void whenPut_ShouldFireTrigger() throws Exception {
        boolean[] fired = {false};
        Map.Entry<String, String>[] listenEntry = new Map.Entry[1];
        boolean[] checkNull = {true};
        PropertyChangeListener propertyChangeListener = evt -> {
            if (ListenerMap.PROP_PUT.equals(evt.getPropertyName())) {
                if(checkNull[0]) {
                    assertThat(evt.getOldValue(), is(nullValue()));
                }
                else {
                    Map.Entry<String, String> oldValue = (Map.Entry<String, String>) evt.getOldValue();
                    assertThat(oldValue.getKey(), is("k1"));
                    assertThat(oldValue.getValue(), is("v1"));
                }
                listenEntry[0] = (Map.Entry<String, String>) evt.getNewValue();
                fired[0] = true;
            }
        };
        map.addPropertyChangeListener(propertyChangeListener);
        map.put("k1", "v1");
        assertThat(fired[0], is(true));
        assertThat(listenEntry[0].getKey(), is("k1"));
        assertThat(listenEntry[0].getValue(), is("v1"));
        checkNull[0] = false;
        map.put("k1", "v2");
    }

    @Test
    public void whenRemove_ShouldNotFire() throws Exception {
        boolean[] fired = {false};
        PropertyChangeListener propertyChangeListener = evt -> {
            fired[0] = true;
        };
        map.addPropertyChangeListener(propertyChangeListener);
        map.put("k1", "v1");
        assertThat(fired[0], is(true));
        fired[0] = false;
        map.removePropertyChangeListener(propertyChangeListener);
        map.put("k2", "v2");
        assertThat(fired[0], is(false));
    }

}