본문 바로가기
멀티캠퍼스 풀스택 과정/Java의 정석

자바의 정석6-2 String 클래스, Stringbuffer(StringBuilder)

by 이쟝 2022. 1. 7.

String클래스

- 문자열을 다루기 위한 클래스

- String클래스 = 데이터(char[])(문자배열) + 메서드(문자열 관련)

- 인스턴스 생성시 생성자의 매개변수로 입력 받는 문자열은 문자형 배열(char[])로 저장되는 것

 

String클래스의 구조

-> String클래스는 앞에 final이 붙어 있으므로 다른 클래스의 조상이 될 수 없다.

 

내용을 변경할 수 없는 불변(immutable) 클래스

String a = “a”;

String b = “b”;

ab = a + b;

새로운 문자열 객체 ab가 만들어지게 됨

- 덧셈 연산자(+)를 이용한 문자열 결합은 성능이 떨어짐(매 연산 시 마다 새로운 문자열 객체가 만들어져서 메모리 공간을 차지해서)

- 문자열의 결합이나 변경이 잦다면, 내용을 변경가능한 StringBuffer(내용변경가능)를 사용


문자열의 비교

(1) 문자열 리터럴을 지정하는 방법 String str = “abc”;

(2) String클래스의 생성자를 사용해서 만드는 방법 String str = new String(“abc”);

 

 

- 문자열 리터럴로 문자열을 만들면 기존의 것을 재사용하기 때문에 하나의 문자열을 여러 참조변수가 공유

- new 연산자에 의해서 항상 새로운 String 인스턴스가 생성(새로운 문자열이 만들어짐)

 

str1 == str2 ? true str3 == str4 ? false
str1.equals(str2) ? true str3.equals(str4) ? true
equlas는 내용 비교 => true가 나옴, 대입연산자는 주소 비교 => true가 나올 수도 있고, false가 나올 수도 있다.

문자열 리터럴

- 문자열 리터럴(상수)은 프로그램 실행 시 자동으로 생성된다.(constant poo(상수저장소)에 저장)

 

 

-> “AAA”를 담고 있는 String인스턴스가 하나 생성된 후. 참조변수 s1, s2, s3는 모두 이 String 인스턴스 참조

 

- 자동으로 만들어진 “AAA” 객체에 각 참조변수가 공유함

- 같은 내용의 문자열 리터럴은 하나만 만들어진다.

- 내용이 변경 불가해서 여러 참조변수가 하나의 String 객체를 공유해도 문제가 없음

 

빈 문자열(“ “, empty string)

- 내용이 없는 문자열, 크기가 0 char형 배열을 저장하는 문자열

- String str = “”;   // str을 빈 문자열로 초기화

 

크기가 0인 배열을 생성하는 것은 어느 타입이나 가능

char[ ] chArr = new char[0];  // 길이가 0이고 참조변수가 chArr char배열

int[ ] iArr = { };             // 길이가 0이고 참조변수가 iArr int 배열

 

배열의 크기가 [10]이면 int 4byte, 4byte x 10(길이, length) = 40 byte(크기, size)

-> 하지만 자바에서는 혼용해서 쓰고 있음(자바에서 메모리를 다루지 않아서) 40byte로 쓰지 않고 주로 10을 씀!(크기나 배열이나 자바에서는..)

 

문자(char)와 문자열(String)의 초기화

String s = “”; // 빈 문자열로 초기화

char c = ‘ ‘;  // 공백으로 초기화

 

 

- 왼쪽 코드처럼 작성하기!

- 왼쪽 코드는 빈 객체가 하나가 만들어져서 str1, str2, str3이 공유하면서 메모리를 적게 사용할 수 있는데 오른쪽 코드는 new 연산자를 써서 빈 객체가 세 개가 만들어짐

 


String클래스의 생성자와 메서드

 

