76

Assume the following:

String example      = "something";
String firstLetter  = "";

Are there differences to be aware of with the following ways of assigning firstLetter that could impact performance; which would be best, and why?

firstLetter = String.valueOf(example.charAt(0));
firstLetter = Character.toString(example.charAt(0));
firstLetter = example.substring(0, 1);

The reason the first letter is being returned as a String is that this is being run in Hadoop, and a string is required to assign to a Text type, firstLetter will be output as a key from a map() method, for example:

public class FirstLetterMapper extends Mapper<LongWritable, Text, Text, IntWritable> {
    String line = new String();
    Text firstLetter = new Text();
    IntWritable wordLength = new IntWritable();

    @Override
    public void map(LongWritable key, Text value, Context context)
            throws IOException, InterruptedException {

        line = value.toString();

        for (String word : line.split("\\W+")){
            if (word.length() > 0) {

                // ---------------------------------------------
                // firstLetter assignment
                firstLetter.set(String.valueOf(word.charAt(0)).toLowerCase());
                // ---------------------------------------------

                wordLength.set(word.length());
                context.write(firstLetter, wordLength);
            }
        }
  }
}
Ankur Lathi
  • 7,636
  • 5
  • 37
  • 49
Adrian Torrie
  • 2,795
  • 3
  • 40
  • 61

5 Answers5

119

Performance wise substring(0, 1) is better as found by following:

    String example = "something";
    String firstLetter  = "";

    long l=System.nanoTime();
    firstLetter = String.valueOf(example.charAt(0));
    System.out.println("String.valueOf: "+ (System.nanoTime()-l));

    l=System.nanoTime();
    firstLetter = Character.toString(example.charAt(0));
    System.out.println("Character.toString: "+ (System.nanoTime()-l));

    l=System.nanoTime();
    firstLetter = example.substring(0, 1);
    System.out.println("substring: "+ (System.nanoTime()-l));

Output:

String.valueOf: 38553
Character.toString: 30451
substring: 8660
Ankur Lathi
  • 7,636
  • 5
  • 37
  • 49
  • 15
    This is way after the fact, but I feel like a more appropriate method of timing would involve performing this test many times, rather than once as implied by the code above. Please see my test code at gist.github.com/micahjsmith/c7c9d31b342c115aef90. Running Java 1.7 on GNU/Linux, I found no difference between the three methods as shown above, and if anything, String.valueOf had the best performance from what I saw. (Though there was some variation between runs.) This leads me inclined to believe that @yshavit below has a more applicable response to the question. – Micah Smith Jan 16 '15 at 19:06
15

Long story short, it probably doesn't matter. Use whichever you think looks nicest.

Longer answer, using Oracle's Java 7 JDK specifically, since this isn't defined at the JLS:

String.valueOf or Character.toString work the same way, so use whichever you feel looks nicer. In fact, Character.toString simply calls String.valueOf (source).

So the question is, should you use one of those or String.substring. Here again it doesn't matter much. String.substring uses the original string's char[] and so allocates one object fewer than String.valueOf. This also prevents the original string from being GC'ed until the one-character string is available for GC (which can be a memory leak), but in your example, they'll both be available for GC after each iteration, so that doesn't matter. The allocation you save also doesn't matter -- a char[1] is cheap to allocate, and short-lived objects (as the one-char string will be) are cheap to GC, too.

If you have a large enough data set that the three are even measurable, substring will probably give a slight edge. Like, really slight. But that "if... measurable" contains the real key to this answer: why don't you just try all three and measure which one is fastest?

yshavit
  • 42,327
  • 7
  • 87
  • 124
12
String whole = "something";
String first = whole.substring(0, 1);
System.out.println(first);
Oleg Kurbatov
  • 1,376
  • 1
  • 19
  • 32
MIk.13
  • 129
  • 1
  • 4
  • This doesn't add anything, as this code is already proposed in the original question, and further extended in all answers to date. – Adrian Torrie Jul 05 '17 at 05:11
2
import org.openjdk.jmh.annotations.Benchmark;
import org.openjdk.jmh.annotations.BenchmarkMode;
import org.openjdk.jmh.annotations.Fork;
import org.openjdk.jmh.annotations.Measurement;
import org.openjdk.jmh.annotations.Mode;
import org.openjdk.jmh.annotations.OutputTimeUnit;
import org.openjdk.jmh.annotations.Scope;
import org.openjdk.jmh.annotations.Setup;
import org.openjdk.jmh.annotations.State;
import org.openjdk.jmh.annotations.Warmup;

import java.util.concurrent.TimeUnit;

@State(Scope.Thread)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@Warmup(iterations = 5, time = 1)
@Fork(value = 1)
@Measurement(iterations = 5, time = 1)
public class StringFirstCharBenchmark {

    private String source;

    @Setup
    public void init() {
        source = "MALE";
    }

    @Benchmark
    public String substring() {
        return source.substring(0, 1);
    }

    @Benchmark
    public String indexOf() {
        return String.valueOf(source.indexOf(0));
    }
}

Results:

+----------------------------------------------------------------------+
| Benchmark                           Mode  Cnt   Score   Error  Units |
+----------------------------------------------------------------------+
| StringFirstCharBenchmark.indexOf    avgt    5  23.777 ? 5.788  ns/op |
| StringFirstCharBenchmark.substring  avgt    5  11.305 ? 1.411  ns/op |
+----------------------------------------------------------------------+
Nikita
  • 21
  • 1
-6
import java.io.*;
class Initials {

    public static void main(String args[]) throws IOException {
        BufferedReader br = new BufferedReader(new InputStreamReader(System.in));
        String s;
        char x;
        int l;
        System.out.print("Enter any sentence: ");
        s = br.readLine();
        s = " " + s; //adding a space infront of the inputted sentence or a name
        s = s.toUpperCase(); //converting the sentence into Upper Case (Capital Letters)
        l = s.length(); //finding the length of the sentence
        System.out.print("Output = ");

        for (int i = 0; i < l; i++) {
            x = s.charAt(i); //taking out one character at a time from the sentence
            if (x == ' ') //if the character is a space, printing the next Character along with a fullstop
                System.out.print(s.charAt(i + 1) + ".");
        }
    }
}
Jack T
  • 315
  • 1
  • 6
  • 18
rohan
  • 1