What this lesson covers
A String in Kotlin is a sequence of characters, each at an index that starts at 0. This lesson shows how to create strings, read characters, measure length, join strings, cut out pieces with substring, and work around the fact that strings can't be edited in place. The exercise at the end combines all of it: it turns "Java" into "TypeScript".
Creating, indexing and measuring
val greeting = "Hello"
val first = greeting[0] // 'H'
val last = greeting[greeting.length - 1] // 'o'
println(greeting.length) // 5
Square brackets return a Char, not a String, so greeting[0] is 'H' with single quotes. Kotlin has no charAt method: square brackets are the way to read a character. length is a property, so it is written without parentheses. An index outside 0 to length - 1 throws a StringIndexOutOfBoundsException at run time.
Joining strings
The + operator joins two strings into a new one, and += joins and stores the result back in a var:
val name = "John"
val str = "Hello " + name
println(str) // Hello John
var greet = "Welcome"
greet += " to the course, "
greet += name
println(greet) // Welcome to the course, John
Both forms copy the existing text into a fresh string. A few joins cost nothing noticeable, but a loop that appends thousands of times copies more and more text on every pass. For that case use a StringBuilder, which grows in place, and call toString() at the end.
Slicing with substring
substring(startIndex, endIndex) returns the characters from startIndex up to, but not including, endIndex. With one argument it runs to the end of the string.
val str = "JavaScript"
println(str.substring(2, 6)) // vaSc
println(str.substring(4)) // Script
The result has endIndex - startIndex characters. Both indexes must lie between 0 and length; "Hello".substring(3, 9) throws a StringIndexOutOfBoundsException.
Strings are immutable
No operation changes a String after it is created. Assigning to a character is a compile error:
val myStr = "Bob"
myStr[0] = 'J' // error: no 'set' operator method providing array access.
The only way to "change" text is to build a new string and store it in a var:
var myStr = "Bob"
myStr = "Job"
myStr = "J" + myStr.substring(1) // Job again, built from pieces
When you join a single Char and a String, put the Char first and the string second ('J' + "ob"), or convert it with toString(). Writing "ob" + 'J' also works, and gives "obJ".
Changing characters with a CharArray
Replacing one character by building a whole new string takes O(n) time. If you need many changes, copy the characters into a CharArray, whose elements can be assigned in O(1), edit it, and convert it back:
var str = "Andy"
val myArr = str.toCharArray() // ['A', 'n', 'd', 'y']
myArr[0] = 'a'
myArr[2] = 'D'
str = String(myArr)
println(str) // anDy
toCharArray() makes a copy, so the original string is untouched. String(myArr) builds a new string from the array. Note that myArr is a val and can still change its elements: val fixes which array the variable points to, not what is inside it.
Looping over characters
val myStr = "Andy"
for (i in 0 until myStr.length) {
println(myStr[i])
}
This prints A, n, d and y on separate lines. 0 until myStr.length excludes the upper bound, so the last index used is length - 1. When the index itself isn't needed, for (c in myStr) is shorter. Either way the loop takes O(n) time.
The exercise, step by step
- Create
langas avar. It starts as"Java"and gets reassigned in the next step, sovalwould not compile. - Append with
+=.lang += "script"builds"Javascript"and stores it inlang. - Cut the tail.
lang.substring(4)starts at index 4, thes, and runs to the end, giving"script". Avalis enough because it is never reassigned. - Join a new prefix.
"Type" + substrgives"Typescript", stored in avarnamednewLangbecause step 7 reassigns it. - Copy into an array.
newLang.toCharArray()gives the mutable copy that the capital letter will be written into. - Edit index 4. The characters of
"Typescript"are T, y, p, e, s, so the lower-casesis at index 4. Assign'S'with single quotes, because the array holdsCharvalues. - Build the string back.
String(charArray)is assigned tonewLang, which now holds"TypeScript".
var lang = "Java"
lang += "script"
val substr = lang.substring(4)
var newLang = "Type" + substr
val charArray = newLang.toCharArray()
charArray[4] = 'S'
newLang = String(charArray)
Common mistakes
- Using double quotes for a character.
charArray[4] = "S"is a type mismatch: aStringis not aChar. - Declaring a variable with
valand then reassigning it. The compiler reports that avalcannot be reassigned. - Off-by-one in
substring. The end index is excluded, and the start index is included. - Expecting
toCharArray()to change the string. It returns a separate copy.
Key takeaways
- Index with
str[i], measure withstr.length, slice withsubstring. - Strings are immutable; build a new one and store it in a
var. - For many edits, use a
CharArrayandString(array); for many joins, use aStringBuilder.