11
\$\begingroup\$

I have to reverse the string "He is the one" to "one the is He". I have written some programs in Java but am looking for other best solutions. Suggest any possible ways to minimize the current program.

First approach:

class StringRev{
    public static void main(String args[]){
    String str = "He is the one";
    String temp = "";
    String finalString = "";
        for(int i =str.length()-1;i>=0;i--){
            temp +=i!=0?str.charAt(i):str.charAt(i)+" ";
            if(str.charAt(i) == ' '||i==0){
                for(int j=temp.length()-1;j>=0;j--){
                    finalString += temp.charAt(j);
                }
                temp = "";
            }
        }
            System.out.println(finalString);
    }
}

Second approach:

class StringRev2{
    public static void main(String args[]){
    String str[] = "He is the one".split(" ");
    String finalStr="";
        for(int i = str.length-1; i>= 0 ;i--){
            finalStr += str[i]+" ";
        }
        System.out.println(finalStr);
    }
}
\$\endgroup\$
4
  • \$\begingroup\$ If you are attempting to write as few characters as possible, you should instead go to Code Golf \$\endgroup\$ Dec 14, 2013 at 17:30
  • \$\begingroup\$ related: Efficiently reverse the order of the words (not characters) in an array of characters \$\endgroup\$
    – jfs
    Dec 26, 2013 at 17:11
  • \$\begingroup\$ Could anyone explain why the second approach with double spaces in the split function gives us the desired result and using single space doesn't ? \$\endgroup\$
    – John
    Oct 30, 2015 at 6:37
  • \$\begingroup\$ basically with split function needs parameter how to break a string . Each word in string is separated by a single space , so I have used single space within double quotes . \$\endgroup\$ Oct 31, 2015 at 17:34

7 Answers 7

10
\$\begingroup\$

Stuffing all your code into main() is bad practice. This functionality belongs in a function of its own.

When doing multiple string concatenations, you really want to use a StringBuilder. If you don't, the compiler will make one for you anyway, every time you concatenate two strings using +. It's better to do it explicitly, then, and control how many of those temporary objects are created.


Your first approach works a character at a time. I suggest the following character-at-a-time approach, which allocates just one large temporary buffer.

private static void reverse(char[] buf, int start, int end) {
    for (int i = start, j = end - 1; i < j; i++, j--) {
        char swap = buf[i];
        buf[i] = buf[j];
        buf[j] = swap;
    }
}

public static String reverseWords1(String sentence) {
    char[] buf = sentence.toCharArray();

    // Reverse the string, character-wise
    reverse(buf, 0, buf.length);

    // Within each word, reverse the characters again
    int wordEnd = 0;
    for (int wordStart = 0; wordStart < buf.length; wordStart = wordEnd + 1) {
        for (wordEnd = wordStart; wordEnd < buf.length && buf[wordEnd] != ' '; wordEnd++) {}

        // wordStart is at the start of a word.
        // wordEnd is just past the end of the word.
        reverse(buf, wordStart, wordEnd);
    }
    return new String(buf);
}

I prefer your second approach, which is more succinct. Here it is, cleaned up by using a StringBuilder.

public static String reverseWords2(String sentence) {
    StringBuilder sb = new StringBuilder(sentence.length() + 1);
    String[] words = sentence.split(" ");
    for (int i = words.length - 1; i >= 0; i--) {
        sb.append(words[i]).append(' ');
    }
    sb.setLength(sb.length() - 1);  // Strip trailing space
    return sb.toString();
}
\$\endgroup\$
11
\$\begingroup\$

"Minimize" is a really bad word to use when trying to consider the quality of your code. It's vague, and it can lead to undesirable outcomes, such as broken or unusable code.

For example, is your goal to minimize the number of bytes used storing the source code on a hard drive? That might have been important when paper tape was a popular storage medium, but it's irrelevant today. It's so irrelevant that the difference between these two files is less than the size of a sector, so both actually occupy the same amount of storage space.

Is your goal to reduce the number of CPU cycles spent? Given that cell phones have 100MHz processors these days, you need to do more work to figure out if the effort you expend thinking about the problem will ever be made up for in efficiency experienced by your users. If this is run once or twice, efficiency simply is not important. On the other hand, if it could ever become part of a networking protocol, efficiency is extremely important. Quite honestly, English word reversal seems to be too specialized to fit the test of practicality for efficiency.

In general, what most people should be striving for in their code is "correctness" and "clarity". You want to know that it works correctly in all situations. The answer to that is to write unit tests. For clarity, you want the code to be readable, understandable, and usable. Make sure you have chosen good names. Modularize the functions. For example, you should consider extracting the dependency on System.out.println, as outputting the string has nothing to do with reversing the string.

\$\endgroup\$
6
\$\begingroup\$

In your first approach you have the right idea of starting at the end of the String and working backwards. The issues you have are:

  • you should use StringBuilder instead of String concatenation....
  • using a temp string which you add-to in reverse order, then reverse again in to the result is a slow approach .... but effective.
  • you should be doing it more than 1 character at a time.

