Skip to content

Commit fab87aa

Browse files
committed
Extremely dirty implementation of immutable ordered map.
1 parent a3909f5 commit fab87aa

File tree

3 files changed

+238
-3
lines changed

3 files changed

+238
-3
lines changed
Lines changed: 210 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,210 @@
1+
package kotlinx.collections.immutable
2+
3+
import org.pcollections.HashTreePMap
4+
import org.pcollections.PMap
5+
import org.pcollections.PVector
6+
import org.pcollections.TreePVector
7+
import java.util.*
8+
9+
10+
internal class ImmutableOrderedMap<K, out V> private constructor(private val impl: PMap<K, V>, private val order: PVector<Map.Entry<K ,V>>) : ImmutableMap<K, V> {
11+
12+
override val size: Int get() = impl.size
13+
override fun isEmpty(): Boolean = impl.isEmpty()
14+
override fun containsKey(key: K): Boolean = impl.containsKey(key)
15+
override fun containsValue(value: @UnsafeVariance V): Boolean = impl.containsValue(value)
16+
17+
override fun get(key: K): V? = impl.get(key)
18+
19+
override fun equals(other: Any?): Boolean = impl.equals(other)
20+
override fun hashCode(): Int = impl.hashCode()
21+
override fun toString(): String = impl.toString()
22+
23+
24+
// should it be immutable set/collection or just read-only?
25+
private var _keys: Set<K>? = null
26+
final override val keys: Set<K> get() = _keys ?: createKeys().apply { _keys = this }
27+
private fun createKeys(): Set<K> = OrderedKeySet()
28+
29+
private var _values: Collection<V>? = null
30+
final override val values: Collection<V> get() = _values ?: createValues().apply { _values = this }
31+
private fun createValues(): Collection<V> = OrderedValueCollection()
32+
33+
private var _entries: Set<Map.Entry<K, V>>? = null
34+
final override val entries: Set<Map.Entry<K, V>> get() = _entries ?: createEntries().apply { _entries = this }
35+
private fun createEntries(): Set<Map.Entry<K, V>> = OrderedEntrySet()
36+
37+
override fun put(key: K, value: @UnsafeVariance V): ImmutableMap<K, V> = wrap(impl.plus(key, value)) { order.addOrReplace(key, value) }
38+
override fun putAll(m: Map<out K, @UnsafeVariance V>): ImmutableMap<K, V> {
39+
var newImpl = impl
40+
var newOrder = order
41+
for ((k, v) in m) {
42+
newImpl.plus(k, v).let { if (it != newImpl) {
43+
newImpl = it
44+
newOrder = newOrder.addOrReplace(k, v)
45+
}}
46+
}
47+
return wrap(newImpl) { newOrder }
48+
}
49+
override fun remove(key: K): ImmutableMap<K, V> = wrap(impl.minus(key), { order.minus(order.indexOfFirst { it.key == key })})
50+
override fun remove(key: K, value: @UnsafeVariance V): ImmutableMap<K, V>
51+
= if (!impl.contains(key, value)) this else remove(key)
52+
53+
override fun clear(): ImmutableMap<K, V> = emptyOf()
54+
55+
override fun builder(): ImmutableMap.Builder<K, @UnsafeVariance V> = Builder(this, impl, order)
56+
57+
private fun entry(key: K, value: @UnsafeVariance V): Map.Entry<K, V> = AbstractMap.SimpleEntry(key, value)
58+
59+
protected fun wrap(impl: PMap<K, @UnsafeVariance V>, order: (PVector<Map.Entry<K, @UnsafeVariance V>>) -> PVector<Map.Entry<K, @UnsafeVariance V>>): ImmutableOrderedMap<K, V> {
60+
return if (impl === this.impl) this else ImmutableOrderedMap(impl, order(this.order))
61+
}
62+
63+
64+
protected class Builder<K, V>(protected var value: ImmutableOrderedMap<K, V>, protected var impl: PMap<K, V>, protected var order: PVector<Map.Entry<K, V>>) : ImmutableMap.Builder<K, V>, AbstractMap<K, V>() {
65+
override fun build(): ImmutableMap<K, V> = value.wrap(impl, { order }).apply { value = this }
66+
67+
override val size: Int get() = impl.size
68+
override fun isEmpty(): Boolean = impl.isEmpty()
69+
override fun containsKey(key: K): Boolean = impl.containsKey(key)
70+
override fun containsValue(value: @UnsafeVariance V): Boolean = impl.containsValue(value)
71+
72+
override fun get(key: K): V? = impl.get(key)
73+
74+
override fun equals(other: Any?): Boolean = impl.equals(other)
75+
override fun hashCode(): Int = impl.hashCode()
76+
override fun toString(): String = impl.toString()
77+
78+
private var entrySet: MutableSet<MutableMap.MutableEntry<K, V>>? = null
79+
override val entries: MutableSet<MutableMap.MutableEntry<K, V>>
80+
get() = entrySet ?: object : MutableSet<MutableMap.MutableEntry<K, V>>, AbstractSet<MutableMap.MutableEntry<K, V>>() {
81+
override val size: Int get() = impl.size
82+
override fun isEmpty(): Boolean = impl.isEmpty()
83+
override fun clear() = this@Builder.clear()
84+
override fun contains(element: MutableMap.MutableEntry<K, V>): Boolean
85+
= impl.contains(element.key, element.value)
86+
87+
override fun remove(element: MutableMap.MutableEntry<K, V>): Boolean {
88+
if (contains(element)) {
89+
this@Builder.remove(element.key)
90+
return true
91+
} else {
92+
return false
93+
}
94+
}
95+
96+
override fun iterator() = object : MutableIterator<MutableMap.MutableEntry<K, V>> {
97+
private var snapshot = impl
98+
private val iterator = order.iterator()
99+
private var entry: Map.Entry<K,V>? = null
100+
101+
override fun hasNext(): Boolean = iterator.hasNext()
102+
override fun next(): MutableMap.MutableEntry<K, V> {
103+
checkForComodification()
104+
val entry = iterator.next()
105+
this.entry = entry
106+
return object : MutableMap.MutableEntry<K, V>, Map.Entry<K, V> by entry {
107+
override fun setValue(newValue: V): V {
108+
checkForComodification()
109+
val oldValue = put(entry.key, newValue) as V
110+
snapshot = impl
111+
return oldValue
112+
}
113+
}
114+
}
115+
116+
override fun remove() {
117+
checkNotNull(entry)
118+
checkForComodification()
119+
this@Builder.remove(entry!!.key)
120+
entry = null
121+
snapshot = impl
122+
}
123+
124+
protected fun checkForComodification() {
125+
if (snapshot !== impl) throw ConcurrentModificationException()
126+
}
127+
}
128+
}.apply { entrySet = this }
129+
130+
// by AbstractMap
131+
// override val keys: MutableSet<K>
132+
// override val values: MutableCollection<V>
133+
134+
override val values: MutableCollection<V>
135+
get() = super.values
136+
override val keys: MutableSet<K>
137+
get() = super.keys
138+
139+
override fun clear() {
140+
mutate(HashTreePMap.empty(), { TreePVector.empty() })
141+
}
142+
143+
override fun put(key: K, value: V): V? =
144+
get(key).apply {
145+
mutate(impl.plus(key, value), { order.addOrReplace(key, value) })
146+
}
147+
148+
override fun putAll(from: Map<out K, V>) {
149+
for ((k, v) in from) put(k, v)
150+
}
151+
152+
153+
override fun remove(key: K): V?
154+
= get(key).apply { mutate(impl.minus(key), { it.minus(it.indexOfFirst { it.key == key })}) }
155+
156+
protected inline fun mutate(newValue: PMap<K, V>, orderOperation: (PVector<Map.Entry<K, V>>) -> (PVector<Map.Entry<K, V>>) ): Boolean {
157+
if (newValue !== impl) {
158+
order = orderOperation(order)
159+
impl = newValue
160+
return true
161+
}
162+
return false
163+
}
164+
165+
}
166+
167+
companion object {
168+
private val EMPTY = ImmutableOrderedMap(HashTreePMap.empty<Any?, Nothing>(), TreePVector.empty())
169+
170+
@Suppress("UNCHECKED_CAST")
171+
fun <K, V> emptyOf(): ImmutableOrderedMap<K, V> = EMPTY as ImmutableOrderedMap<K, V>
172+
}
173+
174+
175+
private inner class OrderedEntrySet : Set<Map.Entry<K, V>> {
176+
override val size: Int get() = impl.size
177+
override fun contains(element: Map.Entry<K, @UnsafeVariance V>): Boolean = impl.entries.contains(element)
178+
override fun containsAll(elements: Collection<Map.Entry<K, @UnsafeVariance V>>): Boolean = impl.entries.containsAll(elements)
179+
override fun isEmpty(): Boolean = impl.isEmpty()
180+
override fun iterator(): Iterator<Map.Entry<K, V>> = order.iterator()
181+
}
182+
183+
private inner class OrderedKeySet : Set<K> {
184+
override val size: Int get() = impl.size
185+
override fun contains(element: K): Boolean = impl.containsKey(element)
186+
override fun containsAll(elements: Collection<K>): Boolean = impl.keys.containsAll(elements)
187+
188+
override fun isEmpty(): Boolean = impl.isEmpty()
189+
190+
private val mapped = order.asSequence().map { it.key }
191+
override fun iterator(): Iterator<K> = mapped.iterator()
192+
}
193+
194+
private inner class OrderedValueCollection : Collection<V> {
195+
override val size: Int get() = impl.size
196+
override fun contains(element: @UnsafeVariance V): Boolean = impl.containsValue(element)
197+
override fun containsAll(elements: Collection<@UnsafeVariance V>): Boolean = impl.values.containsAll(elements)
198+
override fun isEmpty(): Boolean = impl.isEmpty()
199+
200+
private val mapped = order.asSequence().map { it.value }
201+
override fun iterator(): Iterator<V> = mapped.iterator()
202+
}
203+
}
204+
205+
private fun <K, V> PVector<Map.Entry<K, V>>.addOrReplace(key: K, value: V): PVector<Map.Entry<K, V>> {
206+
val index = indexOfFirst { it.key == key }
207+
val entry = AbstractMap.SimpleEntry(key, value)
208+
return if (index >= 0) with(index, entry) else plus(entry)
209+
}
210+

