Skip to content
Java strings 5 min read

Reverse a String

Reversing a string is one of the most classic exercises in Java — and it teaches you a lot about how strings actually work under the hood. Because Java strings are immutable, you can never reverse one in-place; you always build a new string. There are several clean ways to do this, and each reveals something useful about the language.

Why You Can’t Reverse In-Place

In languages like C, you can swap characters directly in memory. Java’s String class forbids this — once created, a String object’s contents never change. Every “reversal” must produce a fresh String (or use a mutable type like StringBuilder).

Tip: Because of immutability, the most efficient solutions use StringBuilder or a char[] array — both are mutable and avoid creating unnecessary intermediate String objects.


The simplest and most idiomatic approach. StringBuilder has a built-in reverse() method that does exactly what you need in one call.

public class ReverseString {
    public static void main(String[] args) {
        String original = "Hello, Java!";
        String reversed = new StringBuilder(original).reverse().toString();
        System.out.println(reversed);
    }
}

Output:

!avaJ ,olleH

This is the recommended approach for production code — it is concise, readable, and efficient.


Method 2: Using a char Array

Converting a String to a char[] lets you swap characters in-place inside the array, then reassemble:

public class ReverseWithCharArray {
    public static String reverse(String str) {
        char[] chars = str.toCharArray();
        int left = 0, right = chars.length - 1;

        while (left < right) {
            char temp = chars[left];
            chars[left] = chars[right];
            chars[right] = temp;
            left++;
            right--;
        }
        return new String(chars);
    }

    public static void main(String[] args) {
        System.out.println(reverse("DevCraftly")); // ylftharCveD
    }
}

Output:

ylftharCveD

This is a classic two-pointer algorithm — O(n) time and O(n) space (for the char array).


Method 3: Iterating Backwards with a Loop

Loop from the last character to the first, appending each character to a StringBuilder:

public class ReverseWithLoop {
    public static String reverse(String str) {
        StringBuilder sb = new StringBuilder();
        for (int i = str.length() - 1; i >= 0; i--) {
            sb.append(str.charAt(i));
        }
        return sb.toString();
    }

    public static void main(String[] args) {
        System.out.println(reverse("abcdef")); // fedcba
    }
}

Output:

fedcba

Note: Do NOT use String += inside the loop. Each + on a String creates a new object, making this O(n²) due to how string concatenation works. Always use StringBuilder inside loops.


Method 4: Recursion

Reversing a string recursively is a great mental exercise. The idea: the reverse of "abc" is the reverse of "bc" + "a".

public class ReverseRecursive {
    public static String reverse(String str) {
        if (str.isEmpty()) {
            return str;
        }
        return reverse(str.substring(1)) + str.charAt(0);
    }

    public static void main(String[] args) {
        System.out.println(reverse("Java")); // avaJ
    }
}

Output:

avaJ

Warning: Recursive reversal creates O(n) stack frames and O(n) intermediate String objects. It is elegant but impractical for large strings — avoid it in production. See Recursion for a deeper dive.


Method 5: Using Java 8 Streams

If you are comfortable with the Stream API, you can reverse a string by streaming its characters:

import java.util.stream.IntStream;
import java.util.stream.Collectors;

public class ReverseWithStream {
    public static String reverse(String str) {
        return IntStream.rangeClosed(1, str.length())
                        .mapToObj(i -> String.valueOf(str.charAt(str.length() - i)))
                        .collect(Collectors.joining());
    }

    public static void main(String[] args) {
        System.out.println(reverse("streams")); // smaerts
    }
}

Output:

smaerts

This is functional and expressive, though slightly slower than the StringBuilder approach due to boxing and collector overhead.


Comparison Table

MethodMutable?ReadabilityPerformanceBest For
StringBuilder.reverse()YesExcellentBestGeneral use
char[] two-pointerYes (array)GoodExcellentInterviews, large strings
Loop + StringBuilderYesGoodExcellentExplicit control
RecursionNoEducationalPoor (large n)Learning recursion
StreamsNoFunctionalModerateStream-style code

Checking if a String is a Palindrome

A palindrome reads the same forwards and backwards. Reversing is the easiest way to check:

public class PalindromeCheck {
    public static boolean isPalindrome(String str) {
        String reversed = new StringBuilder(str).reverse().toString();
        return str.equalsIgnoreCase(reversed);
    }

    public static void main(String[] args) {
        System.out.println(isPalindrome("racecar")); // true
        System.out.println(isPalindrome("Java"));    // false
    }
}

Output:

true
false

Tip: Use equalsIgnoreCase for case-insensitive palindrome checks, or str.toLowerCase() before reversing.


Under the Hood

How StringBuilder.reverse() Works

StringBuilder.reverse() in the JDK iterates over the internal char[] with a two-pointer swap, similar to Method 2 above — but it also handles surrogate pairs correctly. Unicode characters outside the Basic Multilingual Plane (codepoints > U+FFFF) are represented as two char values (a surrogate pair). A naive character-by-character swap would break them apart; StringBuilder.reverse() keeps each pair together.

// Surrogate pair example: 𝄞 (musical symbol G-clef, U+1D11E)
String music = "AB𝄞CD";
System.out.println(new StringBuilder(music).reverse()); // DC𝄞BA

A manual char[] swap without surrogate handling would corrupt the 𝄞 character.

Memory Allocation

new StringBuilder(original) copies the string’s characters into its internal char[] (or byte[] in Java 9+ compact strings). The reverse() call mutates that array in-place. The final .toString() creates a new String that shares (or copies) those bytes. Overall: 2 allocations, O(n) time.

Compact Strings (Java 9+)

Since Java 9, String uses a byte[] internally with a coder flag — LATIN1 (1 byte/char) for ASCII-only strings, UTF16 (2 bytes/char) otherwise. StringBuilder mirrors this. For an ASCII-only string, the reverse() operates on a tightly packed byte array, which is very cache-friendly and fast.


  • Strings — the foundation you need before reversing anything
  • String Immutability — why strings cannot be changed and what that means for reversal
  • StringBuilder — the mutable workhorse behind the fastest reversal methods
  • String Methods — the full toolkit of built-in string operations
  • Recursion — deeper look at the recursive approach and its tradeoffs
  • Stream API — the functional style used in Method 5 above
Last updated June 13, 2026
Was this helpful?