Nối chuỗi với Groovy


91

Cách tốt nhất (thành ngữ) để nối các Chuỗi trong Groovy là gì?

Lựa chọn 1:

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

Lựa chọn 2:

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

Tôi đã tìm ra một điểm thú vị về chủ đề này trên trang web Groovy cũ: Những điều bạn có thể làm nhưng tốt hơn là nên hoàn tác.

Như trong Java, bạn có thể nối các Chuỗi với ký hiệu "+". Nhưng Java chỉ cần một trong hai mục của biểu thức "+" là một Chuỗi, bất kể nó ở vị trí đầu tiên hay ở vị trí cuối cùng. Java sẽ sử dụng phương thức toString () trong đối tượng không phải chuỗi của biểu thức "+" của bạn. Nhưng trong Groovy, bạn chỉ nên an toàn rằng mục đầu tiên của biểu thức "+" của bạn thực hiện phương thức plus () theo đúng cách, vì Groovy sẽ tìm kiếm và sử dụng nó. Trong Groovy GDK, chỉ các lớp Number và String / StringBuffer / Character có phương thức plus () được triển khai để nối các chuỗi. Để tránh bất ngờ, hãy luôn sử dụng GStrings.

Câu trả lời:


122

Tôi luôn chọn phương pháp thứ hai (sử dụng mẫu GString), mặc dù khi có nhiều hơn một vài tham số như bạn có, tôi có xu hướng gộp chúng lại ${X}vì tôi thấy nó làm cho nó dễ đọc hơn.

Chạy một số điểm chuẩn (sử dụng mô-đun GBench tuyệt vời của Nagai Masato ) trên các phương pháp này cũng cho thấy tạo khuôn mẫu nhanh hơn các phương pháp khác:

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

Điều đó cho tôi kết quả sau trên máy của tôi:

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

Vì vậy, với khả năng đọc và tốc độ có lợi, tôi khuyên bạn nên tạo khuôn mẫu ;-)

NB: Nếu bạn thêm toString()vào cuối các phương pháp GString để làm cho loại đầu ra giống với các số liệu khác và làm cho nó trở thành một bài kiểm tra công bằng hơn StringBuilderStringBufferđánh bại các phương pháp GString về tốc độ. Tuy nhiên, vì GString có thể được sử dụng thay cho Chuỗi cho hầu hết mọi thứ (bạn chỉ cần thận trọng với các phím Bản đồ và câu lệnh SQL), nó hầu như có thể được để lại mà không cần chuyển đổi cuối cùng này

Thêm các bài kiểm tra này (như nó đã được yêu cầu trong các nhận xét)

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

Bây giờ chúng tôi nhận được kết quả:

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

Vì vậy, như bạn có thể thấy (như tôi đã nói), nó chậm hơn StringBuilder hoặc StringBuffer, nhưng vẫn nhanh hơn một chút so với việc thêm String ...

Nhưng vẫn có thể đọc được nhiều hơn.

Chỉnh sửa sau khi bình luận bởi Ruralcoder bên dưới

Đã cập nhật lên gbench mới nhất, các chuỗi lớn hơn để nối và kiểm tra với StringBuilder được khởi tạo ở kích thước tốt:

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

cho

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
Tôi không đồng ý với việc sử dụng các mẫu GString để dễ đọc, nhưng bạn nên chạy lại các bài kiểm tra có .toString()gắn vào hai bài kiểm tra GString. Cuộc chạy của tôi cho thấy rằng họ sau đó thực hiện gần như giống như String adder. Tôi đoán là bài kiểm tra bạn đã chạy không thực sự xử lý việc nối, vì vậy nó chỉ tạo một đối tượng GString và lưu trữ các tham chiếu. StringBuildervẫn là nhanh nhất, bỏ qua, nếu bạn cần một Stringlúc nào đó.
OverZ ghen tuông

1
Tôi đã bỏ lỡ nửa sau của điều đó bằng cách nào đó! Tất nhiên, ngay cả khi bạn để GString"nguyên trạng", tại một số điểm, nó phải được chuyển đổi thành true String, (thậm chí chỉ để in ra), vì vậy thời gian thực là bộ cuối cùng. Cuối cùng, tính dễ đọc của các GStringmẫu sẽ đánh bại StringBuilderkhi thời gian gần đến mức này, vì vậy đó là một cuộc tranh luận. :-)
quá hăng hái

2
@OverZ Gomez Ahhh vâng, như mọi khi, có những lời nói dối, những lời nói dối chết tiệt và những tiêu chuẩn ;-) Tính dễ đọc là chìa khóa ở đây tôi cảm thấy và vì chúng tôi đã sử dụng Groovy, chúng tôi đã tuyên bố rằng hiệu suất kim loại trần không phải là điểm cân nhắc chính của chúng tôi; -)
tim_yates

1
Vâng, một trong những lợi thế lớn của GStrings là chúng không bị chuyển đổi thành chuỗi cho đến giây phút cuối cùng. Có nghĩa là, ví dụ: nếu bạn ghi một GString với một trình ghi nhật ký như log4j dưới ngưỡng ghi nhật ký, thì GString sẽ không bao giờ được chuyển đổi.
ataylor

1
Điều còn thiếu trong bài kiểm tra là StringBuilder với dung lượng được tính toán. Lý do là foo + bar + baz sẽ gây ra một hoặc hai phần mở rộng bộ đệm, làm tăng thêm thời gian.
Ruralcoder

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

Không hoàn toàn chắc chắn tại sao câu trả lời ở trên cần đi vào điểm chuẩn, bộ đệm chuỗi, kiểm tra, v.v.


1
Ủng hộ vì sự đơn giản. Tôi chỉ cần nối hai chuỗi. lol
harperville

1

Sao chép câu trả lời tim_yates trên phần cứng hiện tại và thêm phương thức leftShift () và concat () để kiểm tra kết quả:

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

Kết quả cho thấy concat () là giải pháp nhanh hơn cho một chuỗi thuần túy, nhưng nếu bạn có thể xử lý GString ở một nơi khác, mẫu GString vẫn ở phía trước, trong khi đề cập danh dự nên chuyển đến leftShift () (toán tử bitwise) và StringBuffer () với ký tự đầu phân bổ:

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
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.