Search code examples
stringkotlinstringbuffer

Is StringBuffer the Kotlin way to handle multiple string concatenation like in java?


What would be the kotlin way to handle multiple string concatenation?

--edit-- placing the piece of code that led me to this doubt

fun getNCharsFromRange(n: Int, range: CharRange): String {
    val chars = range.toList()
    val buffer = StringBuffer()
    while (buffer.length < n) {
        val randomInt = Random.Default.nextInt(0, chars.lastIndex)
        val newchar = chars[randomInt]
        val lastChar = buffer.lastOrNull() ?: ""
        if (newchar != lastChar) {
            buffer.append(newchar)
        }
    }
    return buffer.toString()
}

Solution

  • A StringBuilder is the standard way to construct a String in Kotlin, as in Java.

    (Unless it can be done in one line, of course, where a string template is usually better than Java-style concatenation.)

    Kotlin has one improvement, though: you can use buildString to handle that implicitly, which can make the code a little more concise.  For example, your code can be written as:

    fun getNCharsFromRange(n: Int, range: CharRange): String {
        val chars = range.toList()
        return buildString {
            while (length < n) {
                val randomInt = Random.Default.nextInt(0, chars.lastIndex)
                val newChar = chars[randomInt]
                val lastChar = lastOrNull() ?: ""
                if (newChar != lastChar)
                    append(newChar)
            }
        }
    }
    

    That has no mention of buffer: instead, buildString creates a StringBuilder, makes it available as this, and then returns the resulting String.  (So length, lastOrNull(), and append refer to the StringBuilder.)

    For short code, this can be significantly more concise and clearer; though the benefits are much less clear with longer code.  (Your code may be in the grey area between…)


    Worth pointing out that the function name is misleading: it avoids repeated characters, but allows duplicates that are not consecutive.  If that's deliberate, then it would be worth making clear in the function name (and/or its doc comment).  Alternatively, if the intent is to avoid all duplicates, then there's an approach which is much simpler and/or more efficient: to shuffle the range (or at least part of it).

    Using existing library functions, and making it an extension function on CharRange, the whole thing could be as simple as:

    fun CharRange.randomChars(n: Int) = shuffled().take(n).joinToString("")
    

    That shuffles the whole list, even if only a few characters are needed.  So it would be even more efficient to shuffle just the part needed.  But there's no library function for that, so you'd have to write that manually.  I'll leave it as an exercise!