是否有针对Java的带有监听器的Map实现



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

标准库或任何第三方库中都有类似的内容吗?

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

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

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

这里是一个映射的工作示例,它在put和remove时触发属性更改事件。实现分为两类:

侦听器型号

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

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

您真正想要的是一个可以提供事件通知的Cache。有一些产品,比如Infinispan,已经为您提供了这些功能,但在不了解您的用例的情况下,很难推荐。

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

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

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

  • observableHashMap()静态方法创建一个Observable HashMap
  • observableMap(Map<K,V>Map)静态方法创建一个由传递到该方法中的映射实例支持的Observable转发映射

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

最新更新