41

What is the most efficient way to reverse a string in Java? Should I use some sort of xor operator? The easy way would be to put all the chars in a stack and put them back into a string again but I doubt that's a very efficient way to do it.

And please do not tell me to use some built in function in Java. I am interested in learning how to do it not to use an efficient function but not knowing why it's efficient or how it's built up.

Eric Leschinski
  • 146,994
  • 96
  • 417
  • 335
Hultner
  • 3,710
  • 5
  • 33
  • 43
  • 8
    These kind of questions have been stoned to death for C/C++. Especially The Art of Computer Programming by D. Knuth goes in to a lot of detail. – NomeN Mar 13 '10 at 17:25
  • "stoned to death", hahaha, i like that. – Zaki Mar 14 '10 at 09:25
  • 2
    Out of curiosity: does anyone know of a real use for this? I mean a place where there's a need for an *efficient* string reversing algorithm? – Joachim Sauer Mar 14 '10 at 09:56
  • 23
    @joachim sauer, interviews maybe. – Zaki Mar 14 '10 at 11:13
  • 3
    As an aside: note that reversing the sequence of code-points is not the same as reversing "the string". For example, combining characters: if you just reverse the code-points then they end up combining against what was originally the *previous* character - so "aĉe" (if written with a combining character) could become "ecâ". – Marc Gravell Mar 14 '10 at 22:05

22 Answers22

55

You say you want to know the most efficient way and you don't want to know some standard built-in way of doing this. Then I say to you: RTSL (read the source, luke):

Check out the source code for AbstractStringBuilder#reverse, which gets called by StringBuilder#reverse. I bet it does some stuff that you would not have considered for a robust reverse operation.

JimmyM
  • 360
  • 3
  • 20
Tom
  • 21,468
  • 6
  • 39
  • 44
  • By the way... when I say "bet it does some stuff that you would have considered"... I was talking about surrogate pairs :-). You can see it in the code. – Tom Mar 13 '10 at 17:13
  • 2
    +1 for link to source. One of the best ways to learn how to implement something is to see how it is done in the real world. – Mark Byers Mar 13 '10 at 17:17
  • +1 for directly answering the question, being one of the few that's not on a tangent – John K Mar 13 '10 at 17:23
  • 1
    Here's a link that works, but no easy way to link directly to that method: http://kickjava.com/src/java/lang/AbstractStringBuilder.java.htm – Tom Jan 15 '14 at 20:51
  • A "ctrl-f" find on the above link for 'reverse' takes me right to it. Thanks! – modulitos Dec 23 '14 at 10:18
  • can some one explain me about surrogate pairs? @Tom – Muaaz salagar Mar 04 '16 at 02:24
41

The following does not deal with UTF-16 surrogate pairs.

public static String reverse(String orig)
{
    char[] s = orig.toCharArray();
    int n = s.length;
    int halfLength = n / 2;
    for (int i=0; i<halfLength; i++)
    {
        char temp = s[i];
        s[i] = s[n-1-i];
        s[n-1-i] = temp;
    }
    return new String(s);
}
Simon Nickerson
  • 42,159
  • 20
  • 102
  • 127
24

You said you don't want to do it the easy way, but for those Googling you should use StringBuilder.reverse:

String reversed = new StringBuilder(s).reverse().toString();

If you need to implement it yourself, then iterate over the characters in reverse order and append them to a StringBuilder. You have to be careful if there are (or can be) surrogate pairs, as these should not be reversed. The method shown above does this for you automatically, which is why you should use it if possible.