메서드/설명 예제 결과
String(String s) (잘 사용하지 않음) String s = new String(“Hello”); s = “Hello”
주어진 문자열(s)를 갖는 String인스턴스를 생성한다.
String(char[ ] value) char[ ] c = {‘H’, ’e’, ’l’, ’l’, ’o’};
String s = new String(c)
s = “Hello”
주어진 문자열(value)을 갖는 String인스턴스를 생성한다.( char[ ] -> String, 반대로는 String -> toCharArray( ))
String(StringBuffer buf) StringBuffer sb = new StringBuffer(“Hello”);
String s = new String(sb);
s = “Hello”
Stringbuffer인스턴스가 갖고 있는 문자열과 같은 내용의 String인스턴스를 생성한다. (Stringbuffer는 내용변경가능)
char charAt(int index) String s = “Hello”;
String n = “0123456”;
char c = s.charAt(1);
char c2 = n.charAt(1);
c = ‘e’
c2 = ‘1’
지정된 위치(index)에 있는 문자를 알려준다.(index0부터 시작)
int compareTo(String str) int i = “aaa”.compareTo(“aaa”);
int i2 = “aaa”.compareTo(“bbb”);
int i3 = “bbb.”.compareTo(“aaa”);
사전대로면 음수 사전반대면 양수
 
i=0 같으면
i2 = -1(오른쪽이 크면 음수)
i3 = 1(오른쪽이 작으면 양수)
문자열(str)과 사전순서로 비교한다. 같으면 0을 사전순으로 이전이면 음수를, 이후면 양수를 반환한다.(정렬할 때 사용)
String concat(String str) String s = “Hello”;
String s2 = s.concat(“World”)
s2 = “Hello World”
문자열(str)을 뒤에 덧붙인다.
boolean contains(CharSequence s) String s = “abcdefg”;
Boolean b = s.contains(“bc”)
b = true
지정된 문자열(s)이 포함되었는지 검사 (CharSequence는 인터페이스)
boolean endsWith(String suffix) String file = “Hello.txt”;
Boolean b = file.endsWith(“txt”);
b = true
지정된 문자열(suffix)로 끝나는지 검사<-> startswith
Boolean equals(Object obj) String s = “Hello”;
Boolean b = s.equals(“Hello”);
Boolean b2 = s.euqals(“hello”);
b = true
b2 = true
매개변수로 받은 문자열(obj)String인스턴스의 문자열을 비교한다. objString이 아니거나 문자열이 다르면 falst 반환
Boolean equalsIgnoreCase(String str) String s = “Hello”;
boolean b = s.equalsIgnoreCase(“HELLO”);
boolean b2 = s.equalsIgnoreCase(“heLLO”);
b = true
b2 = true
문자열과 String인스턴스의 문자열을 대소문자 구분없이 비교(대소문자 무시)
int indexof(int ch) String s = “Hello”;
int idx1 = s.indexOf(‘o’);
int idx2 = s.indexOf(‘k’);
idx1 = 4
idx2 = -1
주어진 문자(ch)가 문자열에 존재하는지 확인해 위치(index)를 알려준다. 못 찾으면 -1을 반환한다.(index 0부터 시작)
int indexOf(int ch, int pos) String s = “Hello”;
int idx1 = s.indexOf(‘e’, 0);
int idx2 = s.indexOf(‘e’, 2);
idx = 1
idx2 = -1
주어진 문자(ch)가 문자열에 존재하는지 지정된 위치(pos)부터 확인해 위치(index)를 알려준다. 못 찾으면 -1을 반환한다.
int indexOf(String str) String s = “ABCDEFG”;
int idx = s.indexOf(“CD”);
idx = 2
주어진 문자열이 존재하는지 확인해 그 위치(index)를 알려준다. 없으면 -1을 반환한다.
int lastIndexOf(int ch) String s = “java.lang.Object”;
int idx1 = s.lastIndexOf(‘.’);
int idx2 = s.indexOf(‘.’);
idx1 = 9
idx2 = 4
지정된 문자 또는 문자코드를 문자열의 오른쪽 끝에서부터 찾아서 위치(index)를 알려준다. 못 찾으면 -1을 반환한다.
int lastIndexOf(String str) String s = “java.lang.Object”;
int idx1 = s.lastIndexOf(“java”);
int idx2 = s.indexOf(“java”);
idx1 = 10
idx2 = 0
지정된 문자열을 인스턴스의 문자열문자열부터 찾아서 위치(index)를 알려준다. 못 찾으면 -1 반환
int length( ) String s = “Hello”;
int length = s.length
length = 5
문자열의 길이를 반환
String[ ] split(String regex) String animals = “dog, cat, bear”;
String[ ] arr = animals.split(“,”);
arr[0] = “dog”
arr[1] = “cat”
arr[2] = “bear”
문자열을 지정된 분리자(regex)로 나누어 문자열 배열에 담아 반환
String[ ] split(String regex, int limit) String animals = “dog, cat, bear”;
String[ ] arr = animals.split(“,”,2); (두 부분으로 나눈다!)
arr[0] = “dog”
arr[1] = “cat, bear”
 
