Search code examples
dictionarykotlindelegatesobservableconcurrenthashmap

Observable on map to detect when is added, updated of deleted an entry


I have a map, in my case a ConcurrentHashMap<String, Device> which is updating when receiving some events on websocket. I want to implement an observable on this map to know when an entry is added, updated or deleted. I tried with an ObservableProperty but no methods are called when map is changed.

var deviceCache : ConcurrentHashMap<String, Device> by MyObservable(ConcurrentHashMap())

 class MyObservable<T, V>(initialValue: ConcurrentHashMap<T, V>) : ObservableProperty<ConcurrentHashMap<T, V>>(initialValue) {
override fun afterChange(property: KProperty<*>, oldValue: ConcurrentHashMap<T, V>, newValue: ConcurrentHashMap<T, V>) {
  super.afterChange(property, oldValue, newValue)
  log.e("new value is $newValue")
}

override fun beforeChange(property: KProperty<*>, oldValue: ConcurrentHashMap<T, V>, newValue: ConcurrentHashMap<T, V>): Boolean {
  log.e("beforeChange called")
  return super.beforeChange(property, oldValue, newValue)
}

}

Can anyone help me how can I solve this?


Solution

  • The problem is that Map is not a property, you can't use property delegates this way. What you have to do is to write a decorator for Map like this:

    class ObservableMap<K, V>(private val map: MutableMap<K, V>) : MutableMap<K, V> by map {
    
        override fun put(key: K, value: V): V? {
            TODO("not implemented")
        }
    
        override fun putAll(from: Map<out K, V>) {
            TODO("not implemented")
        }
    
        override fun remove(key: K): V? {
            TODO("not implemented")
        }
    
    }
    

    Here we delegate all operations to the backing map and you just only have to implement your logic when adding/deleting in the methods above.

    I'm not sure what you mean by update but if you mean "a value in the map gets overwritten" than you can handle it in put.

    You can use this ObservableMap like this:

    val map = ObservableMap(ConcurrentHashMap<String, String>())
    

    Note that if you want to support the operations of ConcurrentHashMap you also need to include overrides for AbstractMap<K,V> and ConcurrentMap<K,V> since they add some new operations you might want to track. The code above is just an example.