Mark Byers
  • 811,555
  • 193
  • 1,581
  • 1,452
  • 6
    Prefer `StringBuilder` to `StringBuffer` if you do not need thread safety. – Tom Mar 13 '10 at 17:05
  • -1 for not respecting "And please do not tell me to use some built in function in Java.". Sorry for that ;) I think Hultner would like to know the backgrounds / mechanics. However, the question should not be tagged java, I guess. – Tedil Mar 13 '10 at 17:06
  • @Mark Byers I think Hultner should decide that. I think he really is interested, no matter if it's homework or not, how to do it. If it was homework, atleast he is interested, so does it matter to tag it or not? – Pindatjuh Mar 13 '10 at 17:09
  • For those Googling they should consider answers here in context of the question. – John K Mar 13 '10 at 17:19
  • 1
    In a way homework, our homework is to reverse a string using a stack however I feel like that is not the right or best way to do it therefore I want to learn the best way to actually do it. And that's the reason I'm posting here, I want to learn. The Java assignments we do in school sometimes use extremely stupid ways to do stuff because everyone should understand it. – Hultner Mar 13 '10 at 17:24
  • @Hultner: You're right, that would be a poor way to do it. Using a stack would require boxing and unboxing of the characters. – Mark Byers Mar 13 '10 at 17:48
  • 1
    Wow, I'd have never thought of the surrogates! However, I've also never needed to reverse a string in my professional life. – jkff Mar 13 '10 at 19:56
8

An old post & question, however still did not see answers pertaining to recursion. Recursive method reverse the given string s, without relaying on inbuilt jdk functions

    public static String reverse(String s) {
    if (s.length() <= 1) {
        return s;
    }
    return reverse(s.substring(1)) + s.charAt(0);
}

