Rangkaian string dengan Groovy

91

Apa cara (idiomatik) terbaik untuk menggabungkan String di Groovy?

Pilihan 1:

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

Pilihan 2:

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

Saya telah menemukan poin menarik tentang topik ini di situs web Groovy lama: Hal-hal yang dapat Anda lakukan tetapi lebih baik biarkan tidak diselesaikan.

Seperti di Java, Anda dapat menggabungkan String dengan simbol "+". Tetapi Java hanya membutuhkan salah satu dari dua item ekspresi "+" menjadi String, tidak peduli apakah itu di tempat pertama atau di yang terakhir. Java akan menggunakan metode toString () di objek non-String dari ekspresi "+" Anda. Tetapi di Groovy, Anda harus aman, item pertama dari ekspresi "+" Anda mengimplementasikan metode plus () dengan cara yang benar, karena Groovy akan mencari dan menggunakannya. Di Groovy GDK, hanya kelas Number dan String / StringBuffer / Character yang memiliki metode plus () yang diimplementasikan untuk menggabungkan string. Untuk menghindari kejutan, selalu gunakan GString.

Arturo Herrero
sumber

Jawaban:

122

Saya selalu menggunakan metode kedua (menggunakan template GString), meskipun ketika ada lebih dari beberapa parameter seperti yang Anda miliki, saya cenderung membungkusnya ${X}karena menurut saya itu membuatnya lebih mudah dibaca.

Menjalankan beberapa tolok ukur (menggunakan modul GBench Nagai Masato yang sangat baik ) pada metode ini juga menunjukkan pembuatan template lebih cepat daripada metode lainnya:

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

Itu memberi saya output berikut di mesin saya:

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

Jadi dengan keterbacaan dan kecepatan yang mendukungnya, saya akan merekomendasikan template ;-)

NB: Jika Anda menambahkan toString()ke akhir metode GString untuk membuat jenis keluaran sama dengan metrik lainnya, dan menjadikannya pengujian yang lebih adil, StringBuilderdan StringBuffermengalahkan metode GString untuk kecepatan. Namun karena GString dapat digunakan sebagai pengganti String untuk banyak hal (Anda hanya perlu berhati-hati dengan kunci Peta dan pernyataan SQL), sebagian besar dapat dibiarkan tanpa konversi terakhir ini.

Menambahkan tes ini (seperti yang telah ditanyakan di komentar)

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

Sekarang kita mendapatkan hasilnya:

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

Jadi seperti yang Anda lihat (seperti yang saya katakan), ini lebih lambat dari StringBuilder atau StringBuffer, tetapi masih sedikit lebih cepat daripada menambahkan Strings ...

Tapi masih lebih banyak yang bisa dibaca.

Edit setelah komentar oleh ruralcoder di bawah ini

Diperbarui ke gbench terbaru, string yang lebih besar untuk penggabungan, dan pengujian dengan StringBuilder yang diinisialisasi ke ukuran yang baik:

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

memberi

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
tim_yates
sumber
3
Saya tidak setuju dengan penggunaan template GString untuk keterbacaan, tetapi Anda harus menjalankan kembali pengujian dengan .toString()menambahkan dua pengujian GString. Lari saya menunjukkan bahwa mereka kemudian melakukan hampir sama dengan String adder. Dugaan saya adalah bahwa tes yang Anda jalankan tidak benar-benar menangani penggabungan, jadi ini hanya membuat objek GString dan menyimpan referensi. StringBuildermasih yang tercepat, tangan ke bawah, jika Anda membutuhkannya Stringdi beberapa titik.
OverZealous
1
Entah bagaimana, saya melewatkan paruh kedua itu! Tentu saja, bahkan jika Anda meninggalkan GString"apa", di beberapa titik itu harus diubah menjadi benar String, (bahkan hanya untuk mencetak keluar), sehingga waktu yang benar adalah set terakhir. Pada akhirnya keterbacaan GStringtemplat mengalahkan StringBuilderketika waktunya sedekat ini, jadi ini diperdebatkan. :-)
OverZealous
2
@OverZealous Ahhh ya, seperti biasa, ada kebohongan, kebohongan terkutuk, dan tolok ukur ;-) Keterbacaan adalah kunci di sini saya rasa dan karena kami sudah menggunakan Groovy, kami telah menyatakan bahwa kinerja bare-metal bukanlah pertimbangan utama kami; -)
tim_yates
1
Ya, salah satu keuntungan terbesar dari GString adalah bahwa mereka tidak diubah menjadi string sampai saat-saat terakhir. Artinya, misalnya, jika Anda mencatat GString dengan logger seperti log4j di bawah ambang logging, GString tidak pernah dikonversi sama sekali.
ataylor
1
Apa yang hilang dari pengujian adalah StringBuilder dengan kapasitas yang dihitung. Alasannya adalah bahwa foo + bar + baz akan menyebabkan satu atau dua ekspansi buffer yang menambah waktu.
ruralcoder
19
def my_string = "some string"
println "here: " + my_string 

Tidak begitu yakin mengapa jawaban di atas perlu masuk ke tolok ukur, buffer string, tes, dll.

Snowcrash
sumber
2
Beri suara positif untuk kesederhanaan. Saya hanya perlu menggabungkan dua string. lol
harperville
2

Mereproduksi jawaban tim_yates pada perangkat keras saat ini dan menambahkan metode leftShift () dan concat () untuk memeriksa temuan:

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

Hasilnya menunjukkan concat () sebagai solusi yang lebih cepat untuk String murni, tetapi jika Anda dapat menangani GString di tempat lain, template GString masih maju, sementara sebutan terhormat harus diarahkan ke leftShift () (operator bitwise) dan StringBuffer () dengan inisial alokasi:

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
thoroc.dll
sumber