Java String Manipulation for Coding Interviews: The Reference Guide

==vs.equals(): use.equals()for content comparison —==tests reference identity and silently passes when both literals come from the string poolStringBuilderover+: concatenating with+in a loop is O(n²);StringBuilder.append()is O(1) amortized with a doubling buffersubstring()cost: Java 7+ always copies the slice — calling it inside a loop silently turns an O(n) solution into O(n²)split(".")returns[]: the argument is a regex;.matches any character — usesplit("\\.")for a literal dot- Char arithmetic:
c - 'a'maps letters to 0-based indices;char + intreturnsint, so cast back with(char)before appending to aStringBuilder - Method time complexity:
charAt()andlength()are O(1);indexOf(),contains(),replace(), andtrim()are all O(n) - Java 11 additions: prefer
strip()overtrim()for Unicode correctness;isBlank()andrepeat(n)are fair game on modern JDK interviews

Java strings will betray you in a coding interview if you don't know how they actually work. They look simple. Then you compare two strings with ==, or build one in a loop, and your solution is either wrong or O(n²). Quietly. No warning. Just a no-hire. This guide covers what you need, with the exact time complexities and the pitfalls that end interviews early.
Strings Are Immutable, and That Changes Everything
Every Java String is backed by an immutable character array. That sounds like a textbook footnote, but it has a direct consequence: every method that modifies a string returns a new String object. toUpperCase(), replace(), trim(), substring(), all of them. The original is untouched.
This matters because:
String s = "hello"; s.toUpperCase(); // does nothing to s s = s.toUpperCase(); // this is what you mean
The second consequence is the string pool. String literals are interned automatically. "hello" == "hello" is true because they point to the same pool object. new String("hello") == "hello" is false because new String() always allocates a fresh object on the heap. This leads directly to the interview's most reliable Java trap.
== vs .equals(): Java's Most Loyal Footgun
String a = "hello"; String b = "hello"; String c = new String("hello"); System.out.println(a == b); // true (both from pool) System.out.println(a == c); // false (c is heap object) System.out.println(a.equals(c)); // true (content comparison)
Always use .equals() to compare string content. The only time == is correct is when you explicitly want reference equality, which is almost never in interview problems. If you need case-insensitive comparison, use .equalsIgnoreCase().
This trap works because == looks like it should compare values. In Python it does. In C it does. In Java, for strings, it checks if two variables point to the same object in memory. You'll get true enough of the time (thanks to the pool) that it seems fine, then suddenly it's not, and you're staring at a failing test case five minutes before time runs out.

Galaxy-brain Java developer: uses == on strings, gets true from the pool for 20 minutes, declares the code correct.
String vs StringBuilder: The O(n²) Cliff
Concatenating inside a loop with + is the single most expensive mistake in Java string problems.
// O(n²), each + creates a new String, copying all previous chars String result = ""; for (String word : words) { result += word; } // O(n) amortized, StringBuilder backs into a doubling array StringBuilder sb = new StringBuilder(); for (String word : words) { sb.append(word); } String result = sb.toString();
Here's what's actually happening in the first version: iteration 1 copies 1 character, iteration 2 copies 2 characters, iteration 3 copies 3 characters... you're summing 1 + 2 + 3 + ... + n. That's n(n+1)/2. That's O(n²). Every + operator in that loop allocates a brand-new String and copies everything over from scratch.
When StringBuilder runs out of capacity (default 16 characters), it doubles: newCapacity = (capacity * 2) + 2. So append() is O(1) amortized, O(n) in the rare doubling step. The final toString() is O(n).
See why string concatenation in a loop is O(n²) for the derivation.