`

cocoper
  • 99
  • 1
  • 1
  • 1
    although this is very old, I'd like to share one point i.e. recursion would be considered a heavy operation in terms of storage, the bigger the string, the more memory consumption (I believe that would be stack memory) :) – Lokeshwar Tailor Jan 18 '21 at 17:16
6

The fastest way would be to use the reverse() method on the StringBuilder or StringBuffer classes :)

If you want to implement it yourself, you can get the character array, allocate a second character array and move the chars, in pseudo code this would be like:

String reverse(String str) {
    char[] c = str.getCharArray
    char[] r = new char[c.length];
    int    end = c.length - 1

    for (int n = 0; n <= end; n++) {
        r[n] = c[end - n];
    }

    return new String(r);
}

You could also run half the array length and swap the chars, the checks involved slow things down probably.

rsp
  • 23,135
  • 6
  • 55
  • 69
  • The `String` class does not have a `reverse()` method. Also, your pseudo-code is wrong, as your arrays are sometimes 0-based and sometimes 1-based. – Simon Nickerson Mar 13 '10 at 17:16
  • My bad, StringBUilder & StringBuffer have a reverse, I'll change that. Java arrays are 0 based and the question is tagged Java. – rsp Mar 13 '10 at 17:20
  • No, still wrong. Now it will produce a string of length len-1. You want: `for (int n=0; n – Simon Nickerson Mar 13 '10 at 17:20
  • This is correct imho, example: for a string of length 3 n=0,1,2 and copies chars at 3-0-1,3-1-1,3-2-1 which are 2,1,0 – rsp Mar 13 '10 at 17:24
3

I'm not really sure by what you mean when you say you need an efficient algorithm.

The ways of reversing a string that I can think of are (they are all already mentioned in other answers):

  1. Use a stack (your idea).

  2. Create a new reversed String by adding characters one by one in reverse order from the original String to a blank String/StringBuilder/char[].

  3. Exchange all characters in the first half of the String with its corresponding position in the last half (i.e. the ith character gets swapped with the (length-i-1)th character).

The thing is that all of them have the same runtime complexity: O(N). Thus it cannot really be argued that any one is any significantly better than the others for very large values of N (i.e. very large strings).

The third method does have one thing going for it, the other two require O(N) extra space (for the stack or the new String), while it can perform swaps in place. But Strings are immutable in Java so you need to perform swaps on a newly created StringBuilder/char[] anyway and thus end up needing O(N) extra space.

MAK
  • 26,140
  • 11
  • 55
  • 86
3
public class ReverseInPlace {

  static char[]  str=null;

    public static void main(String s[]) {
      if(s.length==0)
        System.exit(-1);

       str=s[0].toCharArray();

       int begin=0;
       int end=str.length-1;

       System.out.print("Original string=");
       for(int i=0; i<str.length; i++){
         System.out.print(str[i]);
       }

       while(begin<end){
          str[begin]= (char) (str[begin]^str[end]);
          str[end]= (char)   (str[begin]^str[end]);
          str[begin]= (char) (str[end]^str[begin]);

          begin++;
          end--;       
       }

       System.out.print("\n" + "Reversed string=");
       for(int i=0; i<str.length; i++){
         System.out.print(str[i]);
       }

    }
}
arsenal
  • 23,366
  • 85
  • 225
  • 331
abiolaaye
  • 31
  • 1
  • 1
    While I thank you for your answer it's been half a year since I wrote this questiont and the problem have since long been resolved but thanks for your answer. – Hultner Sep 16 '10 at 16:02
  • 1
    what about using the check to mark answers as the correct one to your other questions! – radu florescu Dec 12 '12 at 07:53
2

I think that if you REALLY don't have performance problem you should just go with the most readable solution which is:

StringUtils.reverse("Hello World");
Marcin Szymczak
  • 11,199
  • 5
  • 55
  • 63
2
private static String reverse(String str) {
    int i = 0;
    int j = str.length()-1;
    char []c = str.toCharArray();
    while(i <= j){
        char t = str.charAt(i);
        c[i] = str.charAt(j);
        c[j]=t;
        i++;
        j--;
    }
    return new String(c);
}
1

If you do not want to use any built in function, you need to go back with the string to its component parts: an array of chars.

Now the question becomes what is the most efficient way to reverse an array? The answer to this question in practice also depends upon memory usage (for very large strings), but in theory efficiency in these cases is measured in array accesses.

The easiest way is to create a new array and fill it with the values you encounter while reverse iterating over the original array, and returning the new array. (Although with a temporary variable you could also do this without an additional array, as in Simon Nickersons answer).

In this way you access each element exactly once for an array with n elements. Thus giving an efficiency of O(n).

NomeN
  • 17,140
  • 7
  • 32
  • 33
1

I would simply do it this way without a use of any single util function. Just the String class is sufficient.

public class MyStringUtil {

    public static void main(String[] args) {
        String reversedString = reverse("StringToReverse");
        System.out.println("Reversed String : " + reversedString);
    }

    /**
     * Reverses the given string and returns reversed string
     * 
     * @param s Input String
     * @returns reversed string
     */
    private static String reverse(String s) {
        char[] charArray = s.toCharArray(); // Returns the String's internal character array copy
        int j = charArray.length - 1;
        for (int i = 0; charArray.length > 0 && i < j; i++, j--) {
            char ch = charArray[i];
            charArray[i] = charArray[j];
            charArray[j] = ch;
        }
        return charArray.toString();
    }
}

Check it. Cheers!!

Vivek
  • 3,523
  • 2
  • 25
  • 41
0

Using String:

String abc = "abcd";
int a= abc.length();

String reverse="";

for (int i=a-1;i>=0 ;i--)
{
    reverse= reverse + abc.charAt(i);
}
System.out.println("Reverse of String abcd using invert array is :"+reverse);

Using StringBuilder:

    String abc = "abcd";
    int a= abc.length();
    StringBuilder sb1 = new StringBuilder();

    for (int i=a-1;i>=0 ;i--)
    {
        sb1= sb1.append(abc.charAt(i));
    }
    System.out.println("Reverse of String abcd using StringBuilder is :"+sb1);
jags
  • 527
  • 3
  • 6
  • 15
  • 2
    This question is over 3 years old and so is the accepted answer. The question was how to reverse a string *efficiently*. You code is effective, but *far from* efficient. – Vincent van der Weele Jul 25 '13 at 20:48
  • @heuster: Though this actually means, that it is the perfect source of insight, at least if you explained, why it is not efficient. – omilke Jul 25 '13 at 21:14
  • 2
    @Olli fair enough. Strings in Java are *immutable*, so the line `reverse + abc.charAt(i)` creates a new string of length `i+1` (rather than appending the char to the existing string). Therefore, the loop creates `a` strings in total, of length `0, 1, ..., a-1`. In total, this approach hence needs `O(n^2)` additional memory. Time complexity can never be better than space complexity, so also the running time is `O(n^2)`. As seen in the answer of MAK, an 'efficient' algorithm should have `O(n)` time complexity. – Vincent van der Weele Jul 26 '13 at 07:15
  • I think this now provides an idea to start with why it is inefficient that way, so everyone reviewing this will get chance have the insight. By the way, you are stating that time complexity cannot be lower than space complexity, which somehow feels intuitive. However, could you provide a link with more explanation or at least name that theorem for further research? This is quite interesting. – omilke Jul 26 '13 at 13:34
  • @Olli to be honest: I don't know. But the logic is quite simple: a computer can write to at most a constant number of memory units in a time unit. The size of that constant will depend on many parameters (the length of the time unit, clock frequency, bus width, memory latency, etc), but it has to be a constant. That implies that every memory operation takes *at least* a constant amount of time and hence the space complexity is at most the time complexity. I guess it not that easy to formally prove, though. – Vincent van der Weele Jul 27 '13 at 13:34
  • 1
    @Heuster Thanks, this is what I also considered as the intuitive way of thinking. In fact, I did a little research and found this topic is quite obvious when reducing it to a Turing Machine. It is explained here http://stackoverflow.com/a/7137318/2108919. – omilke Jul 30 '13 at 10:49
0

One variant can be, swapping the elements.

int n = length - 1;
char []strArray = str.toCharArray();
for (int j = 0; j < n; j++) {
   char temp = strArray[j];
   char temp2 = strArray[n];
   strArray[j] = temp2;
   strArray[n] = temp;
   n--;
  }
Naren
  • 2,706
  • 1
  • 21
  • 15
0
public static void main(String[] args){
    String string ="abcdefghijklmnopqrstuvwxyz";
    StringBuilder sb = new StringBuilder(string);
    sb.reverse();

    System.out.println(sb);
}
Dan
  • 979
  • 1
  • 8
  • 29
0
public static String Reverse(String word){
        String temp = "";
        char[] arr = word.toCharArray();
        for(int i = arr.length-1;i>=0;i--){
            temp = temp+arr[i];
        }
        return temp;
    }
saad
  • 11
0
char* rev(char* str)
    {
    int end= strlen(str)-1;
    int start = 0;

    while( start<end )
    {
    str[start] ^= str[end];
    str[end] ^= str[start];
    str[start]^= str[end];

    ++start;
    --end;
    }

    return str;
}

=========================

Wondering how it works?

First operation:

x1 = x1 XOR x2

x1: 1   0   0
x2: 1   1   1
New x1: 0   1   1

Second operation

x2 = x2 XOR x1

x1: 0   1   1
x2: 1   1   1
New x2: 1   0   0
//Notice that X2 has become X1 now

Third operation:

x1 = x1 XOR x2
x1: 0   1   1
x2: 1   0   0
New x1: 1   1   1
//Notice that X1 became X2
  • This question specifically is about Java, not C++, so `char*` should be `char[]` or `String`. But more fundamentally, is using the XOR trick actually faster than just using a `temp` variable to switch two variables? – Teepeemm Mar 16 '15 at 21:18
  • Sorry for it to be posted in C++. XOR is practically speaking the fastest operation a CPU can possibly do, and the good part is that all CPU's support it. The reason for this is quite easy: a XOR gate can be created with only 4 NAND gates or 5 NOR gates -- which means it's easy to create using the fabric of your silicon. Unsurprising, all CPU's that I know of can execute your XOR operation in 1 clock tick (or even less). (Please note that this is something I read few days back on one of the posts and noted down for my reference. I do not have that link anymore though.) – Bhojal Gelda Mar 16 '15 at 21:39
  • What model of efficiency do you suggest, and how do the test results for this approach compare to previous ones, especially that from abiolaaye? – greybeard Mar 17 '15 at 03:39
  • 2
    @BhojalGelda: This might have been useful in the past but not with modern pipelined CPUs. [See this reference.](http://en.wikipedia.org/wiki/XOR_swap_algorithm#Reasons_for_avoidance_in_practice) – Blastfurnace Mar 17 '15 at 04:21
0
public static string getReverse(string str)
{
    char[] ch = str.ToCharArray();
    string reverse = "";
    for (int i = str.Length - 1; i > -1; i--)
    {
        reverse += ch[i];
    }
    return reverse;
}

//using in-built method reverse of Array
public static string getReverseUsingBulidingFunction(string str)
{
    char[] s = str.ToCharArray();
    Array.Reverse(s);
    return new string(s);
}


public static void Main(string[] args)
{
    string str = "123";
    Console.WriteLine("The reverse string of '{0}' is: {1}",str,getReverse(str));
    Console.WriteLine("The reverse string of '{0}' is: {1}", str, getReverseUsingBulidingFunction(str));
    Console.ReadLine();
}
Martin
  • 2,411
  • 11
  • 28
  • 30
Dipitak
  • 97
  • 1
  • 1
  • 3
0

Using multiple threads to swap the elements:

    final char[] strArray = str.toCharArray();

    IntStream.range(0, str.length() / 2).parallel().forEach(e -> {
        final char tmp = strArray[e];
        strArray[e] = strArray[str.length() - e - 1];
        strArray[str.length() - e - 1] = tmp;
    });
    return new String(strArray);
0

Of course this is the most efficient way:

String reversed = new StringBuilder(str).reverse().toString();

But if you don't like using that then I recommend this instead:

public String reverseString(String str)
{
    String output = "";
    int len = str.length();
    for(int k = 1; k <= str.length(); k++, len--)
    {
        output += str.substring(len-1,len);
    }
    return output;
}
0
static String ReverseString(String input) {
    var len = input.Length - 1;
    int i = 0;
    char[] revString = new char[len+1];
    while (len >= 0) {
        revString[i] = input[len];
        len--; 
        i++;
    }
    return new string(revString);   
}

why can't we stick with the simplest loop and revere with character read and keep adding to the char array, I have come across with a whiteboard interview, where interviewer set restrictions on not to use StringBuilder and inbuilt functions.

sh.seo
  • 1,482
  • 13
  • 20
jidh
  • 172
  • 1
  • 6
  • 22
  • What does your compiler have to say when fed your code? – greybeard Apr 18 '19 at 15:37
  • Thanks for your answer, I wrote this question 10 years ago when I still were studying and were just learning Java, at that point I had mainly coded C low level and Assembly before and Java were my first high-level language, being used with low level optimisations I wanted to know how to do similar things in a high-level language like Java. I appreciate the answers and as someone who regularly interviews and hires developers in my work I'd much more appreciate the simplest most readable solution that gets the job done today, I were a hardcore sucker for premature optimisation back then. – Hultner Apr 24 '19 at 11:47
0

This is the optimal way to reverse a string with O(log n) complexity.

public char[] optimisedArrayReverse(char[] chars) {
        char[] reversedChars = chars;
        int length = chars.length;
        int center = (length / 2) - 1;
        int reversedIndex = chars.length - 1;
        
        if (center < 0) {
            return chars;
        }

        for (int index = 0; index <= center; index++) {
            //Swap values
            char temp = reversedChars[index];
            reversedChars[index] = chars[reversedIndex];
            reversedChars[reversedIndex] = temp;
            
            reversedIndex --;
        }

        return reversedChars;
    }
-2
public static String reverseString(String str)
{
    StringBuilder sb = new StringBuilder();

    for (int i = str.length() - 1; i >= 0; i--)
    {
        sb.append(str[i]);
    }

    return sb.toString();
}
Michael Markidis
  • 4,163
  • 1
  • 14
  • 21
dmitry
  • 1
  • 1