In your second approach, I don't like:

  • you have string concatenation again use a StringBuilder()
  • you assume all spaces are 'equal'... what about multi-space breaks "Hello There" with this code will become "There Hello"

So, with a small adjustment to your regular expression (perhaps "\\b" ...) and conversion to StringBuilder, I think the second option is good.

The first option, if written right, will be faster than the split one (although the code is longer)....

Here's an attempt at doing it by first principles:

private static String reverse(String string) {
    if (string.isEmpty()) {
        return string;
    }
    int last = string.length();
    StringBuilder sb = new StringBuilder(string.length());
    boolean contextspace = ' ' == string.charAt(string.length() - 1);
    for (int i = string.length() - 1; i >= 0; i--) {
        if (contextspace != (string.charAt(i) == ' ')) {
            sb.append(string.substring(i + 1, last));
            last = i + 1;
            contextspace = !contextspace;
        }
    }
    sb.append(string.substring(0, last));
    return sb.toString();
}
\$\endgroup\$
1
  • \$\begingroup\$ this is a brilliant solution. \$\endgroup\$ Aug 13, 2017 at 18:33
2
\$\begingroup\$

There are many ways to do this, depending on what you're going for.

If you're stuck with Java, then probably look to Apache commons:

StringUtils.reverseDelimited(st, ' ');

Try using Scala if you can, which is nicer in so many ways:

st.split(" ").reverse.mkString(" ")
\$\endgroup\$
1
2
\$\begingroup\$

Re-use existing code.

Libraries allow for maximum re-use. Use them!

If you don't like libraries, try to re-use as much as Java offers. Look up documentation, examples, etc.

  • Splitting strings is java default split()
  • Reversing can be done with the excellent Collections class
  • Java8 has some nice String join stuff.

I also try to make the code as readable as possible by having short statements and avoiding loops (for, while, etc) when you don't need them.

My approach:

public static String reverseWords (String s) 
{
    String delimiter = " ";
    List<String> words = Arrays.asList(s.split(delimiter));
    Collections.reverse(words);
    return String.join(delimiter, words);
}

public static void main(String[] args) 
{
    System.out.println(reverseWords("He is the one"));
} 
\$\endgroup\$
1
\$\begingroup\$

Here is a clean approach to reverse a string by its parts:

public class Main {

    // ReverseParts: reverses a string by words as opposed to characters.
    public static String ReverseParts(String input, String splitBy, String joinBy) {
        StringBuilder built = new StringBuilder();

        // Note: String.split uses regex. See its definition
        String[] list = input.split(splitBy);

        // Rejoin all the characters into a StringBuilder.
        for ( String part: list ) {
            built.insert(0, part);
            built.insert(0, joinBy);
        }

        // Remove the unnecessary 'joinBy bit'.
        built.delete(0, 1);

        // Get back the final string.
        return built.toString();
    }
    public static void main(String[] args) {
        final String input = "Hello  Johnny's World!";

        System.out.println("Result: '" + ReverseParts(input, " +", " ")+"'");
        // Output:
        // Result: 'World! Johnny's Hello'
    }
}
\$\endgroup\$
2
  • \$\begingroup\$ Dino's version is much better. Don't reinvent the wheel. \$\endgroup\$ Dec 15, 2013 at 12:15
  • \$\begingroup\$ Also don't capitalize the first character in method names. Java is not C# :) \$\endgroup\$ Jan 14, 2016 at 11:04
-3
\$\begingroup\$

Approach 1:

JDK provides java.util.StringTokenizer to do the split or tokenizing a given text. This API helps avoid going character by character. No external jar/libraries required. Tokenizing gives you each separated word as an array element that you could print in reverse.

import java.util.ArrayList;
import java.util.StringTokenizer;


public class reverseString {
    public static void main(String args[])
    {
            StringTokenizer st = new StringTokenizer("this is a string");
            ArrayList<String> arrLstStrings = new ArrayList<>();
            while (st.hasMoreTokens()) {
                arrLstStrings.add(st.nextToken());
            }

            for(int loop=arrLstStrings.size()-1;loop>=0;loop--)
                System.out.println(arrLstStrings.get(loop));
    }
}

Approach 2:

import java.util.regex.*;

public class reverseString {
    public static void main(String args[])
{
        String strSource = "This is a string";
        // give a max int as limit in next stmt.
        String[] tokens = Pattern.compile(" ").split(strSource,15) ;
        for (int loop=tokens.length-1;loop>=0;loop--)
            System.out.println(tokens[loop]);
    }
}
\$\endgroup\$
2
  • \$\begingroup\$ Are you sure what are you doing? This will not reverse the string. \$\endgroup\$ Dec 15, 2013 at 10:29
  • \$\begingroup\$ additionally, you suggest StringTokenizer, but use Regex/split \$\endgroup\$
    – rolfl
    Dec 15, 2013 at 13:15

Not the answer you're looking for? Browse other questions tagged or ask your own question.