Java에서 문자열을 연결할 때 메모리에 몇 개의 문자열이 작성됩니까?


17

Java에서 불변 문자열에 대해 질문 받았습니다. 나는 많은 "a"를 문자열로 연결하는 함수를 작성하는 일을 맡았다.

내가 쓴 것 :

public String foo(int n) {
    String s = "";
    for (int i = 0; i < n; i++) {
        s = s + "a"
    }
    return s;
}

그런 다음 가비지 수집이 발생하지 않는다고 가정 할 때이 프로그램에서 생성 할 문자열 수를 묻습니다. n = 3에 대한 나의 생각은

  1. ""
  2. "ㅏ"
  3. "ㅏ"
  4. "aa"
  5. "ㅏ"
  6. "aaa"
  7. "ㅏ"

루프의 각 반복마다 본질적으로 2 개의 문자열이 작성됩니다. 그러나 대답은 n 2 입니다. 이 함수에 의해 메모리에 어떤 문자열이 생성되며 왜 그렇게됩니까?


15
이 직업을 제공 받았다면 도망
가세요.

여러 가지 이유로 @mattnz (그리고 작성된 코드 때문이 아닙니다).

3
JIT가 루프를 최적화하지 않는 한 O (n ^ 2) 런타임이 필요하지만 n ^ 2 문자열을 작성하지는 않습니다.
user2357112는 Monica

답변:


26

그런 다음 가비지 수집이 발생하지 않는다고 가정 할 때이 프로그램에서 생성 할 문자열 수를 묻습니다. n = 3에 대한 내 생각은 (7)

문자열 1 ( "")과 2 ( "a")는 프로그램의 상수이며, 이것들은 사물의 일부로 생성되지 않지만 컴파일러가 알고있는 상수이기 때문에 '통합'됩니다. 이에 대한 자세한 내용 은 Wikipedia의 String interning 에서 확인하십시오.

또한 "a"문자열 # 2 와 동일 하므로 문자열 5와 7을 카운트에서 제거합니다 . 문자열 # 3, # 4 및 # 6이 남습니다. 답은 코드를 사용하여 "n = 3에 대해 3 개의 문자열이 생성됩니다"입니다.

n = 3 일 때 n이 3이되기 때문에 n 2 의 개수 는 분명히 잘못되었습니다. 최악의 경우는 7 일뿐입니다. 비 인턴 문자열이 정확하면 답 2n + 1 이어야합니다 .

그래서 어떻게 해야 합니까?

문자열은 변경할 수 없기 때문에 새로운 객체를 만들지 않고 변경할 수있는 변경 가능한 것을 원합니다. 이것이 StringBuilder 입니다.

가장 먼저 볼 것은 생성자입니다. 이 경우 우리는 문자열의 길이를 알고, StringBuilder(int capacity) 필요한만큼 정확하게 할당 하는 생성자 가 있습니다.

다음 "a"으로 String 일 필요는 없지만 character 일 수 있습니다 'a'. 이 호출 할 때 밀어주는 약간의 성능이 append(String)대를 append(char)- (가)와 append(String),이 방법은 문자열이 얼마나 오래 알아 내고 그 몇 가지 작업을 할 필요가있다. 반면에 char항상 정확히 한 문자 길이입니다.

코드 차이는 StringBuilder.append (String) vs StringBuilder.append (char) 에서 확인할 수 있습니다 . 아니고 무엇인가를 할 너무 관심, 그러나 당신이 고용주에게 좋은 인상을하려는 경우는 최선의 방법을 사용하는 것이 가장 좋습니다.

그래서 이것을 정리하면 어떻게 보일까요?

public String foo(int n) {
    StringBuilder sb = new StringBuilder(n);
    for (int i = 0; i < n; i++) {
        sb.append('a');
    }
    return sb.toString();
}

하나의 StringBuilder와 하나의 문자열이 작성되었습니다. 추가 문자열을 삽입 할 필요가 없습니다.


