String str = "Java";
str += " Programming";
예를들어 위와 같이 코드를 작성하면, 기존의 "Java"가 변하는 것이 아니다.
메모리(Heap)상에 "Java Programming"이라는 새로운 객체가 생성되고, 변수 str이 새로운 주소값을 가리키게 되는 것이다.
int와 String으로 알아보는 저장방식의 차이
int 타입 변수 num의 경우String 타입 변수 str의 경우str은 바로 그 주소값을 참조하게 된다.str = "abc" 후에 str = "def"가 실행되어 str이라는 변수가 갖는 참조값이 0x11에서 0x22로 바뀐다고 하더라도 그건 str변수가 갖는 참조값이 변경된 것이지 실제 abc가 저장되어 있는 0x11 주소의 데이터가 바뀌는 것은 아니다.
new String() vs String = ""String s1 = "Cat";
String s2 = "Dog";
String s3 = new String("Cat");
String s4 = new String("Cat");
System.out.println(s1 == s2); // true
System.out.println(s1 == s3); // false
System.out.println(s3 == s4); // false
s1 & s2: String pool의 메모리를 가리키고 있다.s3: String pool이 아닌 일반 객체처럼 힙(Heap) 영역에 할당된 메모리를 가리키고 있다.s1 == s2 :모두 큰 따옴표를 사용해 선언되었으므로 String Pool안에 "Cat"이 할당된 같은 메모리를 가리키게 된다.s1 == s3: s1은 String pool의 메모리를, s3는 Heap영역에 동적으로 할당된 메모리를 가리키기 때문에 서로 다른 메모리를 가리켜 결과로 false를 나타낸다.s3 == s4: 모두 String pol이 아닌 Heap 영역에 동적으로 할당된 메모리를 가리키지만, 각자 본인의 메모리를 별도로 할당받기 때문에 결과는 false를 나타낸다.hashCode란?hashCode는 객체의 고유한 정수값을 반환하는 메서드이다.hashCode는 해시 테이블 또는 해시 기반 자료구조(HashMap, HashSet)에서 객체를 빠르게 찾거나 구별하는데 사용된다.hashCode는 객체를 빠르게 비교할 수 있도록 해주는 고유한 숫자를 반환하며, 이 숫자를 기반으로 객체의 저장 위치를 결정할 수 있다.String 객체에서 hashCode 캐싱String 클래스의 hashCode() 메서드를 호출하면 문자열의 해시 값을 계산해 반환한다. 중요한 점은 String 객체는 불변(immutable) 객체이므로, 한 번 계산된 hashCode 값은 절대 반환하지 않는다. 이 특성을 활용해 자바는 String의 hashCode값을 한 번만 계산하고, 그 값을 캐시하여 재사용한다. 이를 통해 성능을 개선할 수 있다.hashCode() 메서드 구현String 클래스의 hashCode()메서드는 문자열의 각 문자를 순차적으로 읽어가면서 그 값을 기반으로 문자열에 대한 고유한 해시 값을 생성한다.public int hashCode() {
int h = 0;
for (int i = 0; i < vaule.length; i++) {
h = 31 * h + value[i]; // 'value'는 문자열을 저장하는 char 배열
}
return h;
}hashCode 캐싱 과정String은 불변 객체이므로 hashCode 값은 객체가 처음 생성될 때 한 번만 계산되고, 그 이후에는 해시 값을 재계산하지 않고 캐시된 값을 사용한다.String s1 = "hello";
System.out.println(s1.hashCode()); // "hello"에 대한 해시 값 계산
System.out.println(s1.hashCode()); // 같은 해시 값 캐시에서 반환"hello"라는 문자열에 대해 hashCode를 처음 호출하면 해시 값이 계산된다. 이후 동일한 String 객체에 대해 hashCode를 호출하면 계산된 해시 값이 메모리에서 캐시되어 바로 반환된다. 이로 인해 반복적인 hashCode() 호출에 대한 성능이 향상된다.String의 hashCode 캐싱과 HashMaphashCode값의 사용 예시HashMap<String, Integer> map = new HashMap<>();
map.put("apple", 1);
map.put("banana", 2);
// 동일한 "apple" 키에 대해 'hashCode'가 이미 캐시되어 있으므로 빠르게 검색됨
System.out.println(map.get("apple")); // 1
위 코드에서는 apple이라는 문자열을 hashMap의 키로 사용한다. apple의 hashCode값은 첫 번째 호출 시 계산되고, 이후에 캐시된 값을 사용하여 빠르게 검색할 수 있다.
String 클래스는 불변(immutable) 특성을 갖고 있기 때문에 문자열을 변경할때마다 새로운 객체를 생성하게 되며, 이는 성능 저하를 초래할 수 잇다. 이에 비해 StringBuilder는 가변(mutable)한 특성을 갖고 있어, 자주문자열을 변경하거나 조작해야하는 경우 매우 효율적인 클래스다.
StringBuilder는 내부적으로 char[] 배열(혹은 byte[])을 사용하여 데이터를 저장.StringBuilder(int capacity)와 같이 생성자를 통해 크기를 지정할 수 있습니다.append() 메서드처럼 문자열을 추가할 때, StringBuilder는 새로운 문자열을 만들지 않고 기존 배열에 데이터를 추가한다.String에 비해 메모리 할당과 객체 생성 비용을 크게 줄여주어, 문자열을 반복적으로 수정할 때 효율적이다.StringBuilder는 문자열이 추가되거나 수정될 때마다 내부 배열이 꽉 차면 문자열의 크기가 두 배로 늘어나는 방식 자동으로 크기가 확장됩니다.StringBuilder sb = new StringBuilder();
sb.append("Hello"); // 기존 객체에 문자열 추가
sb.insert(5, ","); // 특정 위치에 삽입 (Hello, World)
sb.delete("World"); // 특정 구간 삭제 (World)
System.out.println(sb.toString()); // 출력: Hello World
위의 예시에서 StringBuilder는 append() 메서드를 사용하여 문자열을 이어붙입니다. 이때 StringBuilder는 기존 배열에 추가하는 방식으로 작업하며, String처럼 매번 새로운 객체를 만들지 않아서 성능상 유리합니다.
StringBuilder가 성능 면에서 가장 적합하다.String을 사용해야 한다.