여러 프로그램을 만들다보면, 분명 문자열에 대한 유효성 검사를 할 일이 많을 것이다.
ex) 숫자로만 이루어져있는가? 특정한 문자만으로 이루어져있는가 등등
public class Id {
private static final String ID_REGEX = "^[a-zA-Z]+$";
private static final String INVALID_ID_EXCEPTION_MESSAGE = "아이디는 영문만 가능합니다.";
public Id(final String value) {
if (!value.matches(ID_REGEX)) {
throw new IllegalArgumentException(INVALID_ID_EXCEPTION_MESSAGE);
}
}
}
위와 같은 방식으로 코드를 치던 어느 날, 코드 리뷰어에게 성능에 대한 피드백을 받았다.
String의 matches 메소드는 내부적으로 Pattern의 matches 메소드를 실행시킨다.인자는 정규표현식과 String 자기 자신 객체이다.
Pattern의 matches는 compile 메소드에 regex를 넘겨
우리가 지정한 정규표현식을 컴파일 하며 Pattern 객체를 생성한다.
정규표현식에 대한 정보를 가진 Pattern 객체의 matcher 메소드에 검사하려는 값을 넘겨 Matcher를 생성한다.
Matcher의 matches 메소드를 통해 검사대상이 정규표현식에 맞는지를 반환한다.
위와 같이 그냥 String의 matches 메소드를 통해 정규표현식 검사를 하면 참 편한다.
위의 코드가 실행되면
Pattern은 입력받은 정규표현식에 대해 유한 상태 기계(finite state machine)을 만드는데,
이는 인스턴스의 생성 비용이 높다. (Pattern 객체를 만드는 것은 비용이 높다는 것이다)
new Name("Chris"); //-> Pattern 객체 생성
new Name("Roma"); //-> Pattern 객체 생성
new Name("Alex"); //-> Pattern 객체 생성
//생성자 내부의 String의 matches가 실행될 때마다 Pattern 객체가 생성된다.
거기에 더해 Name 객체가 생성될 때마다 String의 matches 메소드가 실행되고
정규식을 반복적으로 컴파일하며 Pattern 객체가 반복적으로 생성된다. 인스턴스 생성 비용이 높은 Pattern 객체가 말이다.
그렇다면 어떻게 해야할까?
이렇게 하면 많은 Pattern 객체가 생성되고 회수되는 낭비를 없앨 수 있다.
실제로 조슈아 블로그 형님께서는 이 방식으로 속도를 6.5배 증가시키셨다는 글이 Effective Java에 나와있다.
사실 성능적인 피드백을 받아본 적이 처음이다. 객체 지향에 대한 이해도 상당히 중요하지만
결국 서비스를 사용하는 사용자의 입장에서 속도(성능)이 떨어지면 당연히 안쓰겠구나.. 라는 것을 다시 한번 느낄 수 있었다.
참고자료