Sitelet https://howtodoinjava.com/java/puzzles/how-to-reverse-string-in-java/

Reverse a String in Java: StringBuilder, Loops and Unicode

Learn to reverse a string in Java with StringBuilder, two pointers, recursion and streams, reverse word order, and keep emoji and accents intact.

Reversing a string with an emoji in Java, where the char array swap breaks the surrogate pair and StringBuilder.reverse keeps it intact

To reverse a string in Java, we wrap it in a StringBuilder, call reverse() and convert the result back with toString(). The String class has no reverse() method of its own, and because strings are immutable, every reversal creates a new string.

Interviewers like string reversal because it tests loops and immutability, and real code needs it too, for example when we check palindromes, reverse the labels of a domain name for sorting, or reverse the word order of a sentence.

The following example reverses the characters of a word and the words of a sentence, using the reverseWords() method from section 5.

String word = "Java";
String reversed = new StringBuilder(word).reverse().toString();   // "avaJ"
String sentence = "Java is fun";
String backwards = reverseWords(sentence);                        // "fun is Java"

Reversing the characters and reversing the words are two different tasks, and we cover both, plus versions without reverse() for interviews, the Unicode cases where a naive loop breaks the text, and a comparison of all the approaches.

1. Reversing Characters with StringBuilder.reverse()

The StringBuilder.reverse() method reverses the characters of the builder in place and returns the same builder, so the calls chain into one line. It handles surrogate pairs correctly, which matters for emoji and other characters outside the Basic Multilingual Plane, as section 4 shows.

static String reverse(String s) {
    if (s == null) {
        return null;
    }
    return new StringBuilder(s).reverse().toString();
}
String hello = reverse("hello");     // "olleh"
String empty = reverse("");           // ""
String none = reverse(null);          // null
String crash = new StringBuilder(null).reverse().toString();   // NullPointerException

The constructor new StringBuilder(null) throws a NullPointerException, so the helper method checks for null first. The older StringBuffer class has the same reverse() method with synchronized methods, which only costs time when a single thread uses the builder, as in this case.

2. Reversing a String Without reverse()

Interviewers often ask for a reversal without the built-in method. We show two loop versions and a recursive one, and all of them work on char values, which is fine for ordinary text in most languages.

2.1. Swapping Characters with Two Pointers

The two-pointer version copies the string into a char[], swaps the first and the last character, and moves both indexes toward the middle. It makes n / 2 swaps and allocates only the array and the result string.

static String reverseCharArray(String s) {
    char[] chars = s.toCharArray();
    for (int left = 0, right = chars.length - 1; left < right; left++, right--) {
        char tmp = chars[left];
        chars[left] = chars[right];
        chars[right] = tmp;
    }
    return new String(chars);
}
String swapped = reverseCharArray("stressed");   // "desserts"
String odd = reverseCharArray("abc");             // "cba"

2.2. Looping Backwards with charAt()

The second loop reads the characters from the last index to the first with charAt() and appends them to a StringBuilder. We append to a builder instead of writing result += c, because each += on a String copies the whole result so far, which makes the loop O(n2) for long strings.

static String reverseLoop(String s) {
    StringBuilder sb = new StringBuilder(s.length());
    for (int i = s.length() - 1; i >= 0; i--) {
        sb.append(s.charAt(i));
    }
    return sb.toString();
}
String looped = reverseLoop("drawer");   // "reward"

2.3. Reversing a String with Recursion

The recursive version reverses everything after the first character and appends the first character at the end. The base case is the empty string. It is a good exercise in recursion, but each call creates a new substring, so the method copies O(n2) characters in total and uses one stack frame per character.

static String reverseRecursive(String s) {
    if (s.isEmpty()) {
        return s;
    }
    return reverseRecursive(s.substring(1)) + s.charAt(0);
}
String rec = reverseRecursive("live");   // "evil"

