How does kotlin store a collection in a for loop? ...
# getting-started
j
How does kotlin store a collection in a for loop? Lets say I have a concurrent queue which locks on every access for get or add or remove.
Copy code
for (item in queue) {
    if (item == 0) {
        queue.remove(item)
    }
}
Will this cause deadlocks? To be specific the queue used here is
ConcurrentLinkedQueue
from
java.util
.
h
Kotlin doesn't store the collection in the for loop at all … it just creates an
Iterator
for the collection.
j
@hho all right so it shouldn't create deadlocks since only one element is retrieved. Thanks!
s
Normally, modifying a collection while also using its iterator would lead to a
ConcurrentModificationException
. Yours will be okay though, because
ConcurrentLinkedQueue
is specifically exempt from that rule 👍.
3
❤️ 2
j
@Sam is this mentioned anywhere specifically (I couldn't find anything about this in the iterator of
ConcurrentLinkedQueue
) or is this just your knowlegde/observations?
s
I don't know if it comes up in the Kotlin docs, but if you take a look at the Java API docs for ConcurrentLinkedQueue you can find this:
Iterators are weakly consistent, returning elements reflecting the state of the queue at some point at or since the creation of the iterator. They do not throw
ConcurrentModificationException
, and may proceed concurrently with other operations. Elements contained in the queue since the creation of the iterator will be returned exactly once.
Whereas for an ArrayList:
The iterators returned by this class's
iterator
and
listIterator
methods are _fail-fast_: if the list is structurally modified at any time after the iterator is created, in any way except through the iterator's own
remove
or
add
methods, the iterator will throw a
ConcurrentModificationException
.
j
Ah yes kotlin docs end at weakly consistent.
Thank you so much @Sam!
w
While I would advocate against premature optimization, I do want to let you know that: `MutableCollection`s have
removeIf
, it's a Java Collection method, and
ConcurrentLinkedQueue
has an optimized override for this.
Copy code
fun MutableCollection<Int>.removeZeroes(): Boolean {
    return removeIf { it == 0 }
}
Additionally, for more fine grained control, sometimes using a
MutableIterator
can be both more convenient as well as faster (
remove
on
ConcurrentLinkedQueue
is
O(n)
, while it's
O(1)
on it's iterator). You can look at the default implementation of
removeIf
to see an example.
❤️ 1
1
j
@Wout Werkman That's really interesting to know. I was planning to use
removeIf
but due to compatibility issues, I dropped it 😞 For the
MutableIterator
part, this is a really nice optimization that i wanted for a long time. I actually hate making remove (basically find and then remove) on collections when I already have them in a for loop. Now this really advocates against the use of
for
loop haha but really thanks for this advice. Iterators are noice concept!
w
You can still use for loop on iterators. But please in all of this. Realize that these optimizations are most often not considered worth the impact on readability and debugability. But I understand that it's fun doing micro optimizations without profiling for pet projects :)
K 1
j
@Wout Werkman I understand exactly what you’re saying. Only makes sense to use this in a list large enough (which is my current use case) since best case of
queue.remove(item)
is my average case of
itr.remove()
. That too if
removeIf{..}
is not compatible.
👍 1