The exact moment you realize your "O(n)" string builder is quadratic because you used + in the loop.
The StringBuilder methods you'll actually use:
| Method | Time | Notes |
|---|---|---|
append(x) | O(1) amortized | accepts char, int, String, boolean |
insert(i, x) | O(n) | shifts elements right |
delete(i, j) | O(n) | shifts elements left |
reverse() | O(n) | in-place on the array |
charAt(i) | O(1) | direct array access |
length() | O(1) | field read |
toString() | O(n) | copies into new String |
For building a result character by character, append to a StringBuilder and call toString() at the end. For reversing a string, new StringBuilder(s).reverse().toString() is the idiomatic one-liner.
Which String Methods Are O(n)? Know Before You Claim Complexity.
These come up constantly in complexity analysis. Know them cold.
| Method | Time | Notes |
|---|---|---|
charAt(i) | O(1) | direct array index |
length() | O(1) | stored field |
substring(i, j) | O(j-i) | always copies in Java 7+ |
indexOf(char) | O(n) | linear scan |
indexOf(String) | O(n·m) | naive search, m = pattern length |
contains(s) | O(n·m) | delegates to indexOf |
split(regex) | O(n) for simple patterns | regex engine; dot needs escaping |
replace(char, char) | O(n) | creates new string |
compareTo() | O(min(n,m)) | lexicographic |
startsWith() / endsWith() | O(m) | m = prefix/suffix length |
trim() / strip() | O(n) | creates new string |
toCharArray() | O(n) | always copies |
String.valueOf(int) | O(d) | d = digit count |
The substring() trap: Before Java 7 update 6, substring() shared the backing array (O(1) time, but caused memory leaks). Since then it always copies. In interviews, treat substring() as O(k) where k is the length of the extracted portion.
The split(".") trap: The argument to split() is a regex. . matches any character. split(".") returns an empty array. Every single character is treated as a delimiter, so there's nothing left to split into. No error. No warning. Just an empty array and a confused candidate.
"a.b.c".split(".") // returns [], every char is a delimiter "a.b.c".split("\\.") // returns ["a", "b", "c"], correct
Use split("\\.") for a literal dot. split(",") for a comma is fine since , has no special regex meaning.
Working With Characters: The Part Most Candidates Get Wrong
When you need to process individual characters, toCharArray() gives you a char[] that you can index and mutate:
char[] chars = s.toCharArray(); // swap, sort, count, all fine on the array String result = new String(chars);
The Character class has the methods you need for classification:
Character.isDigit(c) // '0'-'9' Character.isLetter(c) // any Unicode letter Character.isLetterOrDigit(c) Character.isUpperCase(c) Character.isLowerCase(c) Character.toUpperCase(c) // returns char Character.toLowerCase(c) // returns char
Char arithmetic is the idiom for mapping characters to array indices:
int[] freq = new int[26]; for (char c : s.toCharArray()) { freq[c - 'a']++; // maps 'a'→0, 'b'→1, ..., 'z'→25 }
This works because Java char arithmetic operates on Unicode code points. 'a' is 97. c - 'a' gives you the 0-based index. Just make sure you're only doing this when the input is guaranteed lowercase ASCII. For mixed case, normalize first with Character.toLowerCase(c).
One subtle thing: char + int returns an int, not a char. If you're building a string character by character:
// Wrong: appends the int value 98, not 'b' sb.append('a' + 1); // Correct: cast back to char sb.append((char) ('a' + 1));
Three Conversions Worth Memorizing
These come up in almost every string-heavy problem.
int to String:
String s = String.valueOf(42); // "42" String s = Integer.toString(42); // "42", same result String s = "" + 42; // works but avoid in loops
String to int:
int n = Integer.parseInt("42"); // 42 int n = Integer.parseInt("-7"); // -7 // throws NumberFormatException on invalid input
char to its numeric value:
char c = '7'; int digit = c - '0'; // 7, maps '0'→0, '1'→1, ..., '9'→9 int digit = Character.getNumericValue(c); // also 7
The Five Pitfalls That End Interviews
1. Comparing strings with ==. It works right up until it doesn't. The string pool saves you for literals but not for anything computed at runtime. Use .equals() every time.
2. Forgetting substring() is O(k). A two-pointer approach that takes O(n) substrings can silently become O(n²). Work on index ranges instead of materializing substrings when you're slicing frequently.
3. Mutating while iterating. Strings are immutable so this can't happen directly, but if you convert to char[] and iterate while modifying, track your indices carefully.
4. split(".") eating your input. The argument is a regex. . is "any character." Your string disappears. Test your split delimiter mentally before writing it.
5. Calling toString() inside the loop. When accumulating into a StringBuilder, call toString() once at the end. Calling it inside the loop materializes a new string every iteration and defeats the purpose of using StringBuilder in the first place.
What Java 11 Added That Actually Matters
If your interviewer is on a modern JDK, these occasionally come up:
isBlank(): O(n), true if empty or all whitespace (Unicode-aware, unlikeisEmpty())strip(): O(n), liketrim()but handles Unicode whitespace. Preferstrip()overtrim()for correctness.repeat(n): O(n),"ab".repeat(3)returns"ababab"
trim() only removes ASCII space and control characters (≤ ' '). strip() removes all Unicode whitespace. For interview problems with ASCII input, they're equivalent, but strip() is the right default.
Four Java String Patterns That Cover Most Interview Problems
Check if two strings are anagrams:
boolean isAnagram(String s, String t) { if (s.length() != t.length()) return false; int[] freq = new int[26]; for (char c : s.toCharArray()) freq[c - 'a']++; for (char c : t.toCharArray()) freq[c - 'a']--; for (int f : freq) if (f != 0) return false; return true; }
Check if a string is a palindrome (two pointers):
boolean isPalindrome(String s) { int left = 0, right = s.length() - 1; while (left < right) { if (s.charAt(left) != s.charAt(right)) return false; left++; right--; } return true; }
Reverse a string:
String reversed = new StringBuilder(s).reverse().toString();
Build a result from characters:
StringBuilder sb = new StringBuilder(); for (int i = 0; i < n; i++) { sb.append(someChar); } String result = sb.toString();
What Actually Separates Hires on String Problems
String problems in Java look easy until the interviewer asks about complexity. Knowing that substring() is O(k), that split(".") breaks silently, and that + in a loop is O(n²) separates candidates who understand the language from candidates who just learned the syntax. The difference between a hire and a no-hire on string problems is usually one of these five pitfalls, not the algorithm.
SpaceComplexity runs voice-based mock interviews where you explain your approach out loud, including the complexity reasoning that actually gets you the offer. If you want to stress-test your Java string knowledge before the real thing, that's the place to do it.
See also:
- Java Built-In Time Complexity: The Interview Cheat Sheet
- Java for Coding Interviews: The Standard Library That Bites Back
- What Is String Interning? The O(1) Comparison Behind the
isTrap - What Is a StringBuilder? The Fix for O(n²) String Concatenation