Skip to content

Instantly share code, notes, and snippets.

@ZakTaccardi
Created October 14, 2016 20:59
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ZakTaccardi/8d5a42c029ae2580e711de05625f1d4a to your computer and use it in GitHub Desktop.
Save ZakTaccardi/8d5a42c029ae2580e711de05625f1d4a to your computer and use it in GitHub Desktop.
A basic RxMap
/**
* A reactive map implementation that allows you to observe a key and its value.
*/
class RxMap<K, V>(
private val map: AbstractMap<K, V> = HashMap(),
private val rxMap: AbstractMap<K, Relay<V, V>> = HashMap()
) : Map<K, V> {
/**
* @return a hot observable that emits the value for the specified key. When you subscribe,
* the current value in the map for the key will be emitted.
*/
@Synchronized fun observe(key: K): Observable<V> {
return rxMap.getOrPut(key, createObservable(key)
)
}
private fun createObservable(key: K): () -> Relay<V, V> {
return {
BehaviorRelay.create<V>().toSerialized()
}
}
override fun containsKey(key: K): Boolean {
return map.containsKey(key)
}
override fun containsValue(value: V): Boolean {
return map.containsValue(value)
}
override val entries: Set<Map.Entry<K, V>> = map.entries
override fun get(key: K): V? {
return map[key]
}
override fun isEmpty(): Boolean {
return map.isEmpty()
}
override val keys: Set<K> = map.keys
override val size: Int = map.size
override val values: Collection<V> = map.values
@Synchronized fun put(key: K, value: V) {
map.put(key, value)
rxMap.getOrPut(key, createObservable(key)).call(value)
}
@Synchronized fun put(mapEntry: MapEntry<K, V>) {
put(mapEntry.key, mapEntry.value)
}
@Synchronized fun remove(key: K): V? {
val removed = map.remove(key)
if (removed != null) {
rxMap[key]!!.call(null)
}
return removed
}
/**
* Preferred over [Map.Entry] because we get equals()/hashCode() for free
*/
data class MapEntry<out K, out V>(val key: K, val value: V)
}
/**
* Test for [RxMap]
*/
class RxMapTest {
lateinit var map: RxMap<String, Int>
lateinit var subscriber: TestSubscriber<Int?>
lateinit var expected: MutableList<Int?>
@Before
fun setUp() {
map = RxMap(HashMap())
subscriber = TestSubscriber()
expected = mutableListOf<Int?>()
}
@Test
fun verifyMapEmitsOnInsert() {
val emission1 = MapEntry(key = "5", value = 10)
val emission2 = MapEntry(key = emission1.key, value = 42)
assertEquals(emission1, emission1)
assertNotEquals(emission1, emission2)
map.put(emission1)
expected.add(emission1.value)
map.observe(emission1.key)
.subscribe(subscriber)
assertExpected()
map.remove(emission1.key)
expected.add(null)
assertExpected()
map.put(emission2)
expected.add(emission2.value)
assertExpected()
assertThat(subscriber)
.hasNoTerminalEvent()
}
private fun assertExpected() {
assertThat(subscriber)
.hasReceivedValues(expected)
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment