Use StringBuilder in String.replace (2x faster)

KT-41799
This commit is contained in:
Francesco Vasco
2020-09-10 07:50:21 +02:00
committed by Ilya Gorbunov
parent 51a37bffff
commit 4a0109cf44
@@ -61,10 +61,14 @@ public actual fun String?.equals(other: String?, ignoreCase: Boolean = false): B
*/ */
@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") @Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
public actual fun String.replace(oldChar: Char, newChar: Char, ignoreCase: Boolean = false): String { public actual fun String.replace(oldChar: Char, newChar: Char, ignoreCase: Boolean = false): String {
if (!ignoreCase) // prefer case-insensitive platform implementation
return (this as java.lang.String).replace(oldChar, newChar) if (!ignoreCase) return (this as java.lang.String).replace(oldChar, newChar)
else
return splitToSequence(oldChar, ignoreCase = ignoreCase).joinToString(separator = newChar.toString()) return buildString(length) {
this@replace.forEach { c ->
append(if (c.equals(oldChar, ignoreCase)) newChar else c)
}
}
} }
/** /**
@@ -72,9 +76,29 @@ public actual fun String.replace(oldChar: Char, newChar: Char, ignoreCase: Boole
* with the specified [newValue] string. * with the specified [newValue] string.
*/ */
@Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS") @Suppress("ACTUAL_FUNCTION_WITH_DEFAULT_ARGUMENTS")
public actual fun String.replace(oldValue: String, newValue: String, ignoreCase: Boolean = false): String = public actual fun String.replace(oldValue: String, newValue: String, ignoreCase: Boolean = false): String {
splitToSequence(oldValue, ignoreCase = ignoreCase).joinToString(separator = newValue) // prefer case-insensitive platform implementation
if (!ignoreCase) return (this as java.lang.String).replace(oldValue, newValue)
var occurrenceIndex: Int = indexOf(oldValue, 0, ignoreCase)
// FAST PATH: no match
if (occurrenceIndex < 0) return this
val oldValueLength = oldValue.length
val searchStep = oldValueLength.coerceAtLeast(1)
val newLengthHint = length - oldValueLength + newValue.length
if (newLengthHint < 0) throw OutOfMemoryError()
val stringBuilder = StringBuilder(newLengthHint)
var i = 0
do {
stringBuilder.append(this, i, occurrenceIndex).append(newValue)
i = occurrenceIndex + oldValueLength
if (occurrenceIndex >= length) break
occurrenceIndex = indexOf(oldValue, occurrenceIndex + searchStep, ignoreCase)
} while (occurrenceIndex > 0)
return stringBuilder.append(this, i, length).toString()
}
/** /**
* Returns a new string with the first occurrence of [oldChar] replaced with [newChar]. * Returns a new string with the first occurrence of [oldChar] replaced with [newChar].