Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

String concatenation with Groovy

What is the best (idiomatic) way to concatenate Strings in Groovy?

Option 1:

calculateAccountNumber(bank, branch, checkDigit, account) {     bank + branch + checkDigit + account } 

Option 2:

calculateAccountNumber(bank, branch, checkDigit, account) {     "$bank$branch$checkDigit$account" } 

I've founded an interesting point about this topic in the old Groovy website: Things you can do but better leave undone.

As in Java, you can concatenate Strings with the "+" symbol. But Java only needs that one of the two items of a "+" expression to be a String, no matter if it's in the first place or in the last one. Java will use the toString() method in the non-String object of your "+" expression. But in Groovy, you just should be safe the first item of your "+" expression implements the plus() method in the right way, because Groovy will search and use it. In Groovy GDK, only the Number and String/StringBuffer/Character classes have the plus() method implemented to concatenate strings. To avoid surprises, always use GStrings.

like image 464
Arturo Herrero Avatar asked Jul 06 '12 09:07

Arturo Herrero


People also ask

How do I use append in Groovy?

Groovy - add() Append the new value to the end of this List. This method has 2 different variants. boolean add(Object value) − Append the new value to the end of this List.

How do I add a variable in Groovy?

Variables in Groovy can be defined in two ways − using the native syntax for the data type or the next is by using the def keyword. For variable definitions it is mandatory to either provide a type name explicitly or to use "def" in replacement. This is required by the Groovy parser.


1 Answers

I always go for the second method (using the GString template), though when there are more than a couple of parameters like you have, I tend to wrap them in ${X} as I find it makes it more readable.

Running some benchmarks (using Nagai Masato's excellent GBench module) on these methods also shows templating is faster than the other methods:

@Grab( 'com.googlecode.gbench:gbench:0.3.0-groovy-2.0' ) import gbench.*  def (foo,bar,baz) = [ 'foo', 'bar', 'baz' ] new BenchmarkBuilder().run( measureCpuTime:false ) {   // Just add the strings   'String adder' {     foo + bar + baz   }   // Templating   'GString template' {     "$foo$bar$baz"   }   // I find this more readable   'Readable GString template' {     "${foo}${bar}${baz}"   }   // StringBuilder   'StringBuilder' {     new StringBuilder().append( foo )                        .append( bar )                        .append( baz )                        .toString()   }   'StringBuffer' {     new StringBuffer().append( foo )                       .append( bar )                       .append( baz )                       .toString()   } }.prettyPrint() 

That gives me the following output on my machine:

Environment =========== * Groovy: 2.0.0 * JVM: Java HotSpot(TM) 64-Bit Server VM (20.6-b01-415, Apple Inc.)     * JRE: 1.6.0_31     * Total Memory: 81.0625 MB     * Maximum Memory: 123.9375 MB * OS: Mac OS X (10.6.8, x86_64)   Options ======= * Warm Up: Auto  * CPU Time Measurement: Off  String adder               539 GString template           245 Readable GString template  244 StringBuilder              318 StringBuffer               370 

So with readability and speed in it's favour, I'd recommend templating ;-)

NB: If you add toString() to the end of the GString methods to make the output type the same as the other metrics, and make it a fairer test, StringBuilder and StringBuffer beat the GString methods for speed. However as GString can be used in place of String for most things (you just need to exercise caution with Map keys and SQL statements), it can mostly be left without this final conversion

Adding these tests (as it has been asked in the comments)

  'GString template toString' {     "$foo$bar$baz".toString()   }   'Readable GString template toString' {     "${foo}${bar}${baz}".toString()   } 

Now we get the results:

String adder                        514 GString template                    267 Readable GString template           269 GString template toString           478 Readable GString template toString  480 StringBuilder                       321 StringBuffer                        369 

So as you can see (as I said), it is slower than StringBuilder or StringBuffer, but still a bit faster than adding Strings...

But still lots more readable.

Edit after comment by ruralcoder below

Updated to latest gbench, larger strings for concatenation and a test with a StringBuilder initialised to a good size:

@Grab( 'org.gperfutils:gbench:0.4.2-groovy-2.1' )  def (foo,bar,baz) = [ 'foo' * 50, 'bar' * 50, 'baz' * 50 ] benchmark {   // Just add the strings   'String adder' {     foo + bar + baz   }   // Templating   'GString template' {     "$foo$bar$baz"   }   // I find this more readable   'Readable GString template' {     "${foo}${bar}${baz}"   }   'GString template toString' {     "$foo$bar$baz".toString()   }   'Readable GString template toString' {     "${foo}${bar}${baz}".toString()   }   // StringBuilder   'StringBuilder' {     new StringBuilder().append( foo )                        .append( bar )                        .append( baz )                        .toString()   }   'StringBuffer' {     new StringBuffer().append( foo )                       .append( bar )                       .append( baz )                       .toString()   }   'StringBuffer with Allocation' {     new StringBuffer( 512 ).append( foo )                       .append( bar )                       .append( baz )                       .toString()   } }.prettyPrint() 

gives

Environment =========== * Groovy: 2.1.6 * JVM: Java HotSpot(TM) 64-Bit Server VM (23.21-b01, Oracle Corporation)     * JRE: 1.7.0_21     * Total Memory: 467.375 MB     * Maximum Memory: 1077.375 MB * OS: Mac OS X (10.8.4, x86_64)  Options ======= * Warm Up: Auto (- 60 sec) * CPU Time Measurement: On                                      user  system  cpu  real  String adder                         630       0  630   647 GString template                      29       0   29    31 Readable GString template             32       0   32    33 GString template toString            429       0  429   443 Readable GString template toString   428       1  429   441 StringBuilder                        383       1  384   396 StringBuffer                         395       1  396   409 StringBuffer with Allocation         277       0  277   286 
like image 162
tim_yates Avatar answered Oct 07 '22 04:10

tim_yates