diff --git a/libraries/stdlib/src/kotlin/text/Strings.kt b/libraries/stdlib/src/kotlin/text/Strings.kt index 54522ae1d0e..7d597a8505f 100644 --- a/libraries/stdlib/src/kotlin/text/Strings.kt +++ b/libraries/stdlib/src/kotlin/text/Strings.kt @@ -1167,7 +1167,7 @@ public fun CharSequence.split(vararg delimiters: String, ignoreCase: Boolean = f if (delimiters.size == 1) { val delimiter = delimiters[0] if (!delimiter.isEmpty()) { - return split(delimiters[0], ignoreCase, limit) + return split(delimiter, ignoreCase, limit) } } @@ -1212,33 +1212,20 @@ private fun CharSequence.split(delimiter: String, ignoreCase: Boolean, limit: In var currentOffset = 0 var nextIndex = indexOf(delimiter, currentOffset, ignoreCase) - val isLimited = limit > 0 - // This condition cannot be moved inside of ctor call because it will kill laziness of internal array allocation - val result = if (isLimited) ArrayList(limit.coerceAtMost(10)) else ArrayList() - - while (nextIndex != -1) { - if (!isLimited || result.size < limit - 1) { - result.add(substring(currentOffset, nextIndex)) - currentOffset = nextIndex + delimiter.length - nextIndex = indexOf(delimiter, currentOffset, ignoreCase) - } else { - result.add(substring(currentOffset, length)) - currentOffset = length - break - } - } - - if (currentOffset == 0) { - // If no matches found return single element list - // instead of triggering allocation of underlying array in 'result' - // Speedup is ~15% for small string + if (nextIndex == -1) { return listOf(this.toString()) } - if (!isLimited || result.size < limit) { - result.add(substring(currentOffset, length)) + val isLimited = limit > 0 + val result = ArrayList(if (isLimited) limit.coerceAtMost(10) else 10) + while (nextIndex != -1 && (!isLimited || result.size < limit - 1)) { + result.add(substring(currentOffset, nextIndex)) + currentOffset = nextIndex + delimiter.length + // Do not search for next occurrence if we're reaching limit + nextIndex = if (result.size == limit - 1) break else indexOf(delimiter, currentOffset, ignoreCase) } + result.add(substring(currentOffset, length)) return result } diff --git a/libraries/stdlib/test/text/StringTest.kt b/libraries/stdlib/test/text/StringTest.kt index 2f9641053f3..081296477a2 100644 --- a/libraries/stdlib/test/text/StringTest.kt +++ b/libraries/stdlib/test/text/StringTest.kt @@ -527,12 +527,16 @@ class StringTest { assertEquals(listOf("test"), (+"test").split(*arrayOf()), "empty list of delimiters, none matched -> entire string returned") assertEquals(listOf("abc", "def", "123;456"), (+"abc;def,123;456").split(';', ',', limit = 3)) + assertEquals(listOf("abc;def,123;456"), (+"abc;def,123;456").split(';', ',', limit = 1)) assertEquals(listOf("abc", "def", "123", "456"), (+"abc=-def==123=456").split("==", "=-", "=")) assertEquals(listOf("", "a", "b", "c", ""), (+"abc").split("")) assertEquals(listOf("", "a", "b", "b", "a", ""), (+"abba").split("", "a")) assertEquals(listOf("", "", "b", "b", "", ""), (+"abba").split("a", "")) + assertEquals(listOf("", "bb", ""), (+"abba").split("a", "c")) + assertEquals(listOf("", "bb", ""), (+"abba").split('a', 'c')) + } @Test fun splitSingleDelimiter() = withOneCharSequenceArg { arg1 -> @@ -543,10 +547,21 @@ class StringTest { assertEquals(listOf("abc", "def", "123,456"), (+"abc,def,123,456").split(',', limit = 3)) assertEquals(listOf("abc", "def", "123,456"), (+"abc,def,123,456").split(",", limit = 3)) + assertEquals(listOf("abc", "def", "123,456,789"), (+"abc,def,123,456,789").split(',', limit = 3)) + assertEquals(listOf("abc", "def", "123,456,789"), (+"abc,def,123,456,789").split(",", limit = 3)) + + assertEquals(listOf("abc", "def", "123,456,789,"), (+"abc,def,123,456,789,").split(',', limit = 3)) + assertEquals(listOf("abc", "def", "123,456,789,"), (+"abc,def,123,456,789,").split(",", limit = 3)) + + assertEquals(listOf("abc,def,123,456,789,"), (+"abc,def,123,456,789,").split(',', limit = 1)) + assertEquals(listOf("abc,def,123,456,789,"), (+"abc,def,123,456,789,").split(",", limit = 1)) + assertEquals(listOf("abc", "def", "123", "456"), (+"abc
def
123
456").split("
", ignoreCase = true)) assertEquals(listOf("abc", "def
123
456"), (+"abc
def
123
456").split("
", ignoreCase = false)) assertEquals(listOf("a", "b", "c"), (+"a*b*c").split("*")) + assertEquals(listOf("", "bb", ""), (+"abba").split("a")) + assertEquals(listOf("", "bb", ""), (+"abba").split('a')) } @Test fun splitToLines() = withOneCharSequenceArg { arg1 ->