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에서는 Number 및 String / StringBuffer / Character 클래스에만 문자열을 연결하기 위해 구현 된 plus () 메서드가 있습니다. 놀라움을 피하려면 항상 GStrings를 사용하세요.

답변:


122

저는 항상 두 번째 방법 (GString 템플릿 사용)을 사용하지만, 여러분과 같은 매개 변수가 두 개 이상있을 때 ${X}더 읽기 쉽게 만들 수 있다는 것을 알기 때문에 그것들을 감싸는 경향이 있습니다.

이러한 방법에 대해 일부 벤치 마크 ( Nagai Masato 의 우수한 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 메소드 끝에 추가 하여 출력 유형을 다른 측정 항목과 동일하게 만들고 더 공정한 테스트로 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보다 느리지 만 여전히 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

3
가독성을 위해 GString 템플릿을 사용하는 데 동의하지 않지만 .toString()두 GString 테스트에 추가 된 테스트를 다시 실행해야 합니다. 내 실행에 따르면 그들은 String adder. 내 생각에는 실행 한 테스트가 실제로 연결을 처리하지 않으므로 GString 개체를 만들고 참조를 저장하는 것입니다. 어느 시점에서 StringBuilder필요한 경우 여전히 가장 빠릅니다 String.
OverZealous

1
어떻게 든 그 후반부를 놓쳤습니다! 물론, GString"있는 그대로 " 그대로 두더라도 어떤 시점에서 true로 변환해야하므로 String(단지 출력하기 위해도) 실제 타이밍이 마지막 세트입니다. 결국 GString템플릿 의 가독성은 StringBuilder타이밍이 이렇게 가까울 때보 다 낫습니다. :-)
OverZealous

2
@OverZealous Ahhh 예, 항상 그렇듯이 거짓말, 저주받은 거짓말 및 벤치 마크가 있습니다 ;-) 여기에서 가독성이 핵심이며 이미 Groovy를 사용하고 있기 때문에 베어 메탈 성능이 우리의 주요 고려 사항이 아니라고 말했습니다. -)
tim_yates 2012

1
예, GStrings의 가장 큰 장점 중 하나는 마지막 순간까지 문자열로 변환되지 않는다는 것입니다. 예를 들어 log4j와 같은 로거를 사용하여 로깅 임계 값 아래로 GString을 로깅하면 GString이 전혀 변환되지 않습니다.
ataylor

1
테스트에서 누락 된 것은 계산 된 용량이있는 StringBuilder입니다. 그 이유는 foo + bar + baz가 시간을 추가하는 하나 또는 두 개의 버퍼 확장을 유발하기 때문입니다.
ruralcoder

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

위의 답변이 벤치 마크, 문자열 버퍼, 테스트 등에 들어가야하는 이유를 잘 모르겠습니다.


2
단순성을 위해 찬성합니다. 두 문자열을 연결하면됩니다. lol
harperville

2

현재 하드웨어에서 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
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.