2012-03-23 11 views

Antwort

13

Ich bin nicht bekannt, dass Standard oder 3rd-Party, aber es ist einfach, erstellen Sie einfach eine Klasse, die eine andere Karte wickelt und implementiert die Karte Schnittstelle:

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. 

} 
+0

Anstatt 'implementiert Karte ', denke ich, es ist einfacher zu verwenden 'erweitert abstrakte Karte '. (Aber ich stimme zu, es als einen Wrapper zu strukturieren, der auf eine andere Karte delegiert.) – ruakh

+3

Guavas ForwardingMap ist im Grunde genau das - es leitet alle Methoden an den Delegaten weiter, und dann können Sie sie überschreiben, um zu schmecken. –

+0

Das ist, was ich dachte, nur Angeln für wiederverwendbaren Code vor dem Schreiben meiner eigenen :) –

4

Saison nach Geschmack. Dies ist repräsentativ, nicht normativ. Natürlich hat es Probleme.

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

Erweiterung ist keine gute Sache für diese Anforderung, ich glaube, ein Wrapper für jede Karte ist viel besser. –

+0

@AmirPashazadeh Was meinst du mit "Wrapper für jede Karte"? –

+0

Ein Wrapper, der jede Map-Implementierung als Argument akzeptiert, sodass das Standardverhalten der Map unverändert bleibt. –

0

Was Sie im Wesentlichen fragen für ist ein Cache, der Ereignisbenachrichtigung bereitstellen kann. Es gibt einige Produkte da draußen wie Infinispan, die das bereits für Sie bereitstellen, aber ohne zu wissen, dass Ihr Anwendungsfall schwer zu empfehlen ist.

Wenn Sie eine einfache ObservableMap möchten, sollte es einfach zu implementieren sein. Sie müssen lediglich ein Beobachtermuster erstellen. Sie finden ein Beispiel here.

+0

Infinispan wäre großartig, aber ich brauche das in einem Applet, damit ich Daten an .... Infinispan liefern kann! Nur weil JavaScript-Aufrufe als nicht signierter Code behandelt werden, kann ich nicht direkt liefern :( –

+0

Infinispan hat sowohl eingebettete als auch verteilte Modi. Sie können Infinispan in Ihre JVM einbetten. – uaarkoti

0

Hier ist ein Arbeitsbeispiel für eine Map, die Eigenschaftenänderungsereignisse bei put und remove auslöst. Die Umsetzung erfolgt in zwei Klassen eingeteilt:

ListenerModel

Enthält die damit verbundenen Methoden zum Hinzufügen und Entfernen der Änderung Zuhörer und auch ein Verfahren zum Brennen der Eigenschaft ändert.

ListenerMap

Verlängert ListenerModel und implementes die java.util.Map Schnittstelle durch Delegation. Es löst nur die Eigenschaftenänderungen in der put- und remove-Methode aus. Es wäre sinnvoll, die Eigenschaften auf andere Methoden wie z.B. 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(); 
    } 
} 

Hier ist ein JUnit 4 Test:

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

} 
Verwandte Themen