Java/Basic

[Java-Basic] String, StringBuffer, StringBuilder 속도 비교 및 차이점

Jeong Jeon
반응형

우선 크게 String과 StringBuffer & StringBuilder로 차이점을 확인 해보자.

 

String & StringBuilder & StringBuffer 비교

 

1). String vs StringBuilder & StringBuffer 차이점

String : 객체는 한번 생성되면 할당된 공간이 변하지 않음(immutable)

StringBuffer & StringBuilder : 객체의 공간이 부족해지는 경우 버퍼의 크기를 유연하게 늘려줍니다. (mutable)

 

메모리 관점으로 설명을 추가해보자면...

String은 Immutable하기 때문에 메모리상에 추가적인 공간을 할당받아야한다
예를 들어 , hello를 메모리에 올리고, world를 추가해서 이어붙이려고한다.
이때 메모리상에서는 Heap의 stringpool에 hello가 적재되고, 변수 str 은 hello를 바라보게된다.
추가로 world를 붙이게 될때 str이 바라보던 기존 hello를 바라보지 않고, 새로운 helloworld라는 데이터를 적재하게된다.
이때 그럼 기존의 hello는 어떻게 되는것인가? => str이 바라보는것을 중지하고, str은 helloworld를 바라보며, str이 바라보는것이 중지된 hello는 gc에 의해 삭제목록으로 들어가게 된다.

이로써 문자열이 계속 변경 되어야 할 경우 String 보다는 String Buffer를 사용하는것이 좋다.

 

그렇다면 StringBuffer와 StringBuilder의 차이점은 어떤것이 있을까라는 생각을 하게 된다.

StringBuilder와 StringBuffer의 차이점을 확인 해보자.

 

2). StringBuffer vs StringBuilder 차이점   ==>동기화(Syncronized) 지원 유무!!!

StringBuffer : 각 메서드별 sysnchronized keyword가 존재하여 Multi-Thread 상태에서 동기화를 지원한다.

StringBuilder : Single-Thread환경에서만 사용하도록 만들어졌다. StringBuffer보다 속도는 빠름

 

 

StringBuffer와 StringBuilder를 사용하면 좋은 경우

String str = "hello";
str+= "world";

위와 같은 경우 String은 크기가 고정되어있기 때문에 문자열을 수정할 수 없다. 결국 매번 새로운 String 객체를 생성해서 문자열을 할당한다고 보면 된다.

당연히 메모리상 불필요한 공간을 많이 사용하고, 속도는 더 느려질 수 밖에없다.

하지만 StringBuffer 와 StringBuilder는 매번 new를 하지 않고 , 추가/수정/삭제 기능을 수행할 수 있도록 설계되어있어 새로운 객체를 생성하지 않아도 된다. 

당연히 매번 새로운 객체를 만드는것보다 훨씬 성능은 좋을수 밖에 없다.

 

하지만 !!!!! 여기서 그럼 당연히 StringBuffer만 쓰면 땡이네 라고 할 수 있다.

여기서 간과하면 안돼는 점이 있다.

StringBuffer나 StringBuilder의 단점이 한가지 있다.

바로 StringBuffer나 StringBuilder를 생성할때 Buffer의 크기를 초기에 설정 해줘야하는데, 이 때문에 객체 생성속도는 느려진다.

그러므로 많은연산이 일어나는경우가 아니면 String을 사용해서 문자열 연산을 하는것이 나을수 있다.

 

시간 비교

public class testMain {
	public static double longStr(int len) {
		long start = System.nanoTime();

		String str = "";
	    for(int i=0 ; i < len ; i++) {
	     	str += "a";
	    };

	    long end = System.nanoTime();
	    return (end-start)/(double)1_000_000_000;
	}

	public static double longStrBuffer(int len) {
	    long start = System.nanoTime();

	    StringBuffer sb = new StringBuffer();
	    for(int i=0 ; i < len ; i++) {
	    	sb.append("a");
	    };

	    long end = System.nanoTime();
	    return (end-start)/(double)1_000_000_000;
	}

	public static double longStrBuilder(int len) {
	    long start = System.nanoTime();

	    StringBuilder sb = new StringBuilder();
	    for(int i=0 ; i < len ; i++) {
	    	sb.append("a");
	    };

	    long end = System.nanoTime();
	    return (end-start)/(double)1_000_000_000;
	}
	
	public static void main(String[] args) {
	    int[] lenList = new int[]{10_000, 20_000, 40_000, 80_000, 160_000, 320_000, 640_000,
	                                1_280_000, 2_560_000, 5_120_000, 10_240_000, 20_480_000, 40_960_000, 81_920_000, 163_840_000};

	    for(int len : lenList)
	    {
	    	System.out.println("String :\t" + len + "/\t" + longStr(len));
	        System.out.println("StringBuffer :\t" + len + "/\t" + longStrBuffer(len));
	        System.out.println("StringBuilder :\t" + len + "/\t" + longStrBuilder(len));
	    }
	}
}
String :	10000/	0.0521221
StringBuffer :	10000/	7.252E-4
StringBuilder :	10000/	3.549E-4
String :	20000/	0.1562421
StringBuffer :	20000/	8.992E-4
StringBuilder :	20000/	4.286E-4
String :	40000/	0.5575475
StringBuffer :	40000/	0.0017252
StringBuilder :	40000/	0.0011155
String :	80000/	1.6480827
StringBuffer :	80000/	0.0029146
StringBuilder :	80000/	0.0037118
String :	160000/	6.5718681
StringBuffer :	160000/	0.0027307
StringBuilder :	160000/	0.0019174
String :	320000/	30.3206881
StringBuffer :	320000/	0.003266
StringBuilder :	320000/	0.0031393
String :	640000/	135.6962307
StringBuffer :	640000/	0.0045165
StringBuilder :	640000/	0.0057316
String :	1280000/	568.2966798
StringBuffer :	1280000/	0.0082649
StringBuilder :	1280000/	0.0098229

 

160000부터 String때문에 너무 느려져서 그이상은 버티다 버티다 포기했다.

이정도로 많은 연산을 필요로 할 상황이 아니면... String이 좋지만

이정도로 많은 연산을 해야하면 String은 절대 쓰면 안됀다는것을 확인 할 수 있다.

반응형