String
문자열을 생성하는 방법
final String a = "a"; // 리터럴
final String b = new String("a"); // new 연산자를 통한 객체 생성
final String c = new String("a").intern();
리터럴 방식으로 문자열을 생성하면 String Constant Pool에 문자열이 생성된다.
new 연산자를 통해 객체를 생성하면 Heap 영역에 문자열이 생성된다.
new 연산자를 통해 객체를 생성하고 intern()
을 호출하면 Heap 영역에 문자열이 생성되었다가 String pool에 같은 값을 가지는 String이 있다면 해당 Reference를 반환한다. 즉 a == c 이다.
+ 연산자
String은 불변 객체이므로 + 연산자를 사용하면 새로운 객체를 계속 만들어낸다.
"a" + "b" + "c"
를 실행하면 "ab"
객체가 만들어지고 "abc"
객체가 만들어진다.
JAVA9 부터는 String의 + 연산자의 내부 구현이 StringConcatFactory.makeConcatWithconstants()
를 사용하여
내부적으로 StringBuilder를 사용하여 문자열 결합을 수행하므로, 성능이 향상된다. 또한, 이 메소드는 컴파일 시점에 최적화된 코드를 생성하므로, 코드가 더 간결하고 가독성이 높아진다.
그렇다면 그냥 + 연산자를 막 사용해도 될까? 아래의 코드를 보자.
private static void usePlus() {
String a = "a";
long start = System.currentTimeMillis();
for (int i = 0; i < 30; i++) {
a += a;
}
long end = System.currentTimeMillis();
System.out.println("time : " + (end - start));
}
time : 1446
위의 코드에서는 반복을 30번만 해도 시간이 꽤 오래걸린다.
StringConcatFactory.makeConcatWithconstants()
는 내부적으로 StringBuilder를 생성하지만 위와 같은 상황에서
매 반복마다 StringBuilder를 새로 생성하기 때문에 성능이 저하된다.
private static void useStringBuilder() {
StringBuilder a = new StringBuilder("a");
long start = System.currentTimeMillis();
for (int i = 0; i < 30000; i++) {
a.append("a");
}
long end = System.currentTimeMillis();
System.out.println("time : " + (end - start));
}
time : 4
이러한 상황에서는 위와 같이 StringBuilder를 하나만 생성하고 append를 하도록 처리해주자.
StringBuffer
StringBuffer는 멀티 스레드 환경에서 효율적이다.
StringBuilder
StringBuilder가 JDK1.5에서 추가되었다.
StringBuilder는 단일 스레드환경에서 뛰어난 성능을 보여준다.
실제로 문자열을 다룰 때 멀티 스레드환경에서 다룰 일이 없으므로 StringBuilder를 사용하는 것이 옳다.
일급 컬렉션의 객체 생성 책임
일급 컬렉션 객체를 생성할 때, 컬렉션 객체를 생성하는 책임을 일급 컬렉션 객체 내부에 두는 것이 좋다. 이렇게 하면 일급 컬렉션 객체의 구현 세부 사항을 외부로부터 감추어 줄 수 있다. 즉, 일급 컬렉션 객체를 사용하는 코드에서는 단순히 일급 컬렉션 객체를 생성하고 사용하기만 하면 된다.
반면에, 컬렉션 객체를 생성하는 책임을 외부로 분리하는 것은 일급 컬렉션 객체의 책임 범위를 벗어나는 것이다. 이렇게 하면 일급 컬렉션 객체의 구현 세부 사항을 외부에서 알아야 하며, 일급 컬렉션 객체를 사용하는 코드에서도 컬렉션 객체를 생성하는 코드를 함께 작성해야 한다. 이는 코드의 복잡도를 증가시킬 수 있으며, 유지보수성을 저해할 수 있다.
따라서, 일급 컬렉션 객체의 컬렉션 생성 책임은 일급 컬렉션 객체 자체가 갖는 것이 가장 적절하다. 이렇게 함으로써, 일급 컬렉션 객체의 책임 범위를 명확히 하고, 코드의 가독성과 유지보수성을 향상시킬 수 있다.
그러나 일급 컬렉션 객체가 생성하는 요소가 복잡한 객체인 경우, 해당 객체를 생성하는 책임은 일반적으로 일급 컬렉션 객체의 책임 범위를 벗어난다. 이 경우, 일급 컬렉션 객체는 복잡한 객체를 생성하기 위해 필요한 정보를 수집하고, 외부 팩토리(factory)나 빌더(builder) 패턴 등의 디자인 패턴을 활용하여 요소 객체를 생성할 수 있다.
따라서, 일급 컬렉션 객체가 생성하는 요소에 따라 책임 범위가 달라지므로, 일급 컬렉션 객체의 구현을 설계할 때에는 이러한 차이점을 고려하여야 한다.