用Groovy进行字符串连接


91

在Groovy中串联字符串的最佳(惯用)方法是什么?

选项1:

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

选项2:

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

我已经在旧的Groovy网站上找到了有关此主题的有趣观点:您可以做的事情最好不要做。

与Java中一样,您可以将字符串与“ +”符号连接起来。但是Java只需要将“ +”表达式的两个项目之一作为字符串,无论它在首位还是在最后一位。Java将在“ +”表达式的非String对象中使用toString()方法。但是在Groovy中,您应该安全无忧,“ +”表达式的第一项以正确的方式实现plus()方法,因为Groovy会搜索并使用它。在Groovy GDK中,只有Number和String / StringBuffer / Character类具有实现串联字符串的plus()方法。为避免意外,请始终使用GStrings。

Answers:


122

我总是选择第二种方法(使用GString模板),尽管当有多个像您这样的参数时,我倾向于将它们包裹起来,${X}因为我发现它使它更具可读性。

在这些方法上运行一些基准测试(使用Nagai Masato的出色的GBench模块)还显示出模板比其他方法更快:

@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()

这使我在机器上获得以下输出:

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

因此,出于可读性和速度的考虑,我建议模板;-)

注意:如果您添加toString()到的GString的方法,最终使输出类型一样的其他指标,使之更公平的测试,StringBuilderStringBuffer击败了速度GString型方法。但是,由于GString在大多数情况下都可以代替String使用(您只需要谨慎使用Map键和SQL语句),因此大多数情况下,无需进行最终转换即可

添加这些测试(如评论中所要求的)

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

现在我们得到结果:

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

因此,正如您所见(如我所说),它比StringBuilder或StringBuffer慢,但仍比添加Strings快一点...

但是仍然更具可读性。

在下面的ruralcoder评论后编辑

已更新为最新的gbench,较大的字符串以进行连接,并使用StringBuilder将测试初始化​​为合适的大小:

@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()

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

3
我不会不同意使用GString模板的可读性,但是您应该.toString()在两个GString测试的后面追加重新运行测试。我的跑步表明,他们的表现几乎与相同String adder。我的猜测是,您运行的测试实际上并未处理串联,因此它只是创建一个GString对象并存储引用。 StringBuilder仍然是最快的,如果需要的话,请放手String
OverZealous 2012年

1
我以某种方式错过了下半场!当然,即使您保留GString“原样”,在某些时候也必须将其转换为true String(即使只是将其打印出来),因此真实的时间是最后一个设置。最后,当时间临近时,GString模板的易读性胜过StringBuilder,因此没有意义。:-)
OverZealous 2012年

2
@OverZealous Ahhh是的,一如既往,有谎言,该死的谎言和基准;-)在这里,可读性是关键,因为我们已经在使用Groovy,所以我们已经指出,裸机性能不是我们的主要考虑因素; -)
tim_yates 2012年

1
是的,GStrings的一大优点是它们直到最后一刻才转换为字符串。这意味着,例如,如果使用低于日志记录阈值的log4j之类的记录器记录GString,则GString根本不会转换。
ataylor 2012年

1
测试中缺少的是具有计算能力的StringBuilder。原因是foo + bar + baz将导致一两个缓冲区扩展,这会增加时间。
13年

19
def my_string = "some string"
println "here: " + my_string 

不太确定为什么以上答案需要进入基准测试,字符串缓冲区,测试等。


1
为了简单起见。我只需要连接两个字符串。大声笑
哈珀维尔

1

在当前硬件上重现tim_yates答案,并添加leftShift()和concat()方法以检查发现:

  'String leftShift' {
    foo << bar << baz
  }
  'String concat' {
    foo.concat(bar)
       .concat(baz)
       .toString()
  }

结果表明concat()是纯String的更快解决方案,但是如果您可以在其他地方处理GString,则GString模板仍在前面,同时值得一提的是应该将leftShift()(按位运算符)和StringBuffer()放在初始位置分配:

Environment
===========
* Groovy: 2.4.8
* JVM: OpenJDK 64-Bit Server VM (25.191-b12, Oracle Corporation)
    * JRE: 1.8.0_191
    * Total Memory: 238 MB
    * Maximum Memory: 3504 MB
* OS: Linux (4.19.13-300.fc29.x86_64, amd64)

Options
=======
* Warm Up: Auto (- 60 sec)
* CPU Time Measurement: On

                                    user  system  cpu  real

String adder                         453       7  460   469
String leftShift                     287       2  289   295
String concat                        169       1  170   173
GString template                      24       0   24    24
Readable GString template             32       0   32    32
GString template toString            400       0  400   406
Readable GString template toString   412       0  412   419
StringBuilder                        325       3  328   334
StringBuffer                         390       1  391   398
StringBuffer with Allocation         259       1  260   265
By using our site, you acknowledge that you have read and understand our Cookie Policy and Privacy Policy.
Licensed under cc by-sa 3.0 with attribution required.