문자열의 시작 문자를 검사할 때는 startsWith가 아닌 charAt을 활용하자

등록일: 2014. 08. 12

어떤 문자열이 특정 문자열로 시작하는지 검사할 때는 startsWith가 매우 유용하다. 특히 메시지 코드나 이벤트 코드 등과 같이 의미가 함축된 문자열을 분류하는 데 매우 유용하게 사용된다. 예를 들어, UPD00001, DEL00001, CRT00001처럼 명령어[UPD/DEL/CRT]와 일련번호로 구성되어 접두사에 특별한 의미가 부여된 문자열을 검사할 때는 startsWith 메서드가 불특정 위치의 문자열을 검사하는 indexOf 메서드에 비해 사용 범위가 제한돼 있긴 하지만 더욱 빠르고 불필요한 인스턴스가 생성되는 것을 방지할 수 있다는 장점이 있다. 예제 4.9.1은 이러한 startsWith 메서드와 indexOf의 실행 시간을 비교한 예다.

예제 4.9.1 startsWith와 indexOf로 접두사를 비교한 예

package com.software.string.problem;

public class StartWithExample {

    public static void main(String[] args) {
        StartWithExample example = new StartWithExample();
        example.doStartsWith();
        example.doIndexOf();
    }

    public void doStartsWith() {
        String str = "UPD00001";

        // 시작 시간
        long startTime = System.currentTimeMillis();

        // startsWith 메서드로 접두사를 100만 번 비교
        for (int i=0; i < 1000000; i++) {
            if (str.startsWith("UPD")) {
                // System.out.println("상품 정보를 수정합니다.");
            }
        }

        // 종료 시간
        long endTime = System.currentTimeMillis();

        // 시간 출력
        System.out.println("##startWith 실행시간(초.0f) : " + ( endTime - startTime )/1000.0f +"초");
    }

    public void doIndexOf() {
        String str = "UPD00001";

        // 시작 시간
        long startTime = System.currentTimeMillis();

        // indexOf로 접두사를 100만 번 비교
        for (int i=0; i < 1000000; i++) {
            if (str.indexOf("UPD") == 0) {
                // System.out.println("상품 정보를 수정합니다.");
            }
        }
        // 종료 시간
        long endTime = System.currentTimeMillis();

        // 시간 출력
        System.out.println("##indexOf 실행시간(초.0f) : " + ( endTime - startTime )/1000.0f +"초");
    }
}

실행 결과

figure4-9

위와 같이 문자열의 접두사를 비교할 때는 indexOf 메서드를 이용하는 방법보다 startsWith 메서드를 이용하는 방법이 더욱 빠르다. 하지만 예제 4.9.2와 같이 접두사가 단 한 글자로 구성된 문자라면 startsWith보다 해당 위치의 문자를 찾는 charAt을 활용하는 편이 더욱더 빠른 방법이다.

예제 4.9.1 startsWith와 charAt을 이용해 한 글자로 구성된 접두사를 비교한 예

package com.software.string.problem;

public class StartWithExample {
    public static void main(String[] args) {
        StartWithExample example = new StartWithExample();
        example.doStartsWith();
        example.doCharAt();
    }

    public void doStartsWith() {
        String str = "U00001";
        // 시작 시간
        long startTime = System.currentTimeMillis();
        // startsWith로 접두사를 100만 번 비교
        for (int i = 0; i < 1000000; i++) {
            if (str.startsWith("U")) {
                // System.out.println("상품 정보를 수정합니다.");
            }
        }
        // 종료 시간
        long endTime = System.currentTimeMillis();
        // 시간 출력
        System.out.println("##startWith 실행시간(초.0f) : " + (endTime - startTime)
                / 1000.0f + "초");
    }

    public void doCharAt() {
        String str = "U00001";
        // 시작 시간
        long startTime = System.currentTimeMillis();
        // charAt으로 접두사를 100만 번 비교
        for (int i = 0; i < 1000000; i++) {
            if (str.charAt(0) == 'U') {
                // System.out.println("상품 정보를 수정합니다.");
            }
        }
        // 종료 시간
        long endTime = System.currentTimeMillis();
        // 시간 출력
        System.out.println("##charAt 실행시간(초.0f) : " + (endTime - startTime)
                / 1000.0f + "초");
    }
}

실행결과

figure4-9-1

문제점 진단

PMD에서는 이처럼 indexOf와 startsWith, 그리고 charAt 메서드의 전체적인 속도나 효율성 비교는 할 수 없지만, indexOf의 경우 4.7에서 설명한 룰을 이용해 대응하고 있으며, startsWith와 charAt의 경우에는 한 글자로 구성된 접두사를 검사할 때는 charAt 메서드를 사용하도록 SimplifyStartsWith 룰을 이용해 권고하고 있다. 그림 4.9.1은 이 룰을 이용해 startsWith의 사용을 진단한 결과다.

figure4-9-2
그림 4.9.1 SimplifyStartsWith 룰로 startsWith 메서드를 charAt 메서드로 변경하도록 권고하는 화면

해결 방안

이 문제의 가장 적합한 해결 방안은 어떠한 목적으로 문자열의 검색을 사용하는가와 그에 따라 사용되는 메서드의 활용법을 정확히 아는 것이다. 우선 주로 사용되는 indexOf와 startsWith, charAt의 차이점을 이해하고 활용하는 것이 중요하다. 특히 물류 시스템 또는 쿠폰 시스템 같은 대량 메시지의 빠른 처리가 요구되는 시스템에서 문자열을 잘못 처리하는 것은 시스템 자원을 낭비하고 성능을 저해하는 가장 큰 주범이다. 예제 4.9.3은 indexOf, startsWith, charAt 메서드의 차이점을 비교한 예다.

예제 4.9.3 indexOf, startsWith, charAt 메서드의 차이점

package com.software.string.solution;

public class StartWithExample {
    public static void main(String[] args) {
        String strIndexOf = "0000UPD001";
        String strStartsWith = "UPD00001";
        String strcharAt = "U00001";

        // indexOf 메서드는 문자열 중 불특정 위치의 문자열을 찾을 때 유용하다.
        System.out.println("UPD는 0000UPD001 문자열의 " + strIndexOf.indexOf("UPD")
                + "번째에 위치하고 있다.");

        // startsWith는 문자열의 접두사가 되는 문자열을 찾을 때 유용하다.
        System.out.println("UPD는 UPD00001 문자열의 접두사다? "
                + strStartsWith.startsWith("UPD"));

        // charAt은 문자열에서 한 글자만 가져오기 때문에 문자열의 첫 문자를 찾을 때 유용하다.
        System.out.println("U는 UPD00001 문자열의 첫 글자다? "
                + (strcharAt.charAt(0) == 'U'));
    }
}