2012-03-23 11 views

Odpowiedz

13

nie jestem świadomy żadnym standardzie bądź 3rd party, ale to jest proste, wystarczy utworzyć klasę, która otacza inną mapę i implementuje interfejs Mapa:

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

Zamiast 'implementuje Mapę ', myślę, że łatwiej jest użyć 'rozszerza Abstract Map '. (Ale zgadzam się na to, że utworzyłem go jako opakowanie, które przenosi się na inną mapę.) – ruakh

+3

Guava's ForwardingMap jest w zasadzie właśnie tym - przekazuje wszystkie metody delegatowi, a następnie można je przesłonić. –

+0

Tak właśnie myślałem, po prostu łowiąc kod wielokrotnego użytku przed napisaniem własnego :) –

4

Sezon do smaku. Jest to reprezentatywne, a nie normatywne. Oczywiście, że ma problemy.

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

Rozszerzenie nie jest dobre dla tego wymogu, uważam, że tworzenie opakowania dla każdej mapy jest znacznie lepsze. –

+0

@AmirPashazadeh co masz na myśli przez "opakowanie dla każdej mapy"? –

+0

Opakowanie, które akceptuje dowolną implementację mapy jako argument, więc domyślne zachowanie mapy pozostaje niezmienione. –

0

Co jesteś w istocie do ustalenia for to pamięć podręczna, która może dostarczać powiadomienia o zdarzeniach. Istnieje kilka produktów, takich jak Infinispan, które już zapewniają, że dla Ciebie, ale bez znajomości swojego przypadku użycia trudno polecić.

Jeśli potrzebujesz prostej mapy observable, powinna ona być łatwa do wdrożenia. Po prostu musisz stworzyć wzór obserwatora. Możesz znaleźć przykład: here.

+0

Infinispan będzie świetny, ale potrzebuję tego wewnątrz apletu, więc mogę dostarczyć dane do .... infinispan! Tylko dlatego, że wywołania javascript są traktowane jako niepodpisany kod i nie mogę ich dostarczyć bezpośrednio :( –

+0

Infinispan ma wbudowane i rozproszone tryby. Możesz osadzić Infinispan w swojej maszynie JVM. – uaarkoti

0

Oto działający przykład mapy, która uruchamia zdarzenia zmiany właściwości przy zakładaniu i usuwaniu. Realizacja podzielona jest na dwie klasy:

ListenerModel

Zawiera metody związane dodawanie i usuwanie słuchaczy, a także zmiany sposobu wypalania zmiany własności.

ListenerMap

Rozszerza ListenerModel i implementes interfejs java.util.Map delegowany. Wystrzeliwuje zmiany właściwości tylko w metodzie put and remove. Pożądane byłoby wywoływanie właściwości innymi metodami, np. 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(); 
    } 
} 

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

} 
Powiązane problemy