JDK9 이후로 문자열 관련 알게된점

깡통·2023년 12월 22일
0

20번문제 궁금했던점 1. StringBuilder() 에서 굳이 생성자 파라미터로 capacity를 지정하는이유?

먼저 기본생성자로 StringBuilder를 생성하고, append()메서드로 무한정 문자열을 추가한다면 어떤일이 발생하는지 알아보자

기본 capacity 사이즈는 16으로 설정된다.

부모클래스인 AbstractStringBuilder에서는 문자열 압축(compact String)여부를 확인하고 value(StringBuilder의 내부버퍼 크기)와 coder(문자열 저장방식)값을 세팅한다.

중요한건 append()메서드로 문자열을 추가하는데 기본사이즈인 16을초과했을때 어떤일이 발생하는지 알아보는게 핵심이다.

StringBuilder의 append는 문자열을 받아 super의 append()메서드를 호출한다

AbstractStringBuilder의 append()에서는 문자열 null 체크후에 문자열의 길이를 가지고 ensureCapacityInternal메서드를 호출한다.

핵심 메서드이다.
value는 현재 StringBuilder의 버퍼의 길이, coder는 유니코드 저장방식이다.
이를 이용해 현재 문자열의 용량을 계산하는것이다
if문에서, append()로 현재 문자열 용량보다 큰 문자열이 들어온다면 새로운 버퍼를 생성해 기존 버퍼를 복사하고 용량을 2배로 늘리는 복사과정이 발생한다.

20번문제 궁금했던점 2.repeat(), join(), generate(), format(), char[] 배열로 해결하는 방법 성능 측정하기

@DisplayNameGeneration(DisplayNameGenerator.ReplaceUnderscores.class)
class StringTest {

    private static final int REPEAT_COUNT = 90000000;

    @Test
    void StringBuilder_로_테스트하기() {
        final long startTime = System.currentTimeMillis();
        final StringBuilder sb = new StringBuilder();

        for (int i = 0; i < REPEAT_COUNT; i++) {
            sb.append("hello");
        }

        final long endTime = System.currentTimeMillis() - startTime;
        System.out.println(endTime); // 2299ms
    }

    @Test
    void repeat으로_테스트하기() {
        final long startTime = System.currentTimeMillis();
        final String result = "hello".repeat(REPEAT_COUNT);
        final long endTime = System.currentTimeMillis() - startTime;
        System.out.println(endTime); // 659ms
    }

    @Test
    void join으로_테스트하기() {
        final long startTime = System.currentTimeMillis();
        final String result = String.join("", Collections.nCopies(REPEAT_COUNT, "hello"));
        final long endTime = System.currentTimeMillis() - startTime;
        System.out.println(endTime); // 6284ms
    }

    @Test
    void generate로_테스트하기() {
        final long startTime = System.currentTimeMillis();
        final String result = Stream.generate(() -> "hello")
            .limit(REPEAT_COUNT)
            .collect(Collectors.joining());
        final long endTime = System.currentTimeMillis() - startTime;
        System.out.println(endTime); // 2221ms
    }
}

문자열 반복 이어 붙히기 간단한 테스트이고 9천만번으로 돌려봤어요. 적게 시행했다가 점점 올려본건데 ms만 변했을뿐 순위의 변동은 없었습니다.
repeat >>> generate >= StringBuilder >>> join 순이네요.

join은 왜 느린가 찾아보니, 얘도 StringBuilder와 비슷하게 기본 사이즈를 넘어가면 copy가 발생해요.
단지 StringBuilder는 기본크기가 16으로 설정되고, 얘는 8로 설정되는데 이것 때문인가 싶기도 한데 정확히는 모르겠네요

repeat은 "hello"라는 문자열을 반복하는경우 반복회수+문자열의 길이를 이용해 한번에 크기에 맞는 배열을 생성해서 연산해 매우 효율적입니다

join은 문자열을 연결할때마다 새로운 문자열을 생성하고, 메모리 할당-해제가 반복됩니다(gc발생)

루프 회수가 5,000,000 이 넘어가는순간부터 유의미한 차이가 벌어지기 떄문에 (repeat 43ms vs join 700ms) 매우 많은 회수로 반복루프문에서 사용할게 아니라면 사실 큰 의미가 없을수도 있어요

결론 : 문자열 반복은 repeat이 효율적이다

21 trim()은 유니코드를 인식하지 못한다

jdk11부터는 strip()이 도입되었는데 이는 trim()이 인식하지못하는 strip()까지 사용할수 있게 한다.
이외에도 문자열의 앞부분 여백만 제거하는 메서드stripLeading()과 뒤여백만 제거하는 Trailing()도 추가되었다고 한다
앞으론 strip()을 자주 써보도록 하자

22. 문자열 변환시 transform()이라는 메서드도 존재함을 알게되었음

		String result = Stream.of("hello")
			.map(s -> s + " world")
			.findFirst()
			.get();



String transform = "hello".transform(s -> s + " world");

위 코드를 아래와같이 변경할수있음

26. 두 원시타입의 연산에서 오버플로가 발생해 -2와 같은 엉뚱한 값이 나오는것 대신, 예외를 던져주는 Math클래스에 API 추가되었음

int z = Math.addExact(x,y); // exception 발생 

0개의 댓글

관련 채용 정보