kotlinx-collections-immutable/src/main/kotlin/kotlinx/collections/immutable/extensions.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -65,6 +65,7 @@ fun <E> immutableSetOf(): ImmutableSet<E> = ImmutableOrderedSet.emptyOf<E>()
6565

6666
fun <E> immutableHashSetOf(vararg elements: E): ImmutableSet<E> = ImmutableHashSet.emptyOf<E>().addAll(elements.asList())
6767

68+
fun <K, V> immutableMapOf(vararg pairs: Pair<K, V>): ImmutableMap<K, V> = ImmutableOrderedMap.emptyOf<K,V>().mutate { it += pairs }
6869
fun <K, V> immutableHashMapOf(vararg pairs: Pair<K, V>): ImmutableMap<K, V> = ImmutableHashMap.emptyOf<K,V>().mutate { it += pairs }
6970

7071
fun <T> Iterable<T>.toImmutableList(): ImmutableList<T> =
@@ -92,7 +93,7 @@ fun <T> Set<T>.toImmutableHashSet(): ImmutableSet<T>
9293
fun <K, V> Map<K, V>.toImmutableMap(): ImmutableMap<K, V>
9394
= this as? ImmutableMap
9495
?: (this as? ImmutableMap.Builder)?.build()
95-
?: ImmutableHashMap.emptyOf<K, V>().putAll(this) // TODO: ImmutableOrderedMap.emptyOf
96+
?: ImmutableOrderedMap.emptyOf<K, V>().putAll(this)
9697

