@Test
public void 문자열_비교() {
// when
String hello = "hello";
String hello2 = "hello";
//TODO 이 방법은 권장하지 않습니다.
String hello3 = new String("hello");
//then
assertThat(hello == hello3).isFalse();
assertThat(hello.equals(hello2)).isTrue();
assertThat(hello == hello2).isTrue();
assertThat(hello == hello2).isTrue();
}
동일한 기능을 함에도 여러번 생성하는 경우들이 있는데 하나씩 알아보자
1️⃣ 문자열
📌 문자열 생성 방법
String hello1 = "hello";
String hello2 = new String("hello"); //이 방법은 권장하지 않습니다.
😮💨 권장하지 않는 방법을 사용했을 때 문제점
1. String 생성자로 들어가는 "hello" 자체가 이 생성자로 만들어내려는 String과 기능적으로 완전히 똑같다.
2. Java에서 문자열 리터럴을 저장하는 독립된 영역인 String Constant Pool를 이용한 재사용을 하지 못한다.
💬 2번 문제를 조금 더 보자
String은 불변 객체로 String Constant Pool를 통해서 성능, 동기화, 캐싱, 보안의 이점을 얻고 있는데 new로 생성하면 heap 영역에 저장되기 때문에 활용하지 못한다.
- 궁금하면 여기를 참고하세요
테스트
@Test
public void 문자열_비교() {
// when
String hello = "hello";
String hello2 = "hello";
//TODO 이 방법은 권장하지 않습니다.
String hello3 = new String("hello");
//then
assertThat(hello == hello3).isFalse();
assertThat(hello.equals(hello2)).isTrue();
assertThat(hello == hello2).isTrue();
assertThat(hello == hello2).isTrue();
}
2️⃣ 정규표현식
정규표현식용 Pattern 인스턴스는 생성 비용이 높고 한 번 쓰고 버려져서 곧바로 가비지 컬렉션 대상이 된다.
public boolean isPhoneNumber(String phoneNumber) {
return phoneNumber.matches("^\\d{3}-\\d{3,4}-\\d{4}$");
}
정적 초기화를 통해서 직접 생성해 캐싱해두면 재사용시 성능의 이점을 얻을 수 있다.
- static final 필드로 추출하면서 이름도 얻을 수 있게 때문에 무슨 정규표현식인지 이해하기 쉽다.
public class Main {
private static final Pattern PHONE_NUMBER_PATTERN =
Pattern.compile("^\\d{3}-\\d{3,4}-\\d{4}$");
public boolean isPhoneNumberCache(String phoneNumber) {
return PHONE_NUMBER_PATTERN.matcher(phoneNumber).matches();
}
public static void main(String[] args) {
Main main = new Main();
List<String> phoneNumbers = IntStream.rangeClosed(1, 1000)
.mapToObj(s -> "010-1234-5678")
.collect(Collectors.toList());
long start = System.nanoTime();
List<String> result = phoneNumbers.stream()
.filter(main::isPhoneNumberCache)
.collect(Collectors.toList());
long end = System.nanoTime();
System.out.println(end - start);
}
}
😛 정규 표현식을 matches말고 split과 replace에서도 사용된다.
String.split(String regex)
String의 split 메서드 주석을 보면 아래 2가지 중에서 포함되면 내부적인 최적화를 얻을 수 있다.
- 길이가 한 글자인 문자열로, 이 문자열이 정규 표현식에서 메타 문자(meta characters)인 ".$|()[{^?*+\" 중 하나가 아닌 경우
- 길이가 두 글자인 문자열로, 첫 번째 글자가 백슬래시(\\)이고, 두 번째 글자가 아스키(ASCII) 숫자나 알파벳 문자가 아닌 경우
위에 경우가 아니면 정규 표현식으로 최적화 하면 된다.
Pattern.compile(regex).split(str)
❗️주의할점
split 최적화가 가능한 경우 Pattern 객체를 캐시처리하는 것보다 빠르게 나왔다.
1️⃣ split 최적화 - 2500000 ~ 2800000
public static void main(String[] args) {
long start = System.nanoTime();
for (int j = 0; j < 1000; j++) {
String name = "야호,크크,메롱,웃겨?";
name.split(",");
}
System.out.println(System.nanoTime() - start); // 2587458
}
2️⃣ Pattern 객체 캐시해서 사용- 4300000 ~ 4800000
public class SplitCache {
private static final Pattern SPLIT_PATTERN = Pattern.compile(",");
public static void main(String[] args) {
long start = System.nanoTime();
for (int j = 0; j < 1000; j++) {
String name = "야호,크크,메롱,웃겨?";
SPLIT_PATTERN.split(name);
}
System.out.println(System.nanoTime() - start); //4816792
}
}
String.replace(String regex, String replacement)
Pattern.compile(regex).matcher(str).replaceAll(repl)
3️⃣ 오토박싱(auto boxing)
오토박싱: 프리미티브 타입을 wrapper 타입으로 변경시 발생
언박싱: wrapper 타입을 프리미티브 타입으로 변환하는 과정
❗️오토박싱의 문제점
오토박싱, 언 박싱 을 런타임에 JVM 이 자동으로 처리하는데 불필요하게 오토박싱이 일어나는 경우 Wrapper 클래스의 인스턴스가 계속 만들어지게 된다.
public class Sum {
private static long sum() {
Long sum = 0L; // long이 아니라 Long이다.
for (long i = 0; i <= Integer.MAX_VALUE; i++)
sum += i; // 오토박싱 발생
return sum;
}
public static void main(String[] args) {
long start = System.nanoTime();
long x = sum();
long end = System.nanoTime();
System.out.println((end - start) / 1_000_000. + " ms.");
System.out.println(x);
}
}
오토박싱이 일어나지 않게 수정하면 성능이 개선된다.
Long sum = 0L; ➡️ long sum = 0L;
📚 Reference
[Java] String이 불변 객체인 이유는 무엇일까?
String이 불변 객체인 이유 String 객체가 불변 객체라는 것은 다들 알고 있을 것입니다. 불변 개체는 완전히 생성된 후에도 내부 상태가 일정하게 유지되는 개체입니다. 즉, 객체가 변수에 할당되
devlog-wjdrbs96.tistory.com
[Java] 많이 헷갈려하는 String constant pool과 Runtime Constant pool, Class file constant pool
String Constant Pool과 Constant Pool 이 두 가지는 완전히 다른 개념입니다. 용어가 비슷한 형태이기 때문에 이 두 가지를 혼용하여 헷갈리는 경우가 많습니다만, 저장되는 위치부터 저장하는 데이터의
deveric.tistory.com
이펙티브 자바 아이템 6 - 불필요한 객체 생성을 피하라 - 완벽 공략
아이템 6 - 불필요한 객체 생성을 피하라 - 완벽 공략 이 글은 백기선 님의 이펙티브 자바 강의와 이펙티브 자바 3 / E 편을 참고하여 작성하였습니다. Deprecation 개발을 하다보면 해당 api 가 바뀌어
pro-dev.tistory.com
'책 > 이펙티브 자바' 카테고리의 다른 글
item3 - private 생성자나 열거 타입으로 싱글턴임을 보증하라 (0) | 2024.02.18 |
---|---|
item2-생성자에 매개변수가 많다면 빌더를 고려하자 (0) | 2024.02.16 |
Item1 - 생성자 대신 정적 팩터리 메서드를 고려하라 (0) | 2024.02.15 |