3
\$\begingroup\$

I need to format a number with space as thousand separator in my Java code for Android.

  • How can these functions be improved?

    • What are the pros and cons to the first recursive method?
    • What are the pros and cons to the second substring method?
  • Is there maybe an even better and easier way to solve the problem?

Java code for the recursive variant:

/**
 * Format a (int) Number with Space as Thousand Separator
 * Recursive variant
 * @param number the Number to Format
 * @return String the Formatted Number
 * @since 1.0
 */
public static String intToThousandSpacedStringOS(int number) {
 boolean positive = true;
 String t = "";
 if (number < 0) {
 positive = false;
 number = -number;
 }
 if (number > 999) {
 t = intToThousandSpacedStringOS(number / 1000) + " ";
 do {
 number = number % 1000;
 } while (number > 999);
 t += String.format("%03d", number);
 } else {
 t = "" + number;
 }
 return positive ? t : "-" + t;
}

Java code for the substring variant:

/**
 * Format a (int) Number with Space as Thousand Separator
 * SubString variant
 * @param number the Number to Format
 * @return String the Formatted Number
 * @since 1.0
 */
public static String intToThousandSpacedStringOS(int number) {
 boolean positive = true;
 String t;
 String r = "";
 int i = 1;
 if (number < 0) {
 positive = false;
 number = -number;
 }
 t = "" + number;
 while (t.length() > 3 * i) {
 r = t.substring(t.length() - 3 * i > 3 ? t.length() - 3 * (i + 1) : 0, t.length() - 3 * i) + " " + r;
 i++;
 }
 r += t.substring(t.length() - 3 > 0 ? t.length() - 3 : 0, t.length());
 return positive ? r : "-" + r;
}
200_success
145k22 gold badges190 silver badges478 bronze badges
asked Feb 18, 2022 at 14:42
\$\endgroup\$
5
  • \$\begingroup\$ How is this different from System.out.printf( "%,d\n", number);? \$\endgroup\$ Commented Feb 18, 2022 at 15:14
  • \$\begingroup\$ @Mast: String.format("%,d\n", number).toString(); would return 123,456 or 123.456 or something language specific, not 123 456 as I want. \$\endgroup\$ Commented Feb 18, 2022 at 16:01
  • \$\begingroup\$ Is this a regional preference? If so, for what country and language? \$\endgroup\$ Commented Feb 18, 2022 at 17:29
  • \$\begingroup\$ No regional preferences, always space in between no matter what. \$\endgroup\$ Commented Feb 18, 2022 at 17:35
  • 5
    \$\begingroup\$ Use the existing libraries: DecimalFormatSymbols dfs = new DecimalFormatSymbols(); dfs.setGroupingSeparator(' '); DecimalFormat df = new DecimalFormat("#,###", dfs); System.out.println(df.format(-1000)); \$\endgroup\$ Commented Feb 18, 2022 at 19:08

1 Answer 1

6
\$\begingroup\$

I will not discuss if it's re-inventing some wheel, but comment on the two code versions given.

Recursive version

The loop

 do {
 number = number % 1000;
 } while (number > 999);

is nonsense. number % 1000 can never be > 999, so number = number % 1000; without the loop does exactly the same.

In the "loop", re-assigning a new value to the method call parameter number is considered bad style. You should introduce a new variable, e.g. int remainder = number % 1000;.

You can avoid the error-prone handling of the positive flag by utilising recursion as well:

if (number < 0) {
 return "-" + intToThousandSpacedStringOS(-number);
}

You declare your variables very early, and later change their values from an (often useless) initial value to something useful, e.g.

String t = "";

Better declare your variables in the exact same line where you know their value. Or at least, don't assign an initial value, then the compiler gives you an error message if in some execution path you forgot to assign a useful value.

To sum it up, my recursive version would read

public static String intToThousandSpacedStringOS(int number) {
 if (number < 0) {
 return "-" + intToThousandSpacedStringOS(-number);
 } else if (number > 999) {
 return intToThousandSpacedStringOS(number / 1000) + 
 String.format(" %03d", number % 1000); // note the space in the format string.
 } else {
 return String.format("%d", number);
 }
}

Iterative version

Reading and understanding this version is a challenge, and that's a red flag. Don't expect any fellow programmer (and your future self next month) to understand what a line like

r = t.substring(t.length() - 3 * i > 3 ? t.length() - 3 * (i + 1) : 0, t.length() - 3 * i) + " " + r;

does. I guess you cut a chunk out of the original decimal string and prepend that, together with a space, to the result string. Then, at least introduce two variables like

int chunkStart = t.length() - 3 * i > 3 ? t.length() - 3 * (i + 1) : 0;
int chunkEnd = t.length() - 3 * i;
r = t.substring(chunkStart, chunkEnd) + " " + r;

With the ternary-operator conditional, you probably want to avoid a negative chunkStart. I'd write

int chunkStart = Math.max(t.length() - 3 * (i + 1), 0);
int chunkEnd = t.length() - 3 * i;
r = t.substring(chunkStart, chunkEnd) + " " + r;

or

int chunkStart = t.length() - 3 * (i + 1);
if (chunkStart < 0) chunkStart = 0;
int chunkEnd = t.length() - 3 * i;
r = t.substring(chunkStart, chunkEnd) + " " + r;

You seem to love while loops. Typically, for loops are much more readable, as they combine all loop aspects in a single line - your while loop spreads its aspects over half of the method length:

  • The initial value is found in line 12: int i = 1;.
  • The condition is in line 20: while (t.length() > 3 * i) {.
  • The updating is in line 22: i++;.

The variable i that you use in the loop is only loosely related to the values you need inside the loop. You want to iterate a position inside the string backwards from the end, in 3-character steps. A straightforward loop would be e.g.

for (int chunkEnd = t.length() - 3; chunkEnd > 0; chunkEnd -= 3) {
 ...
}
answered Feb 18, 2022 at 17:24
\$\endgroup\$
3
  • 2
    \$\begingroup\$ Thank you - very smart and interesting comments. One always thinks one has thought of everything, but sometimes there are easier ways of doing things. \$\endgroup\$ Commented Feb 18, 2022 at 17:56
  • 2
    \$\begingroup\$ I especially like your negative number recursive solution, why didn't I think of that elegant solution? :) if (number < 0) { return "-" + intToThousandSpacedStringOS(-number); } \$\endgroup\$ Commented Feb 18, 2022 at 17:58
  • \$\begingroup\$ Note that the negative logic is wrong for Integer.MIN_VALUE. Writing code that handles every case correctly is surprisingly hard. \$\endgroup\$ Commented Mar 14, 2022 at 16:16

Your Answer

Draft saved
Draft discarded

Sign up or log in

Sign up using Google
Sign up using Email and Password

Post as a guest

Required, but never shown

Post as a guest

Required, but never shown

By clicking "Post Your Answer", you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.