การต่อสตริงกับ Groovy


93

วิธีที่ดีที่สุด (สำนวน) ในการต่อสตริงใน Groovy คืออะไร

ตัวเลือกที่ 1:

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

ทางเลือกที่ 2:

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

ฉันได้พบประเด็นที่น่าสนใจเกี่ยวกับหัวข้อนี้ในเว็บไซต์ Groovy เก่า: สิ่งที่คุณทำได้ แต่ดีกว่าปล่อยให้เลิกทำ

เช่นเดียวกับใน Java คุณสามารถเชื่อมสตริงที่มีสัญลักษณ์ "+" เข้าด้วยกัน แต่ Java ต้องการให้หนึ่งในสองรายการของนิพจน์ "+" เป็น String ไม่ว่าจะอยู่ในตำแหน่งแรกหรือรายการสุดท้ายก็ตาม Java จะใช้เมธอด toString () ในออบเจ็กต์ที่ไม่ใช่สตริงของนิพจน์ "+" ของคุณ แต่ใน Groovy คุณควรจะปลอดภัยรายการแรกของนิพจน์ "+" ของคุณใช้เมธอด plus () อย่างถูกวิธีเพราะ Groovy จะค้นหาและใช้มัน ใน Groovy GDK เฉพาะคลาส Number และ String / StringBuffer / Character เท่านั้นที่มีวิธีการบวก () ที่ใช้เพื่อเชื่อมสตริงเข้าด้วยกัน เพื่อหลีกเลี่ยงความประหลาดใจให้ใช้ GStrings เสมอ

คำตอบ:


124

ฉันมักจะใช้วิธีที่สองเสมอ (โดยใช้เทมเพลต 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 สำหรับสิ่งต่างๆได้มากที่สุด (คุณเพียงแค่ต้องใช้ความระมัดระวังกับคีย์แผนที่และคำสั่ง 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

1
ฉันพลาดครึ่งหลังไปแล้ว! แน่นอนแม้ว่าคุณจะออกจากGString"ตามสภาพ" ในบางครั้งจุดก็ต้องแปลงเป็นจริงString(แม้จะพิมพ์ออกมาก็ตาม) ดังนั้นเวลาที่แท้จริงจึงเป็นชุดสุดท้าย ในท้ายที่สุดความชัดเจนของGStringเทมเพลตจะเต้นStringBuilderเมื่อเวลาใกล้เคียงกันดังนั้นจึงเป็นที่น่าสงสัย :-)
OverZealous

2
@OverZealous Ahhh ใช่เช่นเคยมี โกหกคำโกหกและเกณฑ์มาตรฐาน ;-) ความสามารถในการอ่านเป็นกุญแจสำคัญที่นี่ฉันรู้สึกและในขณะที่เราใช้ Groovy อยู่แล้วเราได้กล่าวว่าประสิทธิภาพของโลหะเปลือยไม่ใช่สิ่งสำคัญของเรา -)
tim_yates

1
ใช่ข้อดีอย่างหนึ่งของ GStrings คือพวกมันจะไม่ถูกแปลงเป็นสตริงจนกว่าจะถึงวินาทีสุดท้าย ซึ่งหมายความว่าตัวอย่างเช่นหากคุณบันทึก GString ด้วยตัวบันทึกเช่น log4j ต่ำกว่าเกณฑ์การบันทึก GString จะไม่ถูกแปลงเลย
ataylor

1
สิ่งที่ขาดหายไปจากการทดสอบคือ StringBuilder ที่มีความจุที่คำนวณได้ เหตุผลก็คือ foo + bar + baz จะทำให้เกิดการขยายบัฟเฟอร์หนึ่งหรือสองครั้งซึ่งจะเพิ่มเวลา
Ruralcoder

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

ไม่แน่ใจว่าทำไมคำตอบข้างต้นจึงต้องเข้าสู่เกณฑ์มาตรฐานบัฟเฟอร์สตริงการทดสอบ ฯลฯ


3
โหวตเพิ่มความเรียบง่าย ฉันแค่ต้องเชื่อมสองสตริงเข้าด้วยกัน lol
harperville

2

ทำซ้ำคำตอบ 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
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.