Strings in Kotlin

Learn via video courses
Topics Covered

Overview

Strings are a fundamental component of nearly every programming language, serving as the building blocks for handling textual data. Working with strings in Kotlin is made intuitive and efficient through a comprehensive set of features and functions. From simple tasks like concatenation to more complex operations like pattern matching and interpolation, Kotlin provides a robust toolkit that empowers developers to manipulate strings with ease. To get better acquainted with Strings in Kotlin, let's dive in.

Introduction

Strings in Kotlin are sequences of characters and serve as a fundamental construct for dealing with textual data, whether it is a simple word, a sentence, or even complex multiline content. Although strings in Kotlin share similarities with their Java counterparts, they introduce new features that enhance their functionality. An essential characteristic of strings in Kotlin is their immutability, which means that once created, the contents and length of a string cannot be altered.

Here's how to declare strings in Kotlin:

Alternatively, the type can be explicitly mentioned as well:

String Constructor and Properties

String Constructor

String constructors in Kotlin are methods that facilitate the creation of strings. They offer various ways to initialize and manipulate textual data. Kotlin provides different constructors to cater to diverse scenarios, making it convenient to work with strings in different contexts. Here are some common string constructors in Kotlin:

  1. Literal String:
    The most common way to create a string is by enclosing the desired text within double quotes. This is known as a literal string.

    Code:

    Output:

  2. String Constructor from Char Array:
    The String class has a constructor that takes a character array and produces a string from its contents.

    Code:

    Output:

  3. String Interpolation:
    String interpolation is a unique feature in Kotlin that allows you to embed expressions within a string using the $ symbol.

    Code:

    Output:

  4. Raw String (Multi-line String):
    Raw strings, enclosed in triple quotes """, are used to create strings that maintain line breaks and formatting.

    Code:

    Output:

  5. Empty String:
    You can create an empty string using the String constructor with no arguments.

    Code:

    Output:

Properties

  1. Length:
    The length property returns the number of characters in the string.

    Code:

    Output:

  2. Indexing:
    String elements in Kotlin can be accessed using zero-based indexing, where individual characters can be accessed using square brackets.

    Code:

    Output:

  3. Equality and Comparison:
    Strings can be compared for equality using the == operator. Additionally, you can perform lexicographic comparison using compareTo() function.

    Code:

    Output:

  4. Substring:
    Kotlin provides methods for extracting substrings from a string. The substring() function allows you to specify the starting and ending indices.

    Code:

    Output:

  5. Conversion:
    Strings can be converted to uppercase and lowercase using the toUpperCase() and toLowerCase() functions.

    Code:

    Output:

  6. SubSequence(start, end):
    This function provides a substring that begins at the start index and concludes just before the end index.

    Code:

    Output:

String Elements and Templates

String Elements

A string element in Kotlin refers to a single character, digit, or any other symbol present within a string. Accessing these individual elements is achieved through various methods. One fundamental approach involves using the syntax string[index], where the index indicates the position of the desired element. In a string, elements are arranged in sequential order, indexed from 0 to string.length - 1.

Kotlin provides three distinct approaches to access string elements:

Index-Based Access:

By employing index-based access, you can retrieve the character located at a specified index within the string. For instance, string[2] retrieves the element at the third position in the string.

Using the "get" Function:

The get function can also be employed to retrieve a specific element from a string. This function takes the desired index as an argument and returns the corresponding character.

Iteration via Loops:

Another method to access string elements is by utilizing looping structures such as for or while. These loops enable you to iterate over the characters within the string sequentially. During each iteration, you gain access to a single element.

Consider the following code snippet:

Output:

Explanation:

In this code, we define a string text containing the phrase "Hello, Kotlin!". We then demonstrate the three methods of accessing string elements:

  1. Index-Based Access:
    We use text[7] to retrieve the element at index 7, which is the character 'K'.
  2. Using the get Function:
    We utilize text.get(0) to access the element at index 0, which is the character 'H'.
  3. Iterating over String Elements:
    We use a for loop to iterate through each character in the string and print them individually. This loop allows us to access and process each element sequentially.

String Template

String templates in Kotlin refer to the code that evaluates and integrates its output into a string. Both escaped and raw strings support template expressions, which start with $ followed by a variable or expression in curly braces {}. This feature streamlines string construction by effortlessly inserting values or expressions, enhancing code clarity and conciseness.

Code:

Output:

Explanation:

In this code, we utilize string templates to integrate values and expressions into strings in Kotlin:

  1. The variable name and age are embedded using $name and $age in the string template message. The values of these variables are automatically substituted into the final string.
  2. In the result string template, we use ${x + y} to evaluate the expression x + y and include the result in the string.

String Literals

String literals in Kotlin are representations of textual data. Kotlin offers two main types of string literals: escaped strings and raw strings (including multi-line strings). These types serve distinct purposes and offer different ways to handle special characters and formatting.

1. Escaped String

An escaped string is a traditional way of representing strings in most programming languages. It uses escape characters to include special characters within the string. In Kotlin, escape characters are preceded by a backslash \.

Code:

Output:

Explanation:

In this case, \n represents a newline character within the string. Escaped strings are suitable for simple strings with minimal formatting requirements.

2. Raw string - Multi-line string

A raw string, also known as a multi-line string, is enclosed within triple quotes """. It preserves the line breaks and formatting as-is, making it convenient for representing multiline text, such as code snippets or text with formatting.

Code:

Output:

Explanation:

In this example, the indentation within the triple-quoted string is preserved, making it readable while maintaining the original formatting.