문자열을 지정된 분리자(regex)로 나누어 문자열 배열에 담아 반환. 단 문자열 전체를 지정된 수(limit)로 자른다.
boolean startsWith(String prefix) String s = “java.lang.Object”;
Boolean b = s.startsWith(“java”);
boolean b2 = s.startsWith(“lang”);
b = true
b2 = false
주어진 문자열(prefix)로 시작하는지 검사한다.
String substring(int begin)
String substring(int begin, int end)
String s = “java.lang.object”;
String c = s.substring(10);
String p = s.substring(5, 9);
c = “Object”
p = “lang”
주어진 시작위치(begin)부터 끝 위치(end)범위에 포함된 문자열을 얻는다. 이 때 시작위치의 문자는 범위에 포함되지만, 끝 위치의 문자는 포함되지 않음
String toLowerCase( ) String s = “Hello”;
String s1 = s.toLowerCase( );
s1 = “hello”
String인스턴스에 저장 되어있는 모든 문자열을 소문자로 변환하여 반환
String toUpperCase( ) String s = “Hello”;
String s1 = s.toUpperCase( );
s1 = “HELLO”
String인스턴스에 저장 되어있는 모든 문자열을 대문자로 변환하여 반환
String trim( ) String s = “   Hello World   “;
String s1 = s.trim( );
s1=”Hello World”
문자열의 왼쪽 끝과 오른쪽 끝에 있는 공백을 없앤 결과를 반환. 이 때 문자열 중간에 있는 공백은 제거되지 않음
String replace (char old, char nw) String s = “Hello”;
String s1 = s.replace(‘H’, ‘C’);
s1 = “Cello”
문자열 중의 문자(old)를 새로운 문자(nw)로 바꾼 문자열을 반환
static String valueOf(Boolean b)
static String valueOf(char c)
static String valueOf(int i)
static String valueOf(long l)
static String valueOf(float f)
static String valueOf(double d)
static String valueOf(Object o)
String b = String.valueOf(true);
String c = String.valueOf(‘a’);
String i = String.valueOf(100);
String l = String.valueOf(100L);
String f = String.valueOf(10f);
String d = String.valueOf(10.0);
java.util.Date dd = new java.util.Date( );
String date = String.valueOf(dd)
b = “true”
c = “a”
i = “100”
l = “100”
f = “10.0”
d = “10.0”
date = “wed Jan 27 21:24:29 KST 2016”
지정된 값을 문자열로 변환하여 반환한다. 참조변수의 경우, toString( )을 호출한 결과를 반환

join( )StringJoiner

- join( )은 여러 문자열 사이에 구분자를 넣어서 결합한다.

- 구분자로 문자열을 자르는 split( )과 반대의 작업을 함

 

String animals = "dog,cat,bear";
String[] arr = animals.split(",");  // 문자열을 ','를 구분자로 나눠서 배열에 저장
String str = String.join("-", arr);  // 배열의 문자열을 '-'로 구분해서 결합
System.out.println(str);             // dog-cat-bear

 

예제)

import java.util.StringJoiner;

