Groovyによる文字列の連結


91

Groovyで文字列を連結するための最良の(慣用的な)方法は何ですか?

オプション1:

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

オプション2:

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

以前のGroovy Webサイトでこのトピックに関する興味深いポイントを見つけました。

Javaと同様に、文字列を「+」記号で連結できます。しかし、Javaは、「+」式の2つの項目のうちの1つが文字列であることだけを必要とします。それが最初にあるか、最後にあるかは関係ありません。Javaは、「+」式の非文字列オブジェクトでtoString()メソッドを使用します。しかし、Groovyでは、「+」式の最初の項目がplus()メソッドを正しい方法で実装するので安全である必要があります。Groovyはそれを検索して使用するからです。Groovy GDKでは、NumberクラスとString / StringBuffer / Characterクラスのみに、文字列を連結するために実装されたplus()メソッドがあります。驚きを避けるために、常にGStringsを使用してください。

回答:


122

私はいつも(GStringテンプレートを使用して)2番目のメソッドを使用しますが、あなたのように2つ以上のパラメーターがある場合は、${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メソッドの最後に追加して、出力タイプを他のメトリックと同じにし、より公平なテストにStringBuilderしてStringBuffer、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を追加するよりも少し高速です...

しかし、それでもずっと読みやすくなっています。

以下のuralcoderによるコメントの後に編集してください

最新の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()が、2つのGStringテストを追加してテストを再実行する必要があります。私の実行は、それらがその後とほぼ同じように機能することを示していString adderます。私の推測では、実行したテストは実際には連結を処理しないため、GStringオブジェクトを作成して参照を格納しているだけです。 ある時点でStringBuilder必要な場合は、まだ最速のハンドダウンですString
OverZealous

1
なんとか後半を逃した!もちろん、GString「現状のまま」のままでも、ある時点でtrue Stringに変換する必要があります(印刷する場合でも)ので、実際のタイミングは最後のセットです。結局のところ、GStringテンプレートの読みやすさはStringBuilder、タイミングがこれに近いときに上回っているため、意味がありません。:-)
OverZealous

2
@OverZealous Ahhhはい、いつものように、嘘、のろわれた嘘、ベンチマークあります ;-)ここでは読みやすさが鍵となり、Groovyをすでに使用しているため、ベアメタルパフォーマンスは私たちの主要な考慮事項ではないと述べました。 -)
tim_yates 2012

1
ええ、GStringの大きな利点の1つは、最後まで文字列に変換されないことです。つまり、たとえば、log4jのようなロガーを使用してGStringをロギングしきい値未満でログに記録した場合、GStringはまったく変換されません。
テイラー

1
テストに欠けているのは、計算された容量を持つStringBuilderです。その理由は、foo + bar + bazは1つまたは2つのバッファー拡張を引き起こし、時間を増加させるからです。
田舎のコーダー2013

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

上記の答えがベンチマーク、文字列バッファ、テストなどに入る必要がある理由がよくわかりません。


1
単純化のために賛成票を投じます。2つの文字列を連結するだけです。笑
ハーパービル

1

現在のハードウェアでのtim_yates回答の再現と、結果を確認するためのleftShift()およびconcat()メソッドの追加:

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

結果は、concat()が純粋な文字列のより高速なソリューションであることを示していますが、GStringを他の場所で処理できる場合、GStringテンプレートはまだ先です。割り当て:

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
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.