Effective Java 3/E
아이템6: 불필요한 객체 생성을 피하라
똑같은 기능의 객체를 매번 생성하기 보다는 객체 하나를 재사용 하는편이 좋을 때가 많다.
String.matches() 는 정규표현식으로 문자열 형태를 확인하기 가장 쉬운 방법이다.
public static void main(String[] args) throws Exception {
String num = "VI";
if (isRomanNumeral(num)) {
...
}
...
}
static boolean isRomanNumeral(String s) {
return s.matches("^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$");
}
}
matches() 내부에서 생성하는 정규 표현식용 Pattern 인스턴스는, 한 번 쓰고 버려져서 곧바로 가비지 컬렉션의 대상이 된다.
Pattern 은 입력받은 정규표현식에 해당하는 유한 상태 머신을 만들기 때문에 인스턴스 생성 비용이 높다.
성능 개선을 위해 정규표현식을 표현하는 Pattern 인스턴스를 초기화 과정에서 직접 캐싱해두고, 나중에 isRomanNumeral() 메서드가 호출 될 때 마다 이 인스턴스를 재사용한다.
private static final Pattern ROMAN = Pattern.compile("^M{0,4}(CM|CD|D?C{0,3})(XC|XL|L?X{0,3})(IX|IV|V?I{0,3})$");
public static void main(String[] args) throws Exception {
String num = "VI";
if (isRomanNumeral(num)) {
...
}
...
}
static boolean isRomanNumeral(String s) {
return ROMAN.matcher(s).matches();
}
...
어댑터 패턴 사용
어댑터(뷰) = 실제 작업은 뒷단 객체에 위임하고, 자신은 제2 인터페이스 역할을 해주는 객체어댑터는 뒷단 객체만 관리한다. 뒷단 객체 하나당 어댑터 하나
Map 인터페이스의 KeySet 메서드 : Map 객체안의 키를 전부 담은 Set 뷰 (어댑터)를 반환
뷰 객체를 여러개 만들거라 생각할 수 있지만, 사실은 매번 같은 인스턴스를 반환한다.
@DisplayName("keyset은 같은 Map을 바라본다")
@Test
void keyset(){
Map<String, Object> javabom = new HashMap<>();
javabom.put("Javabom", "Hello");
Set<String> javabomSet1 = javabom.keySet();
Set<String> javabomSet2 = javabom.keySet();
assertThat(javabomSet1).isSameAs(javabomSet2);
}
오토박싱으로 생성된 객체
오토박싱은 프로그래머가 기본 타입과 박싱된 기본 타입을 섞어 쓸 때 자동으로 상호 변환해주는 기술이다.
오토박싱은 기본 타입과 그에 대응하는 박싱된 기본 타입의 구분을 흐려주지만, 완전히 없애주는 것은 아니다.
의미상으로는 별다를 것 없지만 성능에서는 그렇지 않다. 아래 코드를 한번 보자.
private static long sum(){
Long sum = 0L;
for(long i = 0; i <= Integer.MAX_VALUE; i++){
sum += i;
}
return sum;
}
이 코드는 올바른 값을 내며 정상동작하기는 한다. 그러나 굉장히 느리게 동작한다.
sum변수를 Long으로 선언하여, long타입인 i가 더해질 때 마다 long 타입이 Long객체로 오토박싱이 일어난다.
그렇게 반복적으로 오토박싱이 일어나면서 성능이 저하된다.
이 코드를 단순히 sum을 long타입으로 선언하는 것만으로도 성능이 월등히 향상되는 것을 확인할 수 있다.
따라서 박싱된 기본 타입보다는 기본 타입을 사용하고, 의도치 않은 오토박싱이 숨어들지 않도록 주의하자.
이 내용들을 보고 "객체 생성은 비싸니 피해야 한다."라고 오해하지 말자. 특히 요즘 JVM에서는 별다른 일을 하지 않는 작은 객체를 생성하고 회수히는 일이 크게 부담되지 않는다.
프로그램의 명확성, 간결성, 기능을 위해서 객체를 추가로 생성하는 것이라면 일반적으로 좋은 일이다.
아주 무거운 객체가 아닌 다음에야 단순히 객체 생성을 피하고자 객체 풀을 만들지 말자.