Skip to content

Commit dca3830

Browse files
committed
Rewrite ImmutableOrderedMap without keeping the separate order list, but instead with linking keys of the entries in the insertion order.
1 parent ee3d423 commit dca3830

File tree

2 files changed

+123
-73
lines changed

2 files changed

+123
-73
lines changed

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

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,12 @@ import java.util.*
55

66
internal abstract class AbstractImmutableMap<K, out V> protected constructor(protected val impl: PMap<K, @UnsafeVariance V>) : ImmutableMap<K, V> {
77

8+
abstract class AbstractImmutableEntry<out K, out V> : Map.Entry<K, V> {
9+
override fun equals(other: Any?): Boolean = other is Map.Entry<*,*> && other.key == key && other.value == value
10+
override fun hashCode(): Int = (key?.hashCode() ?: 0) xor (value?.hashCode() ?: 0)
11+
override fun toString(): String = "$key=$value"
12+
}
13+
814
override val size: Int get() = impl.size
915
override fun isEmpty(): Boolean = impl.isEmpty()
1016
override fun containsKey(key: K): Boolean = impl.containsKey(key)

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

Lines changed: 117 additions & 73 deletions
Original file line numberDiff line numberDiff line change
@@ -2,23 +2,32 @@ package kotlinx.collections.immutable
22

33
import org.pcollections.HashTreePMap
44
import org.pcollections.PMap
5-
import org.pcollections.PVector
6-
import org.pcollections.TreePVector
7-
import java.util.*
5+
import java.util.ConcurrentModificationException
86

97

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> {
8+
internal class ImmutableOrderedMap<K, out V> private constructor(private val impl: PMap<K, LinkedEntry<K, V>>) : ImmutableMap<K, V>, AbstractMap<K, V>() {
9+
// TODO: Keep reference to first/last entry
10+
11+
protected class LinkedEntry<out K, out V>(val key: K, val value: @UnsafeVariance V, val prevKey: Any?, val nextKey: Any?) {
12+
// has referential equality/hashCode
13+
14+
fun copy(value: @UnsafeVariance V = this.value, prevKey: Any? = this.prevKey, nextKey: Any? = this.nextKey) =
15+
LinkedEntry(key, value, prevKey, nextKey)
16+
17+
// had to separate map entry implementations because of conflicting equality/hashCode
18+
inner class MapEntry : AbstractImmutableMap.AbstractImmutableEntry<K, V>() {
19+
override val key: K get() = this@LinkedEntry.key
20+
override val value: V get() = this@LinkedEntry.value
21+
}
22+
val mapEntry = MapEntry()
23+
}
1124

1225
override val size: Int get() = impl.size
1326
override fun isEmpty(): Boolean = impl.isEmpty()
1427
override fun containsKey(key: K): Boolean = impl.containsKey(key)
15-
override fun containsValue(value: @UnsafeVariance V): Boolean = impl.containsValue(value)
28+
override fun containsValue(value: @UnsafeVariance V): Boolean = impl.values.any { it.value == value }
1629

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()
30+
override fun get(key: K): V? = impl[key]?.value
2231

2332

2433
// should it be immutable set/collection or just read-only?
@@ -34,50 +43,43 @@ internal class ImmutableOrderedMap<K, out V> private constructor(private val imp
3443
final override val entries: Set<Map.Entry<K, V>> get() = _entries ?: createEntries().apply { _entries = this }
3544
private fun createEntries(): Set<Map.Entry<K, V>> = OrderedEntrySet()
3645

37-
override fun put(key: K, value: @UnsafeVariance V): ImmutableMap<K, V> = wrap(impl.plus(key, value)) { order.addOrReplace(key, value) }
46+
override fun put(key: K, value: @UnsafeVariance V): ImmutableMap<K, V> = wrap(impl.putEntry(impl[key], key, value))
3847
override fun putAll(m: Map<out K, @UnsafeVariance V>): ImmutableMap<K, V> {
3948
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 }
49+
for ((k, v) in m)
50+
newImpl = newImpl.putEntry(newImpl[k], k, v)
51+
52+
return wrap(newImpl)
4853
}
49-
override fun remove(key: K): ImmutableMap<K, V> = wrap(impl.minus(key), { order.minus(order.indexOfFirst { it.key == key })})
54+
override fun remove(key: K): ImmutableMap<K, V> = wrap(impl.removeLinked(key))
55+
5056
override fun remove(key: K, value: @UnsafeVariance V): ImmutableMap<K, V>
5157
= if (!impl.contains(key, value)) this else remove(key)
5258

5359
override fun clear(): ImmutableMap<K, V> = emptyOf()
5460

55-
override fun builder(): ImmutableMap.Builder<K, @UnsafeVariance V> = Builder(this, impl, order)
61+
override fun builder(): ImmutableMap.Builder<K, @UnsafeVariance V> = Builder(this, impl)
5662

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))
63+
protected fun wrap(impl: PMap<K, LinkedEntry<K, @UnsafeVariance V>>): ImmutableOrderedMap<K, V> {
64+
return if (impl === this.impl) this else ImmutableOrderedMap(impl)
6165
}
6266

6367

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 }
68+
protected class Builder<K, V>(protected var value: ImmutableOrderedMap<K, V>, protected var impl: PMap<K, LinkedEntry<K, V>>) : ImmutableMap.Builder<K, V>, AbstractMutableMap<K, V>() {
69+
override fun build(): ImmutableMap<K, V> = value.wrap(impl).apply { value = this }
6670

6771
override val size: Int get() = impl.size
6872
override fun isEmpty(): Boolean = impl.isEmpty()
6973
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)
74+
override fun containsValue(value: @UnsafeVariance V): Boolean = impl.values.any { it.value == value }
7375

