36

So this is the task: Given a string, return a string where for every char in the original, there are two chars.

And I don't understand why its output are numbers instead of letters, I tried doesn't work?

public String doubleChar(String str) {
  String s = "";
  for(int i=0; i<str.length(); i++){
  s +=  str.charAt(i) + str.charAt(i);
  
  }
    return s;
}

Expected :

doubleChar("The") → "TThhee"

doubleChar("AAbb") → "AAAAbbbb"

Output:

doubleChar("The") → "168208202"

doubleChar("AAbb") → "130130196196"

Community
  • 1
  • 1
sctts-lol
  • 557
  • 5
  • 9
  • 22
    Adding two `char`s doesn't give you a string with those two characters, but rather [an int](https://stackoverflow.com/questions/8688668/in-java-is-the-result-of-the-addition-of-two-chars-an-int-or-a-char) with numerical value equal to the sum of the two characters' numerical values. – nanofarad Jul 10 '19 at 19:54
  • 5
    Man, I thought this would be a perfect example to introduce a novice Java user to Java streams, and boy, it's a disaster! https://repl.it/@alexandermomchilov/Java-Double-chars-using-Streams Compare: https://repl.it/@alexandermomchilov/Double-chars-using-Streams-Swift – Alexander Jul 11 '19 at 04:38
  • 2
    @Alexander There is much simpler, even in java:` public static String doubleCharacters(String input) { return input.chars() .mapToObj(c -> "" + (char)c + (char)c) .collect(Collectors.joining()); } ` – Benoit Jul 11 '19 at 08:57
  • 1
    @Alexander @Benoit : Even more efficient (only one temporary array, no boxing/unboxing), with fewer ugly casts and doesn't mangle asian text that use surrogate char pairs: `new String(str.codePoints().flatMap(i -> IntStream.of(i, i)).toArray(), 0, str.length() * 2);` – LordOfThePigs Jul 11 '19 at 12:58
  • @Benoit I was trying to avoid the heap allocation for all those small strings. Though I think Java uses tagged pointers to optimize away the heap allocations for that, so maybe it's okay – Alexander Jul 11 '19 at 15:02
  • 1
    @LordOfThePigs Great stuff. I wonder, does String defensively copy an array like this, or does it just take ownership and use it as its backing storage? – Alexander Jul 11 '19 at 15:03
  • @Alexander String always does a defensive copy, it's the only way it can guarantee immutability in all cases. Doing it in a good old for-loop with manual array manipulations is still the fastest (no extra method calls, no IntStream allocations, etc...), but streams give you a cute one-liner. – LordOfThePigs Jul 22 '19 at 13:04
  • @stevie lol when you add characters in Java it adds ascii value for same.Here you are adding this ascii value to String so you are getting ascii value appended one after another. – Pratik Jul 28 '19 at 07:41

5 Answers5

54

In Java the char primitive type is basically just a numeric value that maps to a character, so if you add two char values together they produce a number and not another char (and not a String) so you end up with an int as you're seeing.

To fix this you can use the Character.toString(char) method like this:

s += Character.toString(str.charAt(i)) + Character.toString(str.charAt(i))

But this is all fairly inefficient because you're doing this in a loop and so string concatenation is producing a lot of String objects needlessly. More efficient is to use a StringBuilder and its append(char) method like this:

StringBuilder sb = new StringBuilder(str.length() * 2);
for (int i = 0; i < str.length(); ++i) {
    char c = str.charAt(i);
    sb.append(c).append(c);
}
return sb.toString();
Bobulous
  • 12,967
  • 4
  • 37
  • 68
  • 3
    This should be correct answer. Creating new String objects each loop iteration is not good practice. Using a StringBuilder is more efficient. Look here for exaplanation https://stackoverflow.com/a/18453485/8685250 – Hayes Roach Jul 10 '19 at 20:12
  • At least since Java 8 the compiler is converting regular string concats to stringbuilder constructs where it helps. There is no need to make your code less clear or more complicated of the compiler can optimize better, anyways – marstato Jul 11 '19 at 07:03
  • 4
    @marstato that is true, but it doesn't apply in this case. The Java compiler will indeed convert sequences of string concatenations to sequences of StringBuilder.append. That will cover the `s += s1 + s2` statement inside the `for` block, but it will be compiled such that each execution of this statement creates its own `StringBuilder`. Manually creaing it outside of the loop won't have this issue. – LordOfThePigs Jul 11 '19 at 12:37
  • @marstato LordOfThePigs is right: the problem with string concatenation within a loop is that it forces the creation of a new `StringBuilder` object for every iteration. Also, the default capacity of a `StringBuilder` is only 16 characters, so a lot of appending will force numerous resizes, thus creating more objects. I have created a JMH benchmark to test this and found that in a loop of 1000 iterations, String concatenation generates 11MiB of garbage, and runs at 876 ops/sec, while a `StringBuilder` with optimum capacity generates only 29KiB of garbage and runs at 39,734 ops/sec. – Bobulous Jul 11 '19 at 19:37
  • @Edward they are just passing down useful information. You have to remember how much "magic" happens behind the scenes in Java. It isn't like other languages, where the most efficient way is the most obvious way. – Joshua Blevins Jul 16 '19 at 13:42
