Skip to content

Commit dd792b7

Browse files
author
Abduqodiri Qurbonzoda
committed
Implement efficient PersistentVectorBuilder iterator
1 parent 021bb9d commit dd792b7

File tree

5 files changed

+356
-7
lines changed

5 files changed

+356
-7
lines changed

kotlinx-collections-immutable/src/main/kotlin/kotlinx/collections/immutable/implementations/immutableList/AbstractListIterator.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@
1616

1717
package kotlinx.collections.immutable.implementations.immutableList
1818

19-
internal abstract class AbstractListIterator<out E>(var index: Int, val size: Int) : ListIterator<E> {
19+
internal abstract class AbstractListIterator<out E>(var index: Int, var size: Int) : ListIterator<E> {
2020
override fun hasNext(): Boolean {
2121
return index < size
2222
}

kotlinx-collections-immutable/src/main/kotlin/kotlinx/collections/immutable/implementations/immutableList/PersistentVectorBuilder.kt

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -22,15 +22,19 @@ import kotlinx.collections.immutable.internal.ListImplementation.checkPositionIn
2222

2323
private class Marker // TODO: Rename to MutabilityOwnership?
2424

25-
// TODO: Fix iteration O(N*logN) complexity
2625
class PersistentVectorBuilder<E>(private var vector: PersistentList<E>,
2726
private var vectorRoot: Array<Any?>?,
2827
private var vectorTail: Array<Any?>,
29-
private var rootShift: Int) : AbstractMutableList<E>(), PersistentList.Builder<E> {
28+
internal var rootShift: Int) : AbstractMutableList<E>(), PersistentList.Builder<E> {
3029
private var marker = Marker()
31-
private var root = vectorRoot
32-
private var tail = vectorTail
30+
internal var root = vectorRoot
31+
private set
32+
internal var tail = vectorTail
33+
private set
3334
override var size = vector.size
35+
private set
36+
37+
internal fun getModCount() = modCount
3438

3539
override fun build(): PersistentList<E> {
3640
vector = if (root === vectorRoot && tail === vectorTail) {
@@ -404,4 +408,17 @@ class PersistentVectorBuilder<E>(private var vector: PersistentList<E>,
404408
setInRoot(mutableRoot[bufferIndex] as Array<Any?>, shift - LOG_MAX_BUFFER_SIZE, index, e, oldElementCarry)
405409
return mutableRoot
406410
}
411+
412+
override fun iterator(): MutableIterator<E> {
413+
return this.listIterator()
414+
}
415+
416+
override fun listIterator(): MutableListIterator<E> {
417+
return this.listIterator(0)
418+
}
419+
420+
override fun listIterator(index: Int): MutableListIterator<E> {
421+
checkPositionIndex(index, size)
422+
return PersistentVectorMutableIterator(this, index)
423+
}
407424
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
/*
2+
* Copyright 2016-2019 JetBrains s.r.o.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
package kotlinx.collections.immutable.implementations.immutableList
18+
19+
/**
20+
* The class responsible for iterating over elements of the [PersistentVectorBuilder].
21+
*
22+
* There are two parts where the elements of the builder are located: root and tail.
23+
* [TrieIterator] is responsible for iterating over elements located at root,
24+
* whereas tail elements are iterated directly from this class.
25+
*/
26+
internal class PersistentVectorMutableIterator<T>(
27+
private val builder: PersistentVectorBuilder<T>,
28+
index: Int
29+
) : MutableListIterator<T>, AbstractListIterator<T>(index, builder.size) {
30+
31+
/**
32+
* The modCount this iterator is aware of.
33+
* Used to check if the [PersistentVectorBuilder] was modified outside this iterator.
34+
*/
35+
private var expectedModCount = builder.getModCount()
36+
/**
37+
* Iterates over leaves of the builder.root trie.
38+
* This property is equal to null if builder.root is null.
39+
*/
40+
private var trieIterator: TrieIterator<T>? = null
41+
/**
42+
* Index of the element this iterator returned from last invocation of next() or previous().
43+
* Used to remove or set new value at this index.
44+
* This property is set to -1 when method `add(element: T)` or `remove()` gets invoked.
45+
*/
46+
private var lastIteratedIndex = -1
47+
48+
init {
49+
setupTrieIterator()
50+
}
51+
52+
override fun previous(): T {
53+
checkForComodification()
54+
checkHasPrevious()
55+
56+
lastIteratedIndex = index - 1
57+
58+
val trieIterator = this.trieIterator ?: return builder.tail[--index] as T
59+
if (index > trieIterator.size) {
60+
return builder.tail[--index - trieIterator.size] as T
61+
}
62+
index--
63+
return trieIterator.previous()
64+
}
65+
66+
override fun next(): T {
67+
checkForComodification()
68+
checkHasNext()
69+
70+
lastIteratedIndex = index
71+
72+
val trieIterator = this.trieIterator ?: return builder.tail[index++] as T
73+
if (trieIterator.hasNext()) {
74+
index++
75+
return trieIterator.next()
76+
}
77+
return builder.tail[index++ - trieIterator.size] as T
78+
}
79+
80+
private fun reset() {
81+
size = builder.size
82+
expectedModCount = builder.getModCount()
83+
lastIteratedIndex = -1
84+
85+
setupTrieIterator()
86+
}
87+
88+
private fun setupTrieIterator() {
89+
val root = builder.root
90+
if (root == null) {
91+
trieIterator = null
92+
return
93+
}
94+
95+
val trieSize = rootSize(builder.size)
96+
val trieIndex = index.coerceAtMost(trieSize)
97+
val trieHeight = builder.rootShift / LOG_MAX_BUFFER_SIZE + 1
98+
if (trieIterator == null) {
99+
trieIterator = TrieIterator(root, trieIndex, trieSize, trieHeight)
100+
} else {
101+
trieIterator!!.reset(root, trieIndex, trieSize, trieHeight)
102+
}
103+
}
104+
105+
override fun add(element: T) {
106+
checkForComodification()
107+
108+
builder.add(index, element)
109+
index++
110+
reset()
111+
}
112+
113+
override fun remove() {
114+
checkForComodification()
115+
checkHasIterated()
116+
117+
builder.removeAt(lastIteratedIndex)
118+
if (lastIteratedIndex < index) index = lastIteratedIndex
119+
reset()
120+
}
121+
122+
override fun set(element: T) {
123+
checkForComodification()
124+
checkHasIterated()
125+
126+
builder[lastIteratedIndex] = element
127+
128+
expectedModCount = builder.getModCount()
129+
setupTrieIterator()
130+
}
131+
132+
private fun checkForComodification() {
133+
if (expectedModCount != builder.getModCount())
134+
throw ConcurrentModificationException()
135+
}
136+
137+
private fun checkHasIterated() {
138+
if (lastIteratedIndex == -1)
139+
throw IllegalStateException()
140+
}
141+
142+
private fun checkHasNext() {
143+
if (!hasNext())
144+
throw NoSuchElementException()
145+
}
146+
147+
private fun checkHasPrevious() {
148+
if (!hasPrevious())
149+
throw NoSuchElementException()
150+
}
151+
}

kotlinx-collections-immutable/src/main/kotlin/kotlinx/collections/immutable/implementations/immutableList/TrieIterator.kt

Lines changed: 13 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -19,15 +19,26 @@ package kotlinx.collections.immutable.implementations.immutableList
1919
internal class TrieIterator<out E>(root: Array<Any?>,
2020
index: Int,
2121
size: Int,
22-
private val height: Int) : AbstractListIterator<E>(index, size) {
23-
private val path: Array<Any?> = arrayOfNulls<Any?>(height)
22+
private var height: Int) : AbstractListIterator<E>(index, size) {
23+
private var path: Array<Any?> = arrayOfNulls<Any?>(height)
2424
private var isInRightEdge = index == size
2525

2626
init {
2727
path[0] = root
2828
fillPath(index - if (isInRightEdge) 1 else 0, 1)
2929
}
3030

31+
internal fun reset(root: Array<Any?>, index: Int, size: Int, height: Int) {
32+
this.index = index
33+
this.size = size
34+
this.height = height
35+
if (path.size < height) path = arrayOfNulls(height)
36+
path[0] = root
37+
isInRightEdge = index == size
38+
39+
fillPath(index - if (isInRightEdge) 1 else 0, 1)
40+
}
41+
3142
private fun fillPath(index: Int, startLevel: Int) {
3243
var shift = (height - startLevel) * LOG_MAX_BUFFER_SIZE
3344
var i = startLevel

0 commit comments

Comments
 (0)