Eclipse에서 다른 간단한 프로그램을 작성하십시오. pmd를 설치 하고 작성한 코드에서 실행하십시오. 그것이 무엇에 대해 불평하고 그 문제를 해결하십시오. 그것은 루프 +와 문자열의 수정을 발견 한 것입니다, 당신은 StringBuilder에 해당를 변경 한 경우, 그것은 것입니다 아마 초기 용량을 찾았지만 확실히 차이를 잡을 것입니다 .append("a").append('a')


9

각 반복 String에서 +연산자 가 새 항목 을 작성 하고에 지정합니다 s. 돌아온 후, 마지막을 제외한 모든 것은 가비지 수집됩니다.

문자열 상수 좋아 ""하고 "a",이 때마다 생성되지 않습니다 인턴 문자열 . 문자열은 변경할 수 없으므로 자유롭게 공유 할 수 있습니다. 이것은 문자열 상수에 발생합니다.

문자열을 효율적으로 연결하려면을 사용하십시오 StringBuilder.


인터뷰에서 사람들은 실제로 리터럴이 있는지 여부에 대해 토론하고 리터럴이 매번 생성되도록 결정했습니다. 그러나 이것은 더 의미가 있습니다.
ahalbert

6
언어가하는 일을 어떻게 "토론"하는가? 반드시 사양을 읽고 확실하게 알지 못하거나 정의되지 않았으므로 정답이 없다 .....
mattnz

@mattnz 구현 세부 사항에 관해서도 사용중인 컴파일러 / 런타임이 무엇인지 아는 것이 흥미로울 수 있습니다. 이것은 특히 성능에 적용됩니다.
svick

1
@ svick : 가정을 통해 많은 것을 얻을 수 있으며 컴파일러가 업그레이드되고 최적화가 변경되었습니다. 정의 된 동작이 아닌 지정되지 않은 동작에 의존하기 때문에 동작이 변경되어 버그가 발생합니다. 그들이 최적화에 대해하는 말을 알고 있습니다. a) 전문가에게 맡기고 b) 아직 전문가가 아닙니다. :) 의존이 성능 기반이지만 언어 사양에 의존하는 경우 성능이 저하됩니다. 여러 번 지정되지 않았거나 컴파일러 특정 동작에 의존하는 코드가 예기치 않은 방식 (대부분 C 및 C ++)으로 중단되는 것을 보았습니다.
mattnz

@mattnz 그렇다면 성과와 관련된 결정을 어떻게 내릴 수 있습니까? 일반적으로 사양 / 문서에서 얻을 수있는 최선의 방법은 복잡성이 크지 만 충분하지 않습니다. 어쨌든 성능은 항상 구현에 달려 있으므로 성능과 관련하여 구현 세부 사항에 의존하는 것이 좋습니다.
svick

4

MichaelT가 그의 답변에서 설명 하듯이 코드는 O (n) 문자열을 할당합니다. 그러나 또한 O (n 2 ) 바이트의 메모리를 할당 하고 O (n 2 ) 시간에 실행됩니다 .

할당 하는 문자열의 길이가 0, 1, 2,…, n-1, n이므로 (n 2 + n) / 2 = O (n 2 ) 이므로 O (n 2 ) 바이트를 할당 합니다.

i 번째 문자열을 할당하려면 길이가 i-1 인 (i-1) 번째 문자열을 복사해야하므로 시간도 O (n 2 )입니다. 즉, 할당 된 각 바이트를 복사해야하며 이는 O (n 2 ) 시간이 걸립니다.

아마도 이것이 면접관의 의미일까요?


여기 처럼 방정식이 (n ^ 2 + n) / 2이어야 합니까?
HeyJude
당사 사이트를 사용함과 동시에 당사의 쿠키 정책개인정보 보호정책을 읽고 이해하였음을 인정하는 것으로 간주합니다.
Licensed under cc by-sa 3.0 with attribution required.