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

时间:2012-03-23 20:53:58

标签: java

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

标准或任何第三方库中是否有类似内容?

5 个答案:

答案 0 :(得分:13)

我不知道任何标准或第三方,但它很容易,只需创建一个包装另一个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.

}

答案 1 :(得分:3)

品尝季节。这是代表性的,而非规范性的。当然它有问题。

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);
    }
}

答案 2 :(得分:1)

Java 在其 JavaFX 库中有一个 javafx.collections.ObservableMap 接口。

ObservableMap 实现可以在 javafx.collections.FXCollections 类中获得。

调用者向 ObservableMap 实例注册一个 MapChangeListener 以接收 onChanged 事件。在 onChanged 事件处理程序中,可以查询 MapChangeListener.Change 方法参数以确定是否发生放置。

答案 3 :(得分:0)

您实质上要求的是可以提供事件通知的缓存。有一些产品,如Infinispan已经为您提供,但不知道您的用例很难推荐。

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

答案 4 :(得分:0)

这是一个地图的工作示例,它在put和remove上触发属性更改事件。实施分为两类:

  

ListenerModel

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

  

ListenerMap

扩展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();
    }
}

这是一个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));
    }

}