16

I was going through the newly added existing features introduced in Java-8. One simple feature newly added to String class is quiet appealing for me – that is String Join method.

Example:

String.join(" ", "AZY","BAX"); // returns AZY BAX

For curiosity, I have checked the performance (execution time) of this feature by writing a simple java code

public static void main(String[] args) {
    long start = System.nanoTime();
    String abc= String.join(" ,"AZY","BAX" … // joining 1000 words of size 3 char;
    long diff = System.nanoTime() - start;
    System.out.println(" Java 8 String Join " + diff);

     start = System.nanoTime();
    abc= "AZY"+"BAX"+"CBA"+ … // adding 1000 word of size 3 char;
    diff = System.nanoTime() - start;
    System.out.println(" Tranditional " + diff);

    start = System.nanoTime();
    new StringBuilder().append("AZY").append("BAX").appe… // appending 1000 word of size 3 char;
    diff = System.nanoTime() - start;
    System.out.println(" String Builder Append " + diff);

}

The result is not so exciting for me (time in neno sec)

Java 8 String Join     1340114
Tranditional             59785
String Builder Append   102807

The complexity is of o(n) – in-fact it is (n * Size of individual element length)

Other performance measures (memory etc) I have not measured.

My questions are:

  1. Is there anything wrong in my measurement (most of the time I believe on the jdk guys)
  2. What is the intent of adding “join” API to String class
  3. Is there any performance analysis for Java 8 is available
dgm
  • 2,287
  • 1
  • 23
  • 27
  • 15
    Writing a correct benchmark in Java is not so simple because of JIT heuristics and garbage collection. See [How do I write a correct micro-benchmark in Java?](http://stackoverflow.com/questions/504103/how-do-i-write-a-correct-micro-benchmark-in-java). – Jesper Jun 05 '14 at 06:56
  • Out of curiosity, why would one use this method instead of `+`? – Djon Jun 05 '14 at 06:58
  • 12
    Also, concatenating string literals is done at compile time, so you're not really testing anything in the second test. – Jesper Jun 05 '14 at 06:58
  • @Djon because `+` is the [slowest operation known to man](http://kaioa.com/node/59). If you want to know more read [this excellent Joel on software article](http://www.joelonsoftware.com/articles/fog0000000319.html). – Boris the Spider Jun 05 '14 at 07:01
  • 1
    Thanks Jesper.. Kishan.. Its my copy/paste miste... It should be String abc= String.join(" ","AZY" ... – dgm Jun 05 '14 at 07:02
  • 5
    the code isn't even equivalent, the join has the delimiter while the traditional and stringbuilder don't. – ratchet freak Jun 05 '14 at 09:58
  • @BoristheSpider look again there are [2](http://docs.oracle.com/javase/8/docs/api/java/lang/String.html#join-java.lang.CharSequence-java.lang.CharSequence...-) [join](http://docs.oracle.com/javase/8/docs/api/java/lang/String.html#join-java.lang.CharSequence-java.lang.Iterable-) methods BOTH use the delimiter. one is just a vararg in which you can also pass an array – ratchet freak Jun 05 '14 at 10:33
  • @ratchetfreak you're right - I just assumed the varags method was `first, rest...` rather than `delim, data...`. Good spot! – Boris the Spider Jun 05 '14 at 10:35

1 Answers1

54

First things first. This is not how you microbench Java

Read How do I write a correct micro-benchmark in Java? first. Your numbers are completely irrelevant, so lets ignore them.

Looking at the second example:

abc= "AZY"+"BAX"+"CBA"+...

These look like compile time constants to me. This String would be concatenated at compile time and there would be nothing to benchmark. This is a useless comparison as the whole point of the StringBuilder or String.join is to concatenate Strings that are not compile time constant.

Moving onto comparing the StringBuilder and String.join. Looking at the source code:

public static String join(CharSequence delimiter, CharSequence... elements) {
    Objects.requireNonNull(delimiter);
    Objects.requireNonNull(elements);
    // Number of elements not likely worth Arrays.stream overhead.
    StringJoiner joiner = new StringJoiner(delimiter);
    for (CharSequence cs: elements) {
        joiner.add(cs);
    }
    return joiner.toString();
}

This uses a StringJoiner. A StringJoiner simply uses a StringBuilder under the hood, so the two are equivalent.

It is often much more informative to look at the code than to try and benchmark performance. Even if you do benchmark correctly.

It's also worth noting that your first method, with join, joins the 1000 Strings on " " (space). Whereas your StringBuilder method simply appends them together. These two are not the same.

The point of the String.join method is that you can do:

String.join(", ", "a", "b", "c") // result is "a, b, c"

With a StringBuilder you would have to add a lot more code.

reevesy
  • 3,452
  • 1
  • 26
  • 23
Boris the Spider
  • 59,842
  • 6
  • 106
  • 166
  • This question it not trying to benchmark Java. but to understand the intent of "join" API. (I think its clear from the questions). I am not still sure why we need an API that does nested looping (one in String.jave- join() method as you said which internally invoke append() method of AbstractStringBuilder) instead of using StringBuilder directly – dgm Jun 05 '14 at 07:16
  • 23
    @dipankaj because the whole point of Java 8 is to add methods that avoid explicit looping and use more functional paradigms. `String.join` is supposed to be a shortcut for `Stream.of(a, b, c).collect(Collectors.joining())`. If you don't see the benefits of allowing users to join a `String` in one line of code rather than 4 then just don't use it... – Boris the Spider Jun 05 '14 at 07:18
  • Unless this is documented, expected behavior, this would be JVM dependent. Which JVM did you pull this from? I was somewhat expecting it to sum the length of the strings in the array and just create the destination array. Especially as using a builder like that is likely to allocate arrays multiple times if you're dealing with the number of strings the OP is dealing with... – Clockwork-Muse Jun 05 '14 at 13:33
  • 1
    @Clockwork-Muse, of course it's JVM dependant - this is Oracle's. The implementation would be the same for OpenJDK I suppose. The algorithm just reuses the `StringJoiner` from the joining `Collector` of the `Stream` API. If you really need to squeeze out more performance then you could do what you say. – Boris the Spider Jun 05 '14 at 13:36
  • 3
    There’s another difference. `String.join` is a *varargs* method which implies before it even starts its work, a temporary array is created and filled with all 1000 `String` references while the `StringBuilder` equivalent in the question just calls `append` 1000 times. – Holger Jun 05 '14 at 16:55
  • How do we understand the impact on memory efficiency when using StringJoiner vs StringBuilder? Is there any impact? – Roman Gherta Aug 11 '18 at 19:49
  • 1
    I don't understand your question @RomanG. As I said a `StringJoiner` uses a `StringBuilder` to do the work. – Boris the Spider Aug 11 '18 at 19:50
  • Understood. Thank you. – Roman Gherta Aug 12 '18 at 06:46