On Java 25 with the default stack size, the recursive method throws a StackOverflowError for strings of 10,000 to 20,000 characters, whereas both loops handle strings of any length. We keep recursion for interviews and use a loop or StringBuilder in real code.

3. Reversing a String with Streams

Streams do not have a reverse operation, so stream solutions either build the result in reverse order or read the characters by a reversed index. The cleanest version works on code points, which keeps emoji and other supplementary characters intact.

static String reverseCodePoints(String s) {
    int[] cps = s.codePoints().toArray();
    int[] rev = IntStream.range(0, cps.length).map(i -> cps[cps.length - 1 - i]).toArray();
    return new String(rev, 0, rev.length);
}
String streamed = reverseCodePoints("stream");   // "maerts"

Some answers online use s.chars().mapToObj(…).reduce(“”, (a, b) -> b + a). That version is short, but it creates a new string for every character, so it has the same O(n2) cost as += in a loop.

4. Unicode Surrogate Pairs and Combining Marks

A Java char is a 16-bit UTF-16 code unit, and characters such as emoji need two of them, called a surrogate pair. If we swap the two halves, as the char[] and charAt() loops do, the result contains an invalid pair and shows up as two broken symbols.

Reversing a string with an emoji in Java, where the char array swap breaks the surrogate pair and StringBuilder.reverse keeps it intact
The char[] swap reverses the two halves of the emoji, whereas StringBuilder.reverse() keeps each surrogate pair together
String emoji = "a\uD83D\uDE00b";                                 // a, grinning face, b
String safe = new StringBuilder(emoji).reverse().toString();
boolean safeOk = safe.equals("b\uD83D\uDE00a");                   // true
String broken = reverseCharArray(emoji);
boolean brokenOk = broken.equals("b\uDE00\uD83Da");               // true, the pair is reversed
long emojiCount = broken.codePoints().filter(Character::isSupplementaryCodePoint).count();   // 0

For user-visible text, we reverse with StringBuilder.reverse() or by code points, never by swapping single char values. The two-pointer loop from section 2 is fine for ASCII data such as IDs and codes.

Code points are still not the whole story. A letter with an accent can be written as two code points, the letter e and the combining accent U+0301. Reversing by code points puts the accent before the letter, so it ends up on the wrong character. The regex \X matches one user-perceived character (a grapheme cluster), so we split by it and reverse the list.

static String reverseGraphemes(String s) {
    List<String> clusters = Pattern.compile("\\X").matcher(s).results().map(MatchResult::group).toList();
    return String.join("", clusters.reversed());
}
String cafe = "cafe\u0301";                                    // accent on the last e
boolean wrongAccent = reverse(cafe).equals("\u0301efac");          // true, the accent comes first
boolean rightAccent = reverseGraphemes(cafe).equals("e\u0301fac"); // true

5. Reversing the Order of Words

To reverse the words instead of the characters, we split the sentence on whitespace, reverse the list of words and join them with single spaces. Since Java 21, List.reversed() returns a reversed view of a list, so no copy or Collections.reverse() call is needed. For other ways to cut text into words, see splitting and tokenizing strings.

static String reverseWords(String sentence) {
    if (sentence == null || sentence.isBlank()) {
        return "";
    }
    List<String> words = Arrays.asList(sentence.strip().split("\\s+"));
    return String.join(" ", words.reversed());
}
String spaced = reverseWords("  learn   Java  today ");   // "today Java learn"
String blank = reverseWords("   ");                         // ""
String eachWord = Arrays.stream("Java is fun".split(" ")).map(w -> reverse(w)).collect(Collectors.joining(" "));   // "avaJ si nuf"

Older solutions use Stack and StringTokenizer. Both still work, but the StringTokenizer Javadoc calls it a legacy class and recommends split(), and the Stack Javadoc recommends ArrayDeque for stack operations. The split and join version also avoids the trailing space that a token-appending loop leaves at the end.

