From f4f82656f7b3cd85d82010f13c3efc21c7cd0b20 Mon Sep 17 00:00:00 2001 From: Ilya Gorbunov Date: Thu, 11 Feb 2016 23:54:49 +0300 Subject: [PATCH] Use list iterators instead of indexed access in operations on lists taking a lambda. Related to #KT-9607 --- .../stdlib/src/generated/_Collections.kt | 103 +++++++++++------- .../test/collections/ListSpecificTest.kt | 7 ++ .../src/templates/Aggregates.kt | 54 +++++++++ .../src/templates/Elements.kt | 41 ++++++- .../src/templates/Filtering.kt | 57 +++++++++- 5 files changed, 217 insertions(+), 45 deletions(-) diff --git a/libraries/stdlib/src/generated/_Collections.kt b/libraries/stdlib/src/generated/_Collections.kt index bbda048d89c..f29c914e72b 100644 --- a/libraries/stdlib/src/generated/_Collections.kt +++ b/libraries/stdlib/src/generated/_Collections.kt @@ -278,10 +278,11 @@ public inline fun Iterable.indexOfFirst(predicate: (T) -> Boolean): Int { * Returns index of the first element matching the given [predicate], or -1 if the list does not contain such element. */ public inline fun List.indexOfFirst(predicate: (T) -> Boolean): Int { - for (index in indices) { - if (predicate(this[index])) { + var index = 0 + for (item in this) { + if (predicate(item)) return index - } + index++ } return -1 } @@ -304,9 +305,10 @@ public inline fun Iterable.indexOfLast(predicate: (T) -> Boolean): Int { * Returns index of the last element matching the given [predicate], or -1 if the list does not contain such element. */ public inline fun List.indexOfLast(predicate: (T) -> Boolean): Int { - for (index in indices.reversed()) { - if (predicate(this[index])) { - return index + val iterator = this.listIterator(size) + while (iterator.hasPrevious()) { + if (predicate(iterator.previous())) { + return iterator.nextIndex() } } return -1 @@ -365,8 +367,9 @@ public inline fun Iterable.last(predicate: (T) -> Boolean): T { * @throws [NoSuchElementException] if no such element is found. */ public inline fun List.last(predicate: (T) -> Boolean): T { - for (index in this.indices.reversed()) { - val element = this[index] + val iterator = this.listIterator(size) + while (iterator.hasPrevious()) { + val element = iterator.previous() if (predicate(element)) return element } throw NoSuchElementException("List contains no element matching the predicate.") @@ -438,8 +441,9 @@ public inline fun Iterable.lastOrNull(predicate: (T) -> Boolean): T? { * Returns the last element matching the given [predicate], or `null` if no such element was found. */ public inline fun List.lastOrNull(predicate: (T) -> Boolean): T? { - for (index in this.indices.reversed()) { - val element = this[index] + val iterator = this.listIterator(size) + while (iterator.hasPrevious()) { + val element = iterator.previous() if (predicate(element)) return element } return null @@ -546,8 +550,8 @@ public fun Iterable.drop(n: Int): List { return emptyList() list = ArrayList(resultSize) if (this is List) { - for (index in n..size - 1) { - list.add(this[index]) + for (item in listIterator(n)) { + list.add(item) } return list } @@ -574,9 +578,12 @@ public fun List.dropLast(n: Int): List { * Returns a list containing all elements except last elements that satisfy the given [predicate]. */ public inline fun List.dropLastWhile(predicate: (T) -> Boolean): List { - for (index in lastIndex downTo 0) { - if (!predicate(this[index])) { - return take(index + 1) + if (!isEmpty()) { + val iterator = listIterator(size) + while (iterator.hasPrevious()) { + if (!predicate(iterator.previous())) { + return take(iterator.nextIndex() + 1) + } } } return emptyList() @@ -726,8 +733,13 @@ public fun List.takeLast(n: Int): List { val size = size if (n >= size) return toList() val list = ArrayList(n) - for (index in size - n .. size - 1) - list.add(this[index]) + if (this is RandomAccess) { + for (index in size - n .. size - 1) + list.add(this[index]) + } else { + for (item in listIterator(n)) + list.add(item) + } return list } @@ -735,9 +747,18 @@ public fun List.takeLast(n: Int): List { * Returns a list containing last elements satisfying the given [predicate]. */ public inline fun List.takeLastWhile(predicate: (T) -> Boolean): List { - for (index in lastIndex downTo 0) { - if (!predicate(this[index])) { - return drop(index + 1) + if (isEmpty()) + return emptyList() + val iterator = listIterator(size) + while (iterator.hasPrevious()) { + if (!predicate(iterator.previous())) { + iterator.next() + val expectedSize = size - iterator.nextIndex() + if (expectedSize == 0) return emptyList() + return ArrayList(expectedSize).apply { + while (iterator.hasNext()) + add(iterator.next()) + } } } return toList() @@ -1368,10 +1389,12 @@ public inline fun Iterable.foldIndexed(initial: R, operation: (Int, R, * Accumulates value starting with [initial] value and applying [operation] from right to left to each element and current accumulator value. */ public inline fun List.foldRight(initial: R, operation: (T, R) -> R): R { - var index = lastIndex var accumulator = initial - while (index >= 0) { - accumulator = operation(get(index--), accumulator) + if (!isEmpty()) { + val iterator = listIterator(size) + while (iterator.hasPrevious()) { + accumulator = operation(iterator.previous(), accumulator) + } } return accumulator } @@ -1383,11 +1406,13 @@ public inline fun List.foldRight(initial: R, operation: (T, R) -> R): * and current accumulator value, and calculates the next accumulator value. */ public inline fun List.foldRightIndexed(initial: R, operation: (Int, T, R) -> R): R { - var index = lastIndex var accumulator = initial - while (index >= 0) { - accumulator = operation(index, get(index), accumulator) - --index + if (!isEmpty()) { + val iterator = listIterator(size) + while (iterator.hasPrevious()) { + val index = iterator.previousIndex() + accumulator = operation(index, iterator.previous(), accumulator) + } } return accumulator } @@ -1554,11 +1579,12 @@ public inline fun Iterable.reduceIndexed(operation: (Int, S, T) -> * Accumulates value starting with last element and applying [operation] from right to left to each element and current accumulator value. */ public inline fun List.reduceRight(operation: (T, S) -> S): S { - var index = lastIndex - if (index < 0) throw UnsupportedOperationException("Empty list can't be reduced.") - var accumulator: S = get(index--) - while (index >= 0) { - accumulator = operation(get(index--), accumulator) + val iterator = listIterator(size) + if (!iterator.hasPrevious()) + throw UnsupportedOperationException("Empty list can't be reduced.") + var accumulator: S = iterator.previous() + while (iterator.hasPrevious()) { + accumulator = operation(iterator.previous(), accumulator) } return accumulator } @@ -1570,12 +1596,13 @@ public inline fun List.reduceRight(operation: (T, S) -> S): S { * and current accumulator value, and calculates the next accumulator value. */ public inline fun List.reduceRightIndexed(operation: (Int, T, S) -> S): S { - var index = lastIndex - if (index < 0) throw UnsupportedOperationException("Empty list can't be reduced.") - var accumulator: S = get(index--) - while (index >= 0) { - accumulator = operation(index, get(index), accumulator) - --index + val iterator = listIterator(size) + if (!iterator.hasPrevious()) + throw UnsupportedOperationException("Empty list can't be reduced.") + var accumulator: S = iterator.previous() + while (iterator.hasPrevious()) { + val index = iterator.previousIndex() + accumulator = operation(index, iterator.previous(), accumulator) } return accumulator } diff --git a/libraries/stdlib/test/collections/ListSpecificTest.kt b/libraries/stdlib/test/collections/ListSpecificTest.kt index 9170047b735..6ef66ec8de1 100644 --- a/libraries/stdlib/test/collections/ListSpecificTest.kt +++ b/libraries/stdlib/test/collections/ListSpecificTest.kt @@ -54,6 +54,13 @@ class ListSpecificTest { assertEquals(1, data.lastIndex) } + @Test + fun indexOfLast() { + expect(-1) { data.indexOfLast { it.contains("p") } } + expect(1) { data.indexOfLast { it.length == 3 } } + expect(-1) { empty.indexOfLast { it.startsWith('f') } } + } + @Test fun mutableList() { val items = listOf("beverage", "location", "name") diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/Aggregates.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/Aggregates.kt index 7a5bbd805d9..65a2bf13dcc 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/Aggregates.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/Aggregates.kt @@ -401,6 +401,19 @@ fun aggregates(): List { return accumulator """ } + body(Lists) { + """ + var accumulator = initial + if (!isEmpty()) { + val iterator = listIterator(size) + while (iterator.hasPrevious()) { + val index = iterator.previousIndex() + accumulator = operation(index, iterator.previous(), accumulator) + } + } + return accumulator + """ + } } templates add f("fold(initial: R, operation: (R, T) -> R)") { @@ -436,6 +449,18 @@ fun aggregates(): List { return accumulator """ } + body(Lists) { + """ + var accumulator = initial + if (!isEmpty()) { + val iterator = listIterator(size) + while (iterator.hasPrevious()) { + accumulator = operation(iterator.previous(), accumulator) + } + } + return accumulator + """ + } } templates add f("reduceIndexed(operation: (Int, T, T) -> T)") { @@ -562,6 +587,21 @@ fun aggregates(): List { --index } + return accumulator + """ + } + body(Lists) { + """ + val iterator = listIterator(size) + if (!iterator.hasPrevious()) + throw UnsupportedOperationException("Empty list can't be reduced.") + + var accumulator: S = iterator.previous() + while (iterator.hasPrevious()) { + val index = iterator.previousIndex() + accumulator = operation(index, iterator.previous(), accumulator) + } + return accumulator """ } @@ -660,6 +700,20 @@ fun aggregates(): List { accumulator = operation(get(index--), accumulator) } + return accumulator + """ + } + body(Lists) { + """ + val iterator = listIterator(size) + if (!iterator.hasPrevious()) + throw UnsupportedOperationException("Empty list can't be reduced.") + + var accumulator: S = iterator.previous() + while (iterator.hasPrevious()) { + accumulator = operation(iterator.previous(), accumulator) + } + return accumulator """ } diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/Elements.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/Elements.kt index 2884fe1cce5..fc776e6baa1 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/Elements.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/Elements.kt @@ -130,6 +130,7 @@ fun elements(): List { templates add f("indexOfFirst(predicate: (T) -> Boolean)") { inline(true) + include(Lists) doc { f -> "Returns index of the first ${f.element} matching the given [predicate], or -1 if the ${f.collection} does not contain such ${f.element}." } returns("Int") body { @@ -144,7 +145,7 @@ fun elements(): List { """ } - body(Lists, CharSequences, ArraysOfPrimitives, ArraysOfObjects) { + body(CharSequences, ArraysOfPrimitives, ArraysOfObjects) { """ for (index in indices) { if (predicate(this[index])) { @@ -174,7 +175,7 @@ fun elements(): List { """ } - body(Lists, CharSequences, ArraysOfPrimitives, ArraysOfObjects) { + body(CharSequences, ArraysOfPrimitives, ArraysOfObjects) { """ for (index in indices.reversed()) { if (predicate(this[index])) { @@ -184,6 +185,17 @@ fun elements(): List { return -1 """ } + body(Lists) { + """ + val iterator = this.listIterator(size) + while (iterator.hasPrevious()) { + if (predicate(iterator.previous())) { + return iterator.nextIndex() + } + } + return -1 + """ + } } templates add f("elementAt(index: Int)") { @@ -535,7 +547,7 @@ fun elements(): List { """ } - body(CharSequences, ArraysOfPrimitives, ArraysOfObjects, Lists) { f -> + body(CharSequences, ArraysOfPrimitives, ArraysOfObjects) { f -> """ for (index in this.indices.reversed()) { val element = this[index] @@ -544,6 +556,16 @@ fun elements(): List { throw NoSuchElementException("${f.doc.collection.capitalize()} contains no ${f.doc.element} matching the predicate.") """ } + body(Lists) { f -> + """ + val iterator = this.listIterator(size) + while (iterator.hasPrevious()) { + val element = iterator.previous() + if (predicate(element)) return element + } + throw NoSuchElementException("${f.doc.collection.capitalize()} contains no ${f.doc.element} matching the predicate.") + """ + } } templates add f("lastOrNull(predicate: (T) -> Boolean)") { @@ -569,7 +591,7 @@ fun elements(): List { """ } - body(CharSequences, ArraysOfPrimitives, ArraysOfObjects, Lists) { + body(CharSequences, ArraysOfPrimitives, ArraysOfObjects) { """ for (index in this.indices.reversed()) { val element = this[index] @@ -578,6 +600,17 @@ fun elements(): List { return null """ } + body(Lists) { + """ + val iterator = this.listIterator(size) + while (iterator.hasPrevious()) { + val element = iterator.previous() + if (predicate(element)) return element + } + return null + """ + } + } templates add f("findLast(predicate: (T) -> Boolean)") { diff --git a/libraries/tools/kotlin-stdlib-gen/src/templates/Filtering.kt b/libraries/tools/kotlin-stdlib-gen/src/templates/Filtering.kt index d46fb6b6cbf..f6747eb61b5 100644 --- a/libraries/tools/kotlin-stdlib-gen/src/templates/Filtering.kt +++ b/libraries/tools/kotlin-stdlib-gen/src/templates/Filtering.kt @@ -49,8 +49,8 @@ fun filtering(): List { list = ArrayList(resultSize) if (this is List) { - for (index in n..size - 1) { - list.add(this[index]) + for (item in listIterator(n)) { + list.add(item) } return list } @@ -202,7 +202,7 @@ fun filtering(): List { """ } - body(Lists, ArraysOfObjects, ArraysOfPrimitives) { + body(ArraysOfObjects, ArraysOfPrimitives) { """ require(n >= 0) { "Requested element count $n is less than zero." } if (n == 0) return emptyList() @@ -214,6 +214,23 @@ fun filtering(): List { return list """ } + body(Lists) { + """ + require(n >= 0) { "Requested element count $n is less than zero." } + if (n == 0) return emptyList() + val size = size + if (n >= size) return toList() + val list = ArrayList(n) + if (this is RandomAccess) { + for (index in size - n .. size - 1) + list.add(this[index]) + } else { + for (item in listIterator(n)) + list.add(item) + } + return list + """ + } } templates add f("dropWhile(predicate: (T) -> Boolean)") { @@ -316,6 +333,19 @@ fun filtering(): List { return emptyList() """ } + body(Lists) { + """ + if (!isEmpty()) { + val iterator = listIterator(size) + while (iterator.hasPrevious()) { + if (!predicate(iterator.previous())) { + return take(iterator.nextIndex() + 1) + } + } + } + return emptyList() + """ + } doc(Strings) { "Returns a string containing all characters except last characters that satisfy the given [predicate]." } doc(CharSequences) { "Returns a subsequence of this char sequence containing all characters except last characters that satisfy the given [predicate]." } @@ -347,6 +377,27 @@ fun filtering(): List { return toList() """ } + body(Lists) { + """ + if (isEmpty()) + return emptyList() + val iterator = listIterator(size) + while (iterator.hasPrevious()) { + if (!predicate(iterator.previous())) { + iterator.next() + val expectedSize = size - iterator.nextIndex() + if (expectedSize == 0) return emptyList() + return ArrayList(expectedSize).apply { + while (iterator.hasNext()) + add(iterator.next()) + } + } + } + return toList() + """ + // TODO: Use iterator.toList() internal method in 1.1 +// return iterator.toList(size - iterator.nextIndex()) + } doc(Strings) { "Returns a string containing last characters that satisfy the given [predicate]." } doc(CharSequences) { "Returns a subsequence of this char sequence containing last characters that satisfy the given [predicate]." }