public class StringEx4 {
	public static void main(String[] args) {
		String animals = "dog,cat,bear";
		String[] arr = animals.split(",");
		String arrToStr = String.join("-",arr); 
		System.out.println(arrToStr);
		
//		System.out.println(String.join("-",arr)); 위 코드와 동일
		
		StringJoiner sj = new StringJoiner("/","{","}"); // 첫번재 문자는 문자열 사이에!
		for(String s :arr) {
			sj.add(s);
		}System.out.println(sj.toString());
		
//		for(int i=0;i<arr.length;i++) { // 위 코드와 동일
//			sj.add(arr[i]);
//		}
//		System.out.println(sj);
	}
}

문자열과 기본형 간의 변환

 

1) 숫자(기본형 값))를 문자열(String)로 바꾸는 방법

 

int i = 100;
String str1 = i + "";            // 100을 '100'으로 변환하는 방법1: 편리
String str2 = String.valueOf(i);  // 100을 '100'으로 변환하는 방법2: 빠름

 

2) 문자열을 숫자로 바꾸는 방법

 

int i2 = Integer.parseInt("100");    // "100"을 100으로 변환하는 방법1: 옛날
int i3 = Integer.valueOf("100");     // "100"을 100으로 변환하는 방법2: 최근
Integer i4 =Integer.valueOf("100");  // 원래는 반환타입이 Integer(i3와 같은 표현)
i2와 i3은 반환타입이 기본형이라 똑같은데 i4는 반환타입이 Integer라서 약간 다름

 

요새는 valueOf으로 주로 사용(문자열-&gt;기본형)

 

예제)

int iVal = 100;
String strVal = String.valueOf(iVal); // int를 String으로 변환
// String strVal = ival + "";   
		
double dVal = 200.0;
String strVal2 = dVal + "";  // double을 String으로 변환
// String strVal2 = String.valueOf(dval);
		
// 문자열을 숫자로 바꾸는 방법(위 코드와 아래 코드는 같은데 parse를 valueOf로 변형한 것!)
double sum = Integer.parseInt(strVal) + Double.parseDouble(strVal2);
double sum2 = Integer.valueOf(strVal) + Double.valueOf(strVal2);
		
// 위 코드와 아래 코드는 같음
System.out.println(String.join("",strVal, "+", strVal2, "=")+sum); //구분자 없이 하나로 합치기!
System.out.println(strVal+"+"+strVal2+'='+sum2);

StringBuffer클래스

- 문자열을 저장하고 다루기 위한 클래스

- String처럼 문자형 배열(char[ ])을 내부적으로 가지고 있다.

- StringBuffer인스턴스에 저장될 문자열의 길이를 고려해 충분히 여유있는 크기로 지정하는 것이 좋음

- 버퍼의 크기를 지정해주지 않으면 16개의 문자를 저장할 수 있는 크기의 버퍼를 생성

 

String과 같은 구조

 

public StringBuffer(int length) { // 적절한 크기를 length에 저장해주기!
	value = new char[length];
	shared = false;
}

public StringBuffer() {
	this(16);  // 버퍼의 크기를 지정하지 않으면 버퍼의 크기는 16이 된다.
}
		
public StringBuffer(String str) {
	this(str.length() + 16);   // 지정한 문자열의 길이보다 16이 더 크게 버퍼를 생성
	append(str); 
}

 

StringBuffer 생성자

- 배열은 길이 변경불가. 공간이 부족하면 새로운 배열 생성(보통 기존 배열의 두 배)해야 함

 

1) 새로운 배열 생성

2) 기존 내용을 복사

3) 참조변수 변경

 

StringBuffer 변경

- String과 달리 내용을 변경할 수 있다.(mutable)

 

&ldquo;abc&rdquo;Char 배열이 저장된 StringBuffer 객체 생성!

 

 

 

StringBuffer sb = new StringBuffer("abc");
sb.append("123");          // sb의 내용 뒤에 "123"을 추가한다.
-> append( )는 지정된 내용을 StringBuffer에 추가 후, StringBuffer의 참조를 반환(반환타입이 StringBuffer)
-> 그 외 delete( ): 삭제, insert( ): 삽입

 

 