Reversing words is useful for host names. Say a log analysis tool must group requests by domain, so api.shop.example.com and www.shop.example.com sort next to each other. Reversing the labels gives com.example.shop.api, the same order that Java package names use.

String host = "api.shop.example.com";
List<String> labels = Arrays.asList(host.split("\\."));
String sortKey = String.join(".", labels.reversed());   // "com.example.shop.api"

6. Which Way to Reverse a String in Java

All the loop and builder versions run in O(n) time, so the choice comes down to Unicode safety and readability. The O(n2) versions, recursion and += concatenation, are only acceptable for short strings.

ApproachTimeSurrogate pairsCombining marksUse it for
StringBuilder.reverse()O(n)KeptSeparatedDefault choice
Two-pointer char[] swapO(n)BrokenSeparatedInterviews, ASCII data
Backward charAt() loopO(n)BrokenSeparatedInterviews
Recursion with substring()O(n2)BrokenSeparatedRecursion practice only
codePoints() streamO(n)KeptSeparatedStream-style code
\X grapheme splitO(n)KeptKeptUser-visible text with accents

The extra memory is O(n) in every version, because Java strings are immutable and the result is always a new string. Apache Commons Lang offers StringUtils.reverse(), which returns null for null input and otherwise calls StringBuilder.reverse(), so adding the library only for reversal brings nothing new.

7. Reverse String FAQs

Searches about string reversal in Java keep ending in these questions.

7.1. Why does the String class not have a reverse() method?

Because the String API covers the operations that are common in text processing, and reversal is rare outside exercises. The JDK puts reverse() in the mutable StringBuilder and StringBuffer classes, which reverse their content in place. Read why strings are immutable for the background.

7.2. How do we reverse a string without using any library method?

With the two-pointer swap from section 2. It uses only toCharArray(), a loop and new String(chars), and runs in O(n) time.

7.3. What is the difference between StringBuilder and StringBuffer reverse()?

The result is the same. StringBuffer synchronizes its methods for use by several threads, whereas StringBuilder does not, so StringBuilder is the right choice for a local variable.

7.4. Does StringBuilder.reverse() work with emoji?

Yes, for emoji made of one code point, because the Javadoc states that surrogate pairs are treated as single characters. Emoji built from several code points, such as flags or family emoji, need the \X grapheme version from section 4.

7.5. How do we check whether a string is a palindrome with reverse()?

We compare the string with its reversed copy, for example s.equals(new StringBuilder(s).reverse().toString()). A two-pointer check is faster because it needs no copy, as the palindrome program article shows.

8. Conclusion

To reverse a string in Java, new StringBuilder(s).reverse().toString() is the shortest and safest choice, because it keeps surrogate pairs together. For interviews, the two-pointer swap and the backward loop show that we understand the algorithm, whereas recursion and += concatenation cost O(n2).

Reversing words is a separate task that we solve with split(), List.reversed() and String.join(). For text with accents written as combining marks, the \X regex splits the string into user-visible characters before we reverse it.

9. References

Happy Learning !!

Source Code on Github

Leave a Comment

  1. String str = “Java technology blog for smart java concepts and coding practices”;

    List words = Arrays.asList(str.split(“\\s”));
    Collections.reverse(words);
    System.out.println(words.stream().reduce((s1, s2) -> s1 + ‘ ‘ + s2).get());

  2. In the same document of StringTokenizer it’s mentioned that,

    “StringTokenizer is a legacy class that is retained for compatibility reasons although its use is discouraged in new code. It is recommended that anyone seeking this functionality use the split method of String or the java.util.regex package instead”

    Nice initiative though!!! Thumbs up!!!

    Thanks,
    Dhiraj.

Comments are closed.

About Us

HowToDoInJava provides tutorials and how-to guides on Java and related technologies.

It also shares the best practices, algorithms & solutions and frequently asked interview questions.