Java
String Manipulation
Algorithms
Programming
Code Efficiency

What is the most efficient algorithm for reversing a String in Java?

Master System Design with Codemia

Enhance your system design skills with over 120 practice problems, detailed solutions, and hands-on exercises.

Introduction

The most efficient way to reverse a string in Java is new StringBuilder(str).reverse().toString(). It runs in O(n) time and O(n) space, correctly handles Unicode surrogate pairs, and is optimized at the JVM level. For character arrays, an in-place two-pointer swap is the fastest approach with O(1) extra space. Avoid string concatenation in a loop (result += str.charAt(i)) — it creates a new String object on every iteration, making it O(n^2).

java
String original = "Hello, World!";
String reversed = new StringBuilder(original).reverse().toString();
System.out.println(reversed);  // !dlroW ,olleH

StringBuilder.reverse() is implemented in native code, handles surrogate pairs correctly, and operates in a single pass over the internal character array.

Method 2: char Array with Two-Pointer Swap

The most memory-efficient approach — O(1) extra space beyond the array:

java
1public static String reverse(String str) {
2    char[] chars = str.toCharArray();
3    int left = 0;
4    int right = chars.length - 1;
5
6    while (left < right) {
7        char temp = chars[left];
8        chars[left] = chars[right];
9        chars[right] = temp;
10        left++;
11        right--;
12    }
13
14    return new String(chars);
15}
16
17System.out.println(reverse("Hello"));  // olleH

This is O(n) time and swaps characters in place without any extra data structures.

Method 3: StringBuilder Manual Append

java
1public static String reverse(String str) {
2    StringBuilder sb = new StringBuilder(str.length());
3    for (int i = str.length() - 1; i >= 0; i--) {
4        sb.append(str.charAt(i));
5    }
6    return sb.toString();
7}

Setting the initial capacity with new StringBuilder(str.length()) avoids internal array resizing.

Method 4: Recursion

java
1public static String reverse(String str) {
2    if (str.length() <= 1) {
3        return str;
4    }
5    return reverse(str.substring(1)) + str.charAt(0);
6}

This is O(n^2) due to string concatenation and substring creating new strings at each level. It also risks StackOverflowError for long strings. Use only for educational purposes.

Method 5: Java 8+ Streams

java
1String reversed = new StringBuilder("Hello")
2    .reverse()
3    .chars()
4    .mapToObj(c -> String.valueOf((char) c))
5    .collect(Collectors.joining());
6// Already reversed by StringBuilder — stream is unnecessary here
7
8// Pure stream approach (slower)
9String reversed2 = "Hello".chars()
10    .mapToObj(c -> String.valueOf((char) c))
11    .reduce("", (a, b) -> b + a);

Streams add significant overhead for this operation. StringBuilder.reverse() is simpler and faster.

Handling Unicode Surrogate Pairs

Characters outside the Basic Multilingual Plane (like emojis) use two char values (a surrogate pair). The two-pointer swap can corrupt them:

java
1String emoji = "Hello 🌍!";
2
3// CORRECT: StringBuilder handles surrogates
4String correct = new StringBuilder(emoji).reverse().toString();
5// !🌍 olleH
6
7// WRONG: naive char swap breaks the surrogate pair
8char[] chars = emoji.toCharArray();
9// chars[6] and chars[7] are the surrogate pair for 🌍
10// Swapping them individually corrupts the emoji

If you use the two-pointer approach, check for surrogates:

java
1public static String reverseSafe(String str) {
2    int[] codePoints = str.codePoints().toArray();
3    int left = 0, right = codePoints.length - 1;
4    while (left < right) {
5        int temp = codePoints[left];
6        codePoints[left] = codePoints[right];
7        codePoints[right] = temp;
8        left++;
9        right--;
10    }
11    return new String(codePoints, 0, codePoints.length);
12}

Performance Comparison

MethodTimeSpaceSurrogate-Safe
StringBuilder.reverse()O(n)O(n)Yes
char[] two-pointerO(n)O(n) for resultNo
codePoints two-pointerO(n)O(n)Yes
StringBuilder manual appendO(n)O(n)No
String concatenation loopO(n^2)O(n^2)No
RecursionO(n^2)O(n) stackNo

Common Pitfalls

  • Using string concatenation in a loop: result = str.charAt(i) + result or result += str.charAt(i) creates a new String object on every iteration because strings are immutable in Java. For a 10,000-character string, this creates 10,000 intermediate string objects and is O(n^2).
  • Not handling surrogate pairs: Characters like emojis (🌍, 😀) are represented as two char values in Java. A naive character-by-character reverse corrupts these pairs. Use StringBuilder.reverse() or codePoints() to handle them correctly.
  • Using recursion for long strings: Each recursive call adds a stack frame. For strings longer than a few thousand characters, the recursive approach throws StackOverflowError. The default stack depth in Java is roughly 5,000-10,000 frames.
  • Not preallocating StringBuilder capacity: new StringBuilder() starts with capacity 16. For a long string, the internal array resizes multiple times (doubling each time). Pass the expected length: new StringBuilder(str.length()).
  • Using StringBuffer instead of StringBuilder: StringBuffer is thread-safe (synchronized), which adds overhead. In single-threaded contexts (most string reversals), StringBuilder is faster. Use StringBuffer only when multiple threads modify the same buffer.

Summary

  • Use new StringBuilder(str).reverse().toString() for the fastest, simplest, and surrogate-safe reversal
  • Use the char array two-pointer swap for minimal memory overhead (but handle surrogates separately)
  • Never use string concatenation in a loop — it is O(n^2)
  • Preallocate StringBuilder capacity when building strings manually
  • Prefer StringBuilder over StringBuffer unless thread safety is required

Course illustration
Course illustration

All Rights Reserved.