Files
kotlin-fork/libraries/stdlib/test/collections/ReversedViewsTest.kt
T
Filipp Zhinkin d0847f2519 KT-57617 Delegate iterators of reversed list views to underlying list iterators
Improve the performance of reversed list view iterators by delegating to
underlying list iterators instead of using implementations provided by
the AbstractList.
Latter use AbstractList::get(index: Int) to implement next() and
previous() methods and depending on the underlying list's implementation
it may lead to worse performance.

The change also improves reversed list views test coverage.
2023-04-11 08:16:21 +00:00

345 lines
11 KiB
Kotlin

/*
* Copyright 2010-2023 JetBrains s.r.o. and Kotlin Programming Language contributors.
* Use of this source code is governed by the Apache 2.0 license that can be found in the license/LICENSE.txt file.
*/
package test.collections
import test.collections.behaviors.listBehavior
import kotlin.test.*
class ReversedViewsTest {
@Test fun testNullToString() {
assertEquals("[null]", listOf<String?>(null).asReversed().toString())
}
@Test fun testBehavior() {
val original = listOf(2L, 3L, Long.MAX_VALUE)
val reversed = original.reversed()
compare(reversed, original.asReversed()) {
listBehavior()
}
}
@Test fun testMutableBehavior() {
val original = mutableListOf(2L, 3L, Long.MAX_VALUE)
val reversed = original.reversed()
compare(reversed, original.asReversed()) {
listBehavior()
}
}
@Test fun testSimple() {
assertEquals(listOf(3, 2, 1), listOf(1, 2, 3).asReversed())
assertEquals(listOf(3, 2, 1), listOf(1, 2, 3).asReversed().toList())
}
@Test fun testRandomAccess() {
val reversed = listOf(1, 2, 3).asReversed()
assertEquals(3, reversed[0])
assertEquals(2, reversed[1])
assertEquals(1, reversed[2])
}
@Test fun testDoubleReverse() {
assertEquals(listOf(1, 2, 3), listOf(1, 2, 3).asReversed().asReversed())
assertEquals(listOf(2, 3), listOf(1, 2, 3, 4).asReversed().subList(1, 3).asReversed())
}
@Test fun testEmpty() {
assertEquals(emptyList<Int>(), emptyList<Int>().asReversed())
}
@Test fun testReversedSubList() {
val reversed = (1..10).toList().asReversed()
assertEquals(listOf(9, 8, 7), reversed.subList(1, 4))
}
@Test fun testMutableSubList() {
val original = arrayListOf(1, 2, 3, 4)
val reversedSubList = original.asReversed().subList(1, 3)
assertEquals(listOf(3, 2), reversedSubList)
reversedSubList.clear()
assertEquals(emptyList<Int>(), reversedSubList)
assertEquals(listOf(1, 4), original)
reversedSubList.add(100)
assertEquals(listOf(100), reversedSubList)
assertEquals(listOf(1, 100, 4), original)
}
@Test fun testMutableSimple() {
assertEquals(listOf(3, 2, 1), mutableListOf(1, 2, 3).asReversed())
assertEquals(listOf(3, 2, 1), mutableListOf(1, 2, 3).asReversed().toList())
}
@Test fun testMutableDoubleReverse() {
assertEquals(listOf(1, 2, 3), mutableListOf(1, 2, 3).asReversed().asReversed())
assertEquals(listOf(2, 3), mutableListOf(1, 2, 3, 4).asReversed().subList(1, 3).asReversed())
}
@Test fun testMutableEmpty() {
assertEquals(emptyList<Int>(), mutableListOf<Int>().asReversed())
}
@Test fun testMutableReversedSubList() {
val reversed = (1..10).toMutableList().asReversed()
assertEquals(listOf(9, 8, 7), reversed.subList(1, 4))
}
@Test fun testMutableAdd() {
val original = mutableListOf(1, 2, 3)
val reversed = original.asReversed()
reversed.add(0) // add zero at end of reversed
assertEquals(listOf(3, 2, 1, 0), reversed)
assertEquals(listOf(0, 1, 2, 3), original)
reversed.add(0, 4) // add four at position 0
assertEquals(listOf(4, 3, 2, 1, 0), reversed)
assertEquals(listOf(0, 1, 2, 3, 4), original)
}
@Test fun testMutableSet() {
val original = mutableListOf(1, 2, 3)
val reversed = original.asReversed()
reversed.set(0, 300)
reversed.set(1, 200)
reversed.set(2, 100)
assertEquals(listOf(100, 200, 300), original)
assertEquals(listOf(300, 200, 100), reversed)
}
@Test fun testMutableRemove() {
val original = mutableListOf("a", "b", "c")
val reversed = original.asReversed()
reversed.removeAt(0) // remove c
assertEquals(listOf("a", "b"), original)
assertEquals(listOf("b", "a"), reversed)
reversed.removeAt(1) // remove a
assertEquals(listOf("b"), original)
reversed.removeAt(0) // remove remaining b
assertEquals(emptyList<String>(), original)
}
@Test fun testMutableRemoveByObj() {
val original = mutableListOf("a", "b", "c")
val reversed = original.asReversed()
reversed.remove("c")
assertEquals(listOf("a", "b"), original)
assertEquals(listOf("b", "a"), reversed)
}
@Test fun testMutableClear() {
val original = mutableListOf(1, 2, 3)
val reversed = original.asReversed()
reversed.clear()
assertEquals(emptyList<Int>(), reversed)
assertEquals(emptyList<Int>(), original)
}
@Test fun testContains() {
assertTrue { 1 in listOf(1, 2, 3).asReversed() }
assertTrue { 1 in mutableListOf(1, 2, 3).asReversed() }
}
@Test fun testBidirectionalModifications() {
val original = mutableListOf(1, 2, 3, 4)
val reversed = original.asReversed()
original.removeAt(3)
assertEquals(listOf(1, 2, 3), original)
assertEquals(listOf(3, 2, 1), reversed)
reversed.removeAt(2)
assertEquals(listOf(2, 3), original)
assertEquals(listOf(3, 2), reversed)
}
@Test fun testIndexOf() {
assertEquals(2, listOf(1, 2, 3).asReversed().indexOf(1))
assertEquals(2, mutableListOf(1, 2, 3).asReversed().indexOf(1))
assertEquals(-1, listOf(1, 2, 3).asReversed().indexOf(0))
assertEquals(-1, mutableListOf(1, 2, 3).asReversed().indexOf(0))
}
@Test fun testLastIndexOf() {
assertEquals(2, listOf(1, 2, 3).asReversed().indexOf(1))
assertEquals(2, mutableListOf(1, 2, 3).asReversed().indexOf(1))
assertEquals(-1, listOf(1, 2, 3).asReversed().lastIndexOf(0))
assertEquals(-1, mutableListOf(1, 2, 3).asReversed().lastIndexOf(0))
}
@Test fun testIteratorAdd() {
val original = mutableListOf(1, 2, 4)
val reversedView = original.asReversed()
val iter = reversedView.listIterator()
val reversedCopy = original.reversed().toMutableList()
val copyIter = reversedCopy.listIterator()
compare(copyIter, iter) {
propertyEquals { add(5) }
propertyEquals { previousIndex() }
propertyEquals { nextIndex() }
propertyEquals { next() }
}
assertEquals(reversedCopy, reversedView)
assertEquals(listOf(1, 2, 4, 5), original)
compare(copyIter, iter) {
propertyEquals { add(3) }
propertyEquals { previousIndex() }
propertyEquals { nextIndex() }
propertyEquals { previous() }
}
iter.seekEnd()
iter.add(0)
assertEquals(listOf(5, 4, 3, 2, 1, 0), reversedView)
assertEquals(listOf(0, 1, 2, 3, 4, 5), original)
}
@Test fun testIteratorRemove() {
val original = mutableListOf(0, 1, 2, 3, 4)
val reversedView = original.asReversed()
val iter = reversedView.listIterator()
val reversedCopy = original.reversed().toMutableList()
val copyIter = reversedCopy.listIterator()
compare(copyIter, iter) {
propertyFailsWith<IllegalStateException> { remove() }
propertyEquals {
next()
remove()
}
propertyEquals { previousIndex() }
propertyEquals { nextIndex() }
propertyFailsWith<IllegalStateException> { remove() }
propertyEquals { next() }
}
assertEquals(reversedCopy, reversedView)
assertEquals(listOf(0, 1, 2, 3), original)
compare(copyIter, iter) {
propertyEquals {
next()
remove()
}
propertyEquals { previousIndex() }
propertyEquals { nextIndex() }
propertyEquals { previous() }
}
assertEquals(reversedCopy, reversedView)
assertEquals(listOf(0, 1, 3), original)
iter.seekEnd()
iter.remove()
assertEquals(listOf(3, 1), reversedView)
assertEquals(listOf(1, 3), original)
}
@Test fun testIteratorSet() {
val original = mutableListOf(2, 3, 4)
val iter = original.asReversed().listIterator()
while (iter.hasNext()) {
val v = iter.next()
iter.set(v * v)
}
assertEquals(listOf(4, 9, 16), original)
}
@Test fun testGetIOOB() {
assertFailsWith<IndexOutOfBoundsException> {
listOf(1, 2, 3).asReversed().get(3)
}.let { exception ->
// we actually don't care about the exact wording,
// we just need the index to be not confusing
assertContains(exception.message!!, "index 3")
}
assertFailsWith<IndexOutOfBoundsException> {
mutableListOf(1, 2, 3).asReversed().get(3)
}.let { exception ->
// we actually don't care about the exact wording,
// we just need the index to be not confusing
assertContains(exception.message!!, "index 3")
}
}
@Test fun testSetIOOB() {
assertFailsWith<IndexOutOfBoundsException> {
mutableListOf(1, 2, 3, 4).asReversed().set(4, 0)
}.let { exception ->
// we actually don't care about the exact wording,
// we just need the index to be not confusing
assertContains(exception.message!!, "index 4")
}
}
@Test fun testAddIOOB() {
assertFailsWith<IndexOutOfBoundsException> {
mutableListOf(1, 2, 3).asReversed().add(4, 0)
}.let { exception ->
// we actually don't care about the exact wording,
// we just need the index to be not confusing
assertContains(exception.message!!, "index 4")
}
}
@Test fun testRemoveIOOB() {
assertFailsWith<IndexOutOfBoundsException> {
mutableListOf(1, 2, 3).asReversed().removeAt(3)
}.let { exception ->
// we actually don't care about the exact wording,
// we just need the index to be not confusing
assertContains(exception.message!!, "index 3")
}
}
@Test fun testIteratorNSEOnNext() {
assertFailsWith<NoSuchElementException> {
val it = listOf(1, 2, 3).asReversed().iterator()
it.seekEnd()
it.next()
}
assertFailsWith<NoSuchElementException> {
val it = mutableListOf(1, 2, 3).asReversed().iterator()
it.seekEnd()
it.next()
}
}
@Test fun testIteratorNSEOnPrevious() {
assertFailsWith<NoSuchElementException> {
listOf(1, 2, 3).asReversed().listIterator().previous()
}
assertFailsWith<NoSuchElementException> {
mutableListOf(1, 2, 3).asReversed().listIterator().previous()
}
}
}
private fun Iterator<*>.seekEnd() {
while (hasNext()) {
next()
}
}