-> 오른쪽 코드처럼(.append.append)를 쓸 수 있는 이유가 반환타입이 StringBuffer이기 때문!(메서드 체이닝: 메서드를 계속 연결해서 호출할 수 있음)

-> sbsb2도 같은 주소를 가리킴


StringBuffer의 비교

- StringBufferequals과 오버라이딩 되어있지 않다. (주소로 비교) -> this == obj

 

-&gt; 내용이 같아도 결과는 false, 참조변수(주소)비교도 둘이 다른 객체이기 때문에 당연히 false

 

- 반면에 toString( )은 오버라이딩되어 있어서 StringBuffer인스턴스에 toString( )을 호출하면, 담고 있는 문자열 반환

- StringBuffer의 객체를 to.String( )을 사용해서 String으로 변환 후에 equals로 비교해야 한다.

 


StringBuffer의 생성자와 메서드

 

 

더보기

- StringBuffer 생성자에 아무것도 넣지 않으면 길이가 16인 배열(char[ ])이 생김

- 만약 10을 넣으면 길이가 10인 배열(char[ ])이 생김

- StringBuffer로 배열을 만든다면 StringBuffer(int length)를 사용!!

- sb, sb2, sb3는 다 같은 배열(char[ ])을 가리키고 있음!(같은 StringBuffer타입 객체를 사용해서)

- StringBuffer는 자기 자신을 반환

 

 

더보기

- 버퍼크기(char[ ]) 배열의 크기는 100이고 저장된 문자열의 길이는 4이다.

- delete은 문자 n개 제거, deleteCharAt은 문자 1개 제거

 

 


예제) StringBuffer의 생성자와 메서드

 

StringBuffer sb = new StringBuffer("01");
StringBuffer sb2 = sb.append(23);
sb2.append(4).append(56);
StringBuffer sb3 = sb.append(78);
sb3.append(9.0);
		
System.out.printf("%s ", sb);
System.out.printf("%s ", sb2);
System.out.printf("%s\n", sb3);
		
System.out.printf("sb:%s ", sb.deleteCharAt(10));  // 10번째숫자 삭제
System.out.printf("sb:%s ", sb.delete(3,6));  // 3번째 숫자부터 5번째 숫자까지 삭제
System.out.printf("sb2:%s ", sb2.insert(3, "abc")); // 3번째 자리부터 순서대로 abc 삽입
System.out.printf("sb3:%s\n", sb3.replace(6,sb.length(),"END"));  // 6번째 숫자부터 마지막까지의 숫자를 END로 치환

System.out.printf("capacity:%d ", sb.capacity()); // 배열 객체의 크기
System.out.printf("length:%d", sb.length());      // 배열 객체 안에 저장되어 있는 문자열의 길이

StringBuilder

- StringBuffer와 거의 유사함

- StringBuffer는 동기화 되어 있고, StringBuilder는 동기화 되어있지 않음

(StringBuffer와 StringBuilder는 동기화 여부의 차이)

- 동기화란? 멀티 쓰레드에 안전(thread-safe)한 것 -> 데이터 보호

 

싱글 쓰레드 멀티 쓰레드
한 번에 1개만 작업 한 번에 여러 개 작업(두가지 작업을 동시에 진행)
데이터를 공유하면서 에러가 날 수 있는데 이것을 막아주는 것이 동기화!

 

- 멀티 쓰레드 프로그램이 아닌 경우, 동기화는 불필요한 성능저하 -> 이 때 StringBuffer대신 StringBuilder를 사용하면 성능 향상(싱글쓰레드 프로그램에는 StringBuilder 사용!)

 

StringBuffer sb1 = new StringBuffer("abc");    // StringBuffer 사용
sb1.append("def”);
		
StringBuilder sb1 = new StringBuilder("abc");  // StringBuilder 사용
sb1.append("def");

 

 

https://www.codejava.net/java-core/the-java-language/why-use-stringbuffer-and-stringbuilder-in-java

 

Why Use StringBuffer and StringBuilder in Java

 

www.codejava.net