23

Why does it output integers?

The + operator is overloaded in Java to perform String concatenation only for Strings, not chars.

From the Java Spec:

If the type of either operand of a + operator is String, then the operation is string concatenation.

Otherwise, the type of each of the operands of the + operator must be a type that is convertible (§5.1.8) to a primitive numeric type, or a compile-time error occurs.

In your case, char is converted to its primitive value (int), then added.

Instead, use StringBuilder.append(char) to concatenate them into a String.

If performance is not a concern, you could even do:

char c = 'A';
String s = "" + c + c;

and s += "" + c + c;

That will force the + String concatenation operator because it starts with a String (""). The Java Spec above explains with examples:

The + operator is syntactically left-associative, no matter whether it is determined by type analysis to represent string concatenation or numeric addition. In some cases care is required to get the desired result. For example [...]

1 + 2 + " fiddlers" is "3 fiddlers"

but the result of:

"fiddlers " + 1 + 2 is "fiddlers 12"

Edward
  • 580
  • 3
  • 15
12

You are adding numeric values of chars first before concatenating the result (now integer) to the string. Try debugging with print statements:

public static String doubleChar(String str) {
  String s = "";
  for (int i = 0; i < str.length(); i++) {
    System.out.println(str.charAt(i));
    System.out.println(str.charAt(i) + str.charAt(i));
    s += str.charAt(i) + str.charAt(i);
  }
  return s;
}

The more efficient way of doing what you want is:

public static String doubleChar(String str) {
  StringBuilder sb = new StringBuilder(str.length() * 2);
  for (int i = 0; i < str.length(); i++) {
    char c = str.charAt(i);
    sb.append(c).append(c);
  }
  return sb.toString();
}
LeffeBrune
  • 3,441
  • 1
  • 23
  • 36
  • 3
    I agree he should be using `StringBuilder` since he is concatenating a `String` inside of a loop, but it may be helpful to explain briefly why it is better. – Nexevis Jul 10 '19 at 20:04
  • At least since Java 8 the compiler is converting regular string concats to stringbuilder constructs where it helps. There is no need to make your code less clear or more complicated of the compiler can optimize better, anyways – marstato Jul 11 '19 at 07:03
  • @marstato Using the stringbuilder is *at least* as clear as any solution not using the string builder. It also offers the opportunity to initialize the string builder to the right size (and I doubt the compiler reliably does that). – Martin Bonner supports Monica Jul 11 '19 at 09:48
  • @marstato compiler would only convert concatenation of strings into a string builder inside of the loop. A new string and string builder would be created on every iteration AFAIK. – LeffeBrune Jul 11 '19 at 14:13
  • A better suggestion is to try debugging with a debugger. – OrangeDog Jul 11 '19 at 14:19
2

You are adding the value of two characters together. Change the String concatenation from:

s +=  str.charAt(i) + str.charAt(i);

To:

s +=  str.charAt(i) + "" + str.charAt(i);

Which will ensure the characters convert to a String.

Note: This is a quick fix, and you should use StringBuilder when String concatenating inside of a loop. See the other answers for how this is done.

Nexevis
  • 4,647
  • 3
  • 13
  • 22
2

String.charAt() returns a char (https://docs.oracle.com/javase/7/docs/api/java/lang/String.html#charAt(int)) so you're dealing with a single character. With onechar, you can perform operations on it like this (which will print "65" which is ASCII value for the 'A' character):

System.out.println('A' + 0);

you can print the ASCII value for the next character ("B") by adding 1 to 'A', like this:

System.out.println('A' + 1);

To make your code work – so that it doubles each character – there are number of options. You could append each character one at a time:

s += str.charAt(i);
s += str.charAt(i);

or various ways of casting the operation to a string:

s += "" + str.charAt(i) + str.charAt(i);
Kaan
  • 5,434
  • 3
  • 19
  • 41