9798
fun <K, V> Map<K, V>.toImmutableHashMap(): ImmutableMap<K, V>
9899
= this as? ImmutableMap

kotlinx-collections-immutable/tests/src/test/kotlin/kotlinx.collections.immutable/ImmutableMapTest.kt

Lines changed: 26 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,9 +4,30 @@ import org.junit.Test
44
import java.util.*
55
import kotlin.test.*
66

7-
class ImmutableMapTest {
7+
class ImmutableHashMapTest : ImmutableMapTest() {
8+
override fun <K, V> immutableMapOf(vararg pairs: Pair<K, V>): ImmutableMap<K, V> = kotlinx.collections.immutable.immutableHashMapOf(*pairs)
9+
}
10+
class ImmutableOrderedMapTest : ImmutableMapTest() {
11+
override fun <K, V> immutableMapOf(vararg pairs: Pair<K, V>): ImmutableMap<K, V> = kotlinx.collections.immutable.immutableMapOf(*pairs)
12+
13+
@Test fun iterationOrder() {
14+
var map = immutableMapOf("x" to null, "y" to 1)
15+
assertEquals(listOf("x", "y"), map.keys.toList())
16+
17+
map += "x" to 1
18+
assertEquals(listOf("x", "y"), map.keys.toList())
19+
20+
map = map.remove("x")
21+
map += "x" to 2
22+
assertEquals(listOf("y", "x"), map.keys.toList())
23+
assertEquals(listOf(1, 2), map.values.toList())
24+
assertEquals(listOf("y" to 1, "x" to 2), map.toList())
25+
}
26+
}
27+
28+
abstract class ImmutableMapTest {
829

9-
fun <K, V> immutableMapOf(vararg pairs: Pair<K, V>): ImmutableMap<K, V> = kotlinx.collections.immutable.immutableHashMapOf(*pairs)
30+
abstract fun <K, V> immutableMapOf(vararg pairs: Pair<K, V>): ImmutableMap<K, V>
1031

1132

1233
@Test fun empty() {
@@ -54,12 +75,15 @@ class ImmutableMapTest {
5475
@Test fun putElements() {
5576
var map = immutableMapOf<String, Int?>()
5677
map = map.put("x", 0)
78+
map = map.put("x", 1)
5779
map = map.putAll(arrayOf("x" to null))
5880
map = map + ("y" to null)
5981
map += "y" to 1
6082
map += map
6183
map += map.map { it.key + "!" to it.value }
6284

85+
assertEquals(map.size, map.entries.size)
86+
6387
assertEquals(mapOf("x" to null, "y" to 1, "x!" to null, "y!" to 1), map)
6488
}
6589

0 commit comments

Comments
 (0)