티스토리 뷰

Item63에서는 문자열 연결은 느리니 주의하라 말한다

String의 작동 방식을 이해하기란 쉽지 않고 제대로 이해하려면 로우 레벨까지 파봐야 하니 

이 글에서는 깊게 다루지는 않고 수박 겉핥기 정도로만 다뤄보려 한다

모든 내용은 이 글을 참고한다, 본 글은 간략하게 정리하기 위함이고 제대로 알아보려면 첨부한 링크를 보는 걸 추천한다

 

문자열 연산을 알아보기 전에 선행해서 생각해볼 점으로 가독성과 성능이 있다

기본 루프를 사용할 것이냐 스트림을 사용할 것이냐 처럼

가독성과 성능 둘 사이의 트레이드오프를 이해하고 상황에 맞는 방식을 사용해야 한다

 

자바에서 문자열 연결 시에 사용할 수 있는 방법은 + 연산자 외에도 꽤 많다

연결해야 하는 인자가 적다면 아무래도 + 연산자가 제일 가독성이 좋을 테고

StringBuilder, StringBuffer는 장황하니 보기에 유쾌하진 않을 것이다

 

+ 연산자는 a + b 형태에서 a와 b를 읽고 메모리에 복사하여 새로운 String 객체를 만들어야 하므로 제일 느리다

JDK 1.5 이후로는 컴파일러가 알아서 최적화를 해줘서 동일한 라인에 있다면 StringBuilder를 이용한 최적화가 들어간다

+ 연산자는 반복문 내에서 최적화가 이뤄질 때 매번 새로운 StringBuilder가 만들어지므로

반드시 명시적으로 StringBuilder를 만들고 사용해야 한다

 

StringBuffer는 기본 동기화를 제공하므로 Builder보다 느리지만 멀티 스레드 환경에서 안전한 API라 할 수 있다

StringBuilder는 메모리 단위의 복사, 새로운 String 인스턴스 생성의 오버헤드를 줄이고자

buffer를 이용해 문자를 다루는데 동기화를 하지 않고 이를 사용하는 클라이언트에게 책임을 넘긴다

따라서 성능을 고려한다면 단연 StringBuilder를 사용해야 한다

멀티 스레드 환경이라면 StringBuffer를 사용하던지 따로 동기화를 시켜주던지 해야 한다

 

여기까지는 이론적인 내용이고 실제 작동 결과를 살펴보면

StringBuilder가 이론대로 성능이 제일 좋긴 하지만 + 연산자, StringBuffer에 비해 압도적인 성능 차이는 아니다

따라서 프로젝트 코드에 StringBuffer로 되어있다고 개거품 물고 바꿀 정도는 아니라는 얘기이고

그 보다는 멀티 스레드 환경인지 고민하는 것이 먼저겠다

또한 concat(), format()은 성능 관점에서는 절대 사용하면 안 될 메서드다

(좌) 두 개의 문자 연결, (우) 문자열 리스트 연결

 

잠깐 딴 길로 새서 내가 경험해본 언어 중 Javascript, Kotlin, Rust에서는 문자열 출력, 연결에 

String Template Literals을 사용하곤 하는데 다음과 같은 형태를 의미한다

자바에서는 아쉽게도 Template Literals을 직접 사용할 수는 없고 String.format()을 이용해야 한다

JDK 15 이후로는 formatted() 메서드를 사용해 %s, %d 등의 키워드에 매핑시켜야 하는 불편함이 있다

String h = "hello";
String w = "world";

// 요건 불가
System.out.println("{h}, {w}!");

// JDK 15 이전
System.out.println(String.format("%s, %s!", h, w);

// JDK 15 이후
System.out.println("%s, %s!".formatted(h, w));

 

개인적인 생각으로는 자바의 방식보다는 다른 언어들의 표현 방식이 훨씬 간결해 보이고 한눈에 쏙 들어오니 좋은 것 같다

매핑해야 할 인자가 여러 개라면 + 연산자나 StringBuilder, StringBuffer 보다는 format(), formatted() 메서드를

사용하는 것이 가독성이 더 좋은 것 같아 사용했었는데 다음 사진을 보고 사용을 자제해야겠다는 생각이 들었다

그럼에도, 글 저자도 말했듯이 성능이 구리다고 무조건 쓰지 않아야 하는 것은 아니다

가독성은 주관적이긴 하지만 format()이 더 이뻐 보이고 수행해야 하는 연산이 많지 않다면

그로 인한 지연이 얼마나 될 것인가를 고려해가면서 사용하면 된다

문자열에 반복 작업을 처리해야 하는 경우엔 당연히 StringBuilder를 써야겠지만 초기화 작업 같은 경우에는 막 써도 된다

 

https://cowtowncoder.medium.com/measuring-performance-of-java-string-format-or-lack-thereof-2e1c6a13362c#:~:text=and%20you%20would%20get%20results%20that%20look%20something%20like%20what%20I%20see%20on%20my%20desktop%20(Mac%20Mini%20(2018)%203.2Ghz%206%2Dcore%20Intel%20Core%20i7)%3A

 

 

JDK 17에서는 String.format()이 기존보다 3배 정도 빨라졌다고 하니 대량 작업에 사용할 순 없지만 부담은 좀 줄어들 것 같다

자바에서도 String Templates를 지원하고자 feature preview로 2021년에 올라왔으니 JDK 23 정도에 기대해볼 법도?

 

JEP draft: String Templates (Preview)

JEP draft: String Templates (Preview) OwnerJim LaskeyTypeFeatureScopeSEStatusSubmittedComponentspecification / languageDiscussionjdk dash dev at openjdk dot java dot netEffortMDurationMReviewed byAlex Buckley, Maurizio CimadamoreCreated2021/09/17 13:41

openjdk.org

 


결국 JDK 21에 preview로 들어간다

간단하게만 훑어봤는데 String Interpolation 방식은 SQL injection과 같은

삽입 공격이 일어날 위험이 있어 대안으로 별도의 Processor와 Template을 활용한다고 한다

다만 Processor가 의존하는 클래스에 중대한 재작성이 필요해 완성되지는 않았고 preview로 들어간다고 한다

 

JEP 430: String Templates (Preview)

JEP 430: String Templates (Preview) OwnerJim LaskeyTypeFeatureScopeSEStatusCompletedRelease21Componentspecification / languageDiscussionamber dash dev at openjdk dot orgEffortMDurationMReviewed byAlex Buckley, Brian Goetz, Maurizio CimadamoreEndorsed b

openjdk.org

 

댓글
링크
글 보관함
«   2024/12   »
1 2 3 4 5 6 7
8 9 10 11 12 13 14
15 16 17 18 19 20 21
22 23 24 25 26 27 28
29 30 31
Total
Today
Yesterday