Mutable vs Immutable Strings
Table of Contents + β
You write some code. You take a string like "Hello". You try to change one letter inside it. And the language just refuses. The string does not change. Instead you get a whole new string back. Confusing, right? This happens because of one idea called immutability. Once you understand it, a lot of string behavior suddenly makes sense. So let us go through it slowly.
π What Does Immutable Mean?
A string is immutable when you cannot change it after it is created. Immutable just means βcannot be changedβ. Once the string exists, its letters are locked.
Now this sounds strange. We change strings all the time, donβt we? We add text. We replace letters. So how can they be locked?
Here is the trick. When you βchangeβ an immutable string, the language does not edit the old one. It makes a brand new string with the change. Then it throws the old one away. Or it keeps the old one if something else is still using it. Either way, the old string stays exactly as it was.
A mutable string is the opposite. Mutable means βcan be changedβ. You can edit its letters in the same place in memory. You do not make a new one.
π« A Real-World Way to Picture It
Think of a chocolate bar that is already wrapped and sealed.
If the bar is immutable, you cannot open it and remove one piece. You want a bar with one piece missing? Then you have to get a fresh new bar and unwrap that one. The old sealed bar stays sealed forever.
If the bar is mutable, you just open it and take a piece. The same bar now has a piece gone. No new bar needed.
So the question for any language is simple. When you change a string, do you get a fresh bar? Or do you edit the same one?
π Which Languages Have Which?
Different languages made different choices. Here is the short version.
Immutable strings (a change makes a new string):
- Java
String - Python
str - JavaScript strings
Mutable strings (you can change them in place):
- C strings, which are really just arrays of characters (
char[]) - C++
std::string, which lets you append with+=or grow it in place - Java
StringBuilder, a special class made for changing text in place
| Type | Language | Mutable? |
|---|---|---|
String | Java | No (immutable) |
str | Python | No (immutable) |
| string | JavaScript | No (immutable) |
std::string | C++ | Yes (mutable) |
StringBuilder | Java | Yes (mutable) |
char[] | C | Yes (mutable) |
π§ͺ See Immutability With Your Own Eyes
Let us prove it. We take a string. We ask for a changed version. Then we check the original. If the original is untouched, the string is immutable.
In the code below we make a string. We build a new uppercase version from it. Then we print both. Watch the first one. It does not change.
# Python str is immutable. .upper() returns a NEW string.def demo(): text = "hello" upper = text.upper() # makes a brand new string
# text is untouched print("original:", text) print("upper copy:", upper)
demo()The output of the above code will be:
original: helloupper copy: HELLONote
In Python and JavaScript you cannot even do text[0] = "J" on a string. The language stops you. In C you can, because a C string is just an array of letters sitting in memory.
π€ Why Would Anyone Make Strings Immutable?
Good question. If you cannot change them, that sounds annoying. But immutability buys you real safety. And here is why.
- Two parts of your program can share the same string. Nobody can secretly change it under the other one. That is safe sharing.
- The string can be used as a key in a map or a set. Keys must stay the same. If a key could change, the map would get lost.
- The language can reuse the same string in memory instead of making copies. This trick is called interning. Interning means storing one copy of a repeated string and pointing everyone to it.
So immutability is mostly about being safe and predictable. You always know a string is what it was when you got it.
π The Hidden Cost: Building Strings in a Loop
Now the bad news. Immutability has one trap. And almost every beginner falls into it.
Say you want to build one big string. You do it by adding small pieces over and over in a loop. Like joining ten thousand words.
The problem is that each + makes a new string. So on every step the language copies everything you have so far. Then it adds the new piece. The string keeps getting longer. So each copy is bigger than the last.
If you add n pieces, the total copying work grows like 1 + 2 + 3 + ... + n. That sum is about n squared. So this loop is O(n^2). For a big n it becomes painfully slow.
Caution
This is the most common string performance bug for freshers. The code looks innocent. It is just a += inside a loop. But it quietly turns into O(n^2), because each step rebuilds the whole string from scratch.
β The Right Way to Build a Big String
The fix is simple. Do not glue pieces one by one onto an immutable string. Instead, collect the pieces in something mutable. Then join them all at once at the end. That join is O(n).
Each language has its own tool for this:
- Java: use
StringBuilder, which is mutable. Then call.toString()at the end. - Python: collect pieces in a list. Then use
"".join(list). - JavaScript: push pieces into an array. Then use
array.join(""). - C++:
std::stringis mutable, so callreserve()once and append with+=in place. - C: the array is already mutable, so just write into it.
The code below builds the string "item0item1...item4" the fast way. Notice we never do += on an immutable string inside the loop.
# Collect pieces in a list (mutable), then join once at the end.def build_string(): pieces = [] # a mutable list
for i in range(5): pieces.append("item" + str(i)) # cheap list append
result = "".join(pieces) # one join, O(n) print(result)
build_string()The output of the above code will be:
item0item1item2item3item4Tip
Rule of thumb: if you are joining a few strings, plain + is totally fine and clear. Only switch to StringBuilder / list-join / array-join when you are joining many pieces in a loop. That is where O(n^2) bites.
β±οΈ The Complexity, Side by Side
This table shows why the right way wins. n is the total number of characters in the final string.
| Way of Building | What Happens Each Step | Total Time |
|---|---|---|
+= on an immutable string in a loop | Copies the whole string so far | O(n^2) |
| StringBuilder / list-join / array-join | Cheap append, one final join | O(n) |
π§© What Youβve Learned
- β Immutable means a string cannot be changed after it is created. Any βchangeβ makes a brand new string.
- β
Java
String, Pythonstr, and JavaScript strings are immutable. C strings (char[]), C++std::string, and JavaStringBuilderare mutable. - β Immutability gives you safe sharing, valid map keys, and reuse through interning.
- β
Building a big string with
+=in a loop is O(n^2), because each step copies everything so far. - β
The fast O(n) way is
StringBuilderin Java, list +joinin Python, and array +joinin JavaScript.
Check Your Knowledge
Test what you learned. Pick an answer for each question, then click Check.
- 1
What happens when you 'change' an immutable string in Java or Python?
Why: Immutable strings cannot be edited, so any change produces a new string while the original is left untouched.
- 2
Which of these is mutable (can be changed in place)?
Why: StringBuilder is built for changing text in place; the other three are immutable.
- 3
Why is building a big string with += inside a loop slow?
Why: Each concatenation makes a new copy of everything built so far, so the total work grows like n squared.
- 4
What is the recommended fast way to build a big string in Python?
Why: Appending to a list is cheap, and a single join at the end is O(n) instead of O(n^2).