Конкатенація рядків із 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 використовуватиме метод toString () в об'єкті, що не є рядком вашого виразу "+". Але в Groovy ви просто повинні бути в безпеці, оскільки перший елемент виразу "+" правильно реалізує метод plus (), оскільки Groovy буде шукати та використовувати його. У Groovy GDK лише класи та String / StringBuffer / Character мають метод plus (), реалізований для конкатенації рядків. Щоб уникнути сюрпризів, завжди використовуйте GStrings.

Відповіді:


122

Я завжди вибираю другий метод (за допомогою шаблону GString), хоча, коли є більше ніж пара параметрів, як у вас, я схильний обертати їх, ${X}оскільки, на мою думку, це робить його більш читабельним.

Запуск деяких тестів (за допомогою чудового модуля GBench Nagai Masato ) на цих методах також показує, що шаблонування швидше, ніж інші методи:

@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, але все ж трохи швидше, ніж додавання рядків ...

Але все ж набагато читабельніше.

Змінити після коментаря від 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

1
Я якось пропустив другу половину цього! Звичайно, навіть якщо ви залишите GString"як є", в якийсь момент воно повинно бути перетворене в справжнє String, (навіть просто для його роздрукування), тому справжній час - це останній набір. Зрештою, читабельність GStringшаблонів перемагає, StringBuilderколи терміни наближаються, тож це спірне питання. :-)
OverZealous

2
@OverZealous Аааа, так, як завжди, тут є брехня, проклята брехня та еталони ;-) Читаність тут є ключовою, і, оскільки ми вже використовуємо Groovy, ми заявили, що показники чистого металу не є нашим головним фактором; -)
tim_yates

1
Так, одна з найбільших переваг GStrings полягає в тому, що вони не перетворюються на рядки до останнього моменту. Що означає, наприклад, якщо ви реєструєте GString за допомогою реєстратора, такого як log4j, нижче порога реєстрації, GString взагалі ніколи не перетворюється.
ataylor

1
Тесту не вистачає StringBuilder з розрахунковою ємністю. Причина полягає в тому, що foo + bar + baz призведе до одного або двох розширень буфера, що збільшує час.
ruralcoder

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

Не зовсім впевнений, чому наведену вище відповідь потрібно шукати у тестах, буферах рядків, тестах тощо.


1
Голосуйте за простоту. Мені просто потрібно об'єднати два рядки. lol
harperville

1

Відтворення відповіді tim_yates на поточному обладнанні та додавання методу leftShift () та concat () для перевірки знахідки:

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

Результат показує, що concat () є швидшим рішенням для чистого рядка, але якщо ви можете обробляти 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
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.