3. Escape Characters

Escape characters are used to represent special characters within strings. Some common escape characters in Kotlin include.

  • \n: Newline
  • \t: Tab
  • \": Double quote
  • \': Single quote
  • \\: Backslash

Code:

Output:

Explanation:

In this code, \" represents a double quote, and \n represents a newline.

String Equality

String equality in Kotlin refers to determining whether two strings in Kotlin are the same or equivalent. Kotlin provides two main concepts of string equality: structural equality and referential equality. These concepts allow you to compare strings based on different criteria, offering flexibility in handling string comparisons.

1. Structural Equality

Structural equality, often referred to as content equality, evaluates whether the contents of two strings are identical. It checks whether the characters within the strings are the same, regardless of their memory locations. To perform structural equality checks, you use the == operator or the .equals() method.

Code:

Output:

Explanation:

In this example, string1 and string2 are structurally equal because their content is the same. string3 is also considered structurally equal to string1 when a case-insensitive comparison is used.

2. Referential Equality

Referential equality, also known as identity equality, determines whether two strings reference the same memory location. It checks if two variables point to the exact same string object in memory. The referential equality check is performed using the === operator.

Code:

Output:

Explanation:

In this case, stringA and stringC are referentially equal because they both point to the same memory location. Hence areReferentiallyEqual2 evaluates to true.

One must intuitively feel that areReferentiallyEqual1 should evaluate to false as stringA and stringB refer to two different string objects. Due to Kotlin's string interning optimization, string literals are usually stored in a string pool to conserve memory. Since the strings are the same and referentially equal, the === operator will indeed return true, indicating that they share the same memory location.

To understand the difference between structural and referential equality better, consider the following code snippet.

Code:

Output:

Explanation:

In this example, str1 and str2 have the same content ("Hello"), so the structural equality is true. However, since str2 is explicitly created as a new string object, it will have a different memory location than str1, resulting in referential equality being false.

String Functions

FunctionsDescription
compareTo(other: String): IntIt performs a comparison between the current object and a specified object to establish their order. If the current object is equal to the specified object, it results in zero.
get(index: Int): CharIt returns the character at the provided index within the current character sequence.
plus(other: Any?): StringIt produces a concatenated string by combining the string representation of the provided second string.
subSequence(startIndex: Int,endIndex: Int): CharSequenceIt generates a fresh character sequence from the existing one, beginning at the startIndex and extending up to the endIndex.
CharSequence.contains(other: CharSequence, ignoreCase: Boolean = false)It evaluates as true if the character sequence encompasses the specified alternative character sequence.
CharSequence.count(): IntIt provides the length of the character sequence.
String.drop(n: Int): StringIt retuns a string by excluding the initial n characters.
String.dropLast(n: Int): StringIt retuns a string by excluding the last n characters.
String.dropWhile(predicate: (Char) -> Boolean): StringIt generates a character sequence comprising all characters except those at the beginning that meet the specified predicate.
CharSequence.elementAt(index: Int): CharIt retrieves a character at the specified index, or raises an IndexOutOfBoundsException if the index is out of range within the character sequence.
CharSequence.indexOf(char: Char, startIndex: Int = 0,ignoreCase: Boolean = false): IntIt provides the index of the initial appearance of the specified character, commencing from the provided index value.
CharSequence.indexOfFirst(predicate: (Char) -> Boolean): IntIt returns the index of the first character that meets the criteria defined by the given predicate, or -1 if no such character exists within the character sequence.
CharSequence.indexOfLast(predicate: (Char) -> Boolean): IntIt provides the index of the last character that meets the criteria defined by the given predicate, or -1 if no such character exists within the character sequence.
CharSequence.getOrElse(index: Int, defaultValue: (Int) ->Char): CharIt returns the character at the indicated index or the outcome of invoking the defaultValue function if the index is beyond the bounds of the existing character sequence.
CharSequence.getOrNull(index: Int): Char?It returns a character at the provided index or yields null if the index falls outside the bounds of the character sequence.

Extension Functions with String in Kotlin

Extension functions are a powerful feature in Kotlin that allow you to add new functionality to existing classes without modifying their source code. This concept extends to the String class as well. You can create extension functions specifically for String objects to enhance their capabilities and create more expressive and concise code.

Creating an Extension Function for Strings in Kotlin:

To define an extension function for the String class, you declare a function with the desired functionality and prefix it with the receiver type (String in this case). The receiver type becomes the context in which the function is called. Let's consider an example extension function that counts the number of words in a string.

Code:

Explanation:

In this example, we've defined an extension function named countWords() for the String class. It splits the string into words using a regular expression that matches whitespace characters, and then returns the count of words.

Using Extension Functions:

Once you've defined an extension function, you can use it just like any other member function on a String object:

Code:

Conclusion

Here are the key takeaways regarding strings in Kotlin:

  1. Strings in Kotlin are immutable, meaning their content cannot be changed after creation. This ensures data integrity and simplifies reasoning about the state of strings.
  2. Kotlin offers string templates, allowing you to embed variables or expressions directly within strings. This enhances code readability and reduces the need for explicit concatenation.
  3. Strings in Kotlin can be compared for both structural equality (content-based) using the == operator or equals() method, and referential equality (memory reference) using the === operator.
  4. Kotlin provides raw strings enclosed within triple quotes """. They maintain line breaks and formatting, making them suitable for representing multi-line text, such as code snippets or documentation.
  5. You can extend the functionality of the String class by defining extension functions. This allows you to add new methods to strings without modifying the original class, promoting code modularity and reusability.