1. ホーム
  2. string

[解決済み] Groovyによる文字列の連結

2023-01-22 22:43:48

質問

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

オプション1:

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

オプション 2:

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

このトピックについては、昔のGroovyのサイトで面白い指摘をしたことがあります。できるけど、やらないほうがいいこと。

Javaでは、文字列の連結は"+"という記号で行います。しかし、Java は、"+" 式の2つの項目のうち1つが文字列であることだけが必要です。 しかし、Javaでは、"+"式の2つの項目のうち1つが文字列であればよく、それが最初であろうと最後であろうと、文字列は必要ありません。Java は、"+".式の非StringオブジェクトでtoString()メソッドを使用します。 の式に変換します。しかし、Groovyでは、最初の項目が安全であればよいのです。 しかし、Groovyの場合は、"+"式の最初の項目が正しい方法でplus()メソッドを実装していることを確認するだけです。 というのも、Groovyではそれを検索して使うからです。Groovy GDKでは、Number(数値) とString/StringBuffer/Characterクラスだけが、文字列を連結するためのplus() メソッドを実装しています。 メソッドが実装されています。驚かないように、常に GStringsを使用してください。

どのように解決するのですか?

私はいつも2番目の方法(GStringテンプレートを使用する)をとりますが、あなたのようにパラメータが2つ以上ある場合は、それらを ${X} で囲むことが多いのですが、その方が読みやすくなります。

いくつかのベンチマークを実行し ( 永井正人 の優れた 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よりは遅いですが、それでもStringを追加するよりは少し速いです...。

しかし、まだずっと読みやすい。

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