74-
override fun equals(other: Any?): Boolean = impl.equals(other)
75-
override fun hashCode(): Int = impl.hashCode()
76-
override fun toString(): String = impl.toString()
76+
override fun get(key: K): V? = impl.get(key)?.value
7777

7878
private var entrySet: MutableSet<MutableMap.MutableEntry<K, V>>? = null
7979
override val entries: MutableSet<MutableMap.MutableEntry<K, V>>
80-
get() = entrySet ?: object : MutableSet<MutableMap.MutableEntry<K, V>>, AbstractSet<MutableMap.MutableEntry<K, V>>() {
80+
get() = entrySet ?: object : AbstractMutableSet<MutableMap.MutableEntry<K, V>>() {
81+
override fun add(element: MutableMap.MutableEntry<K, V>): Boolean = throw UnsupportedOperationException()
82+
8183
override val size: Int get() = impl.size
8284
override fun isEmpty(): Boolean = impl.isEmpty()
8385
override fun clear() = this@Builder.clear()
@@ -95,15 +97,14 @@ internal class ImmutableOrderedMap<K, out V> private constructor(private val imp
9597

9698
override fun iterator() = object : MutableIterator<MutableMap.MutableEntry<K, V>> {
9799
private var snapshot = impl
98-
private val iterator = order.iterator()
99-
private var entry: Map.Entry<K,V>? = null
100+
private var entry: LinkedEntry<K,V>? = impl.firstEntry()
100101

101-
override fun hasNext(): Boolean = iterator.hasNext()
102+
override fun hasNext(): Boolean = entry != null
102103
override fun next(): MutableMap.MutableEntry<K, V> {
103104
checkForComodification()
104-
val entry = iterator.next()
105-
this.entry = entry
106-
return object : MutableMap.MutableEntry<K, V>, Map.Entry<K, V> by entry {
105+
val entry = this.entry ?: throw NoSuchElementException()
106+
this.entry = snapshot[entry.nextKey]
107+
return object : MutableMap.MutableEntry<K, V>, Map.Entry<K, V> by entry.mapEntry {
107108
override fun setValue(newValue: V): V {
108109
checkForComodification()
109110
val oldValue = put(entry.key, newValue) as V
@@ -127,35 +128,32 @@ internal class ImmutableOrderedMap<K, out V> private constructor(private val imp
127128
}
128129
}.apply { entrySet = this }
129130

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
138131

139132
override fun clear() {
140-
mutate(HashTreePMap.empty(), { TreePVector.empty() })
133+
mutate(HashTreePMap.empty())
141134
}
142135

143-
override fun put(key: K, value: V): V? =
144-
get(key).apply {
145-
mutate(impl.plus(key, value), { order.addOrReplace(key, value) })
146-
}
136+
override fun put(key: K, value: V): V? {
137+
val entry = impl[key]
138+
139+
mutate(impl.putEntry(entry, key, value))
140+
141+
return entry?.value
142+
}
147143

148144
override fun putAll(from: Map<out K, V>) {
149145
for ((k, v) in from) put(k, v)
150146
}
151147

152148

153-
override fun remove(key: K): V?
154-
= get(key).apply { mutate(impl.minus(key), { it.minus(it.indexOfFirst { it.key == key })}) }
149+
override fun remove(key: K): V? {
150+
val entry = impl[key] ?: return null
151+
mutate(impl.removeEntry(entry))
152+
return entry.value
153+
}
155154

156-
protected inline fun mutate(newValue: PMap<K, V>, orderOperation: (PVector<Map.Entry<K, V>>) -> (PVector<Map.Entry<K, V>>) ): Boolean {
155+
protected inline fun mutate(newValue: PMap<K, LinkedEntry<K, V>>): Boolean {
157156
if (newValue !== impl) {
158-
order = orderOperation(order)
159157
impl = newValue
160158
return true
161159
}
@@ -165,46 +163,92 @@ internal class ImmutableOrderedMap<K, out V> private constructor(private val imp
165163
}
166164

167165
companion object {
168-
private val EMPTY = ImmutableOrderedMap(HashTreePMap.empty<Any?, Nothing>(), TreePVector.empty())
166+
private val EMPTY = ImmutableOrderedMap(HashTreePMap.empty<Any?, LinkedEntry<Any?, Nothing>>())
167+
168+
private val TERMINATOR = Any()
169+
170+
private fun <K, V> PMap<K, LinkedEntry<K, V>>.contains(key: K, value: @UnsafeVariance V): Boolean
171+
= this[key]?.let { entry -> entry.value == value } ?: false
172+
173+
private fun <K, V> PMap<K, LinkedEntry<K, V>>.removeLinked(key: K): PMap<K, LinkedEntry<K, V>> {
174+
val entry = this[key] ?: return this
175+
return removeEntry(entry)
176+
}
177+
178+
private fun <K, V> PMap<K, LinkedEntry<K, V>>.removeEntry(entry: LinkedEntry<K, V>): PMap<K, LinkedEntry<K, V>> {
179+
val prevKey = entry.prevKey
180+
val nextKey = entry.nextKey
181+
var new = this.minus(entry.key)
182+
183+
if (prevKey !== TERMINATOR) {
184+
@Suppress("UNCHECKED_CAST")
185+
val prevEntry = this[prevKey as K]!!
186+
val newPrevEntry = prevEntry.copy(nextKey = nextKey)
187+
new = new.plus(newPrevEntry.key, newPrevEntry)
188+
}
189+
if (nextKey !== TERMINATOR) {
190+
@Suppress("UNCHECKED_CAST")
191+
val nextEntry = this[nextKey as K]!!
192+
val newNextEntry = nextEntry.copy(prevKey = prevKey)
193+
new = new.plus(newNextEntry.key, newNextEntry)
194+
}
195+
return new
196+
}
197+
198+
private fun <K, V> PMap<K, LinkedEntry<K, V>>.putEntry(entry: LinkedEntry<K, V>?, key: K, value: V): PMap<K, LinkedEntry<K, V>> {
199+
if (entry != null) {
200+
return if (entry.value == value) this else this.plus(key, entry.copy(value = value))
201+
}
202+
val lastEntry = this.lastEntry()
203+
if (lastEntry == null) {
204+
val newEntry = LinkedEntry(key, value, TERMINATOR, TERMINATOR)
205+
return this.plus(key, newEntry)
206+
} else {
207+
val newEntry = LinkedEntry(key, value, lastEntry.key, TERMINATOR)
208+
val newLastEntry = lastEntry.copy(nextKey = key)
209+
return this.plus(lastEntry.key, newLastEntry).plus(key, newEntry)
210+
}
211+
}
212+
213+
private fun <K, V> PMap<K, LinkedEntry<K, V>>.firstEntry() = this.values.firstOrNull { it.prevKey === TERMINATOR }
214+
private fun <K, V> PMap<K, LinkedEntry<K, V>>.lastEntry() = this.values.firstOrNull { it.nextKey === TERMINATOR }
215+
169216

170217
@Suppress("UNCHECKED_CAST")
171218
fun <K, V> emptyOf(): ImmutableOrderedMap<K, V> = EMPTY as ImmutableOrderedMap<K, V>
172219
}
173220

221+
private val entrySequence = generateSequence(impl.firstEntry()) { e -> impl[e.nextKey] }
174222

175-
private inner class OrderedEntrySet : Set<Map.Entry<K, V>> {
223+
private inner class OrderedEntrySet : AbstractSet<Map.Entry<K, V>>() {
176224
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)
225+
override fun contains(element: Map.Entry<K, @UnsafeVariance V>): Boolean = impl.contains(element.key, element.value)
226+
override fun containsAll(elements: Collection<Map.Entry<K, @UnsafeVariance V>>): Boolean = elements.all { (k, v) -> impl.contains(k, v) }
179227
override fun isEmpty(): Boolean = impl.isEmpty()
180-
override fun iterator(): Iterator<Map.Entry<K, V>> = order.iterator()
228+
private val mapped = entrySequence.map { it.mapEntry }
229+
override fun iterator(): Iterator<Map.Entry<K, V>> = mapped.iterator()
181230
}
182231

183-
private inner class OrderedKeySet : Set<K> {
232+
private inner class OrderedKeySet : AbstractSet<K>() {
184233
override val size: Int get() = impl.size
185234
override fun contains(element: K): Boolean = impl.containsKey(element)
186235
override fun containsAll(elements: Collection<K>): Boolean = impl.keys.containsAll(elements)
187236

188237
override fun isEmpty(): Boolean = impl.isEmpty()
189238

190-
private val mapped = order.asSequence().map { it.key }
239+
private val mapped = entrySequence.map { it.key }
191240
override fun iterator(): Iterator<K> = mapped.iterator()
192241
}
193242

194-
private inner class OrderedValueCollection : Collection<V> {
243+
private inner class OrderedValueCollection : AbstractCollection<V>() {
195244
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)
245+
override fun contains(element: @UnsafeVariance V): Boolean = containsValue(element)
246+
override fun containsAll(elements: Collection<@UnsafeVariance V>): Boolean = elements.all { v -> containsValue(v) }
198247
override fun isEmpty(): Boolean = impl.isEmpty()
199248

200-
private val mapped = order.asSequence().map { it.value }
249+
private val mapped = entrySequence.map { it.value }
201250
override fun iterator(): Iterator<V> = mapped.iterator()
202251
}
203252
}
204253

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-
}
210254

0 commit comments

Comments
 (0)