StringBuffer/StringBuilder 대상 의 append 방법 으로 문자열 을 연결 하 는 것 보다'+'연산 자 를 사용 하여 문자열 을 연결 하 는 것 이 더 좋 은 경우 가 있 습 니까?

31054 단어 Java
문자열 은 자바 프로그램 에서 가장 자주 사용 하 는 데이터 구조 중의 하나 이다.자바 에서 String 클래스 가'+'를 다시 불 러 왔 습 니 다.즉,문자열 은 다음 코드 와 같이'+'로 직접 연결 할 수 있 습 니 다.
String s = "abc" + "ddd";

그런데 이렇게 하면 정말 좋아요?물론 이 문 제 는 yes or no 에 간단하게 대답 할 수 없습니다.구체 적 인 상황 에 따라 정 해 야 한다.자바 에 StringBuilder 클래스(이 클래스 는 J2SE 5 이상 버 전에 서 만 제공 되 며,이전 버 전에 서 는 StringBuffer 클래스 를 사용 합 니 다)를 제공 합 니 다.이 클래스 도'+'역할 을 할 수 있 습 니 다.그럼 우리 뭐 써 야 되 지?다음은 다음 과 같은 코드 를 살 펴 보 겠 습 니 다.
package string;
public class TestSimplePlus
{
public static void main(String[] args)
{
String s = "abc";
String ss = "ok" + s + "xyz" + 5;
System.out.println(ss);
	}
}

위의 코드 는 정확 한 결 과 를 출력 할 것 이다.겉으로 볼 때 문자열 과 정형 에 대해'+'번 호 를 사용 하 는 것 은 별 차이 가 없 지만 사실은 그렇습니까?다음은 이 코드 의 본질 을 살 펴 보 자.우 리 는 먼저 역 컴 파일 도구(예 를 들 어 jdk 테이프 의 자바 p,또는 jad)를 사용 하여 TestSimple Plus 를 자바 Byte Code 로 역 컴 파일 하 는데 그 중의 비밀 은 한눈 에 알 수 있다.본 고 는 jad 를 사용 하여 역 컴 파일 할 것 입 니 다.명령 은 다음 과 같 습 니 다jad -o -a -s d.java TestSimplePlus.class역 컴 파일 후의 코드 는 다음 과 같 습 니 다.
package string;
import java.io.PrintStream;
public class TestSimplePlus
{
public TestSimplePlus()
{
// 0 0:aload_0
// 1 1:invokespecial #8 
// 2 4:return
}

public static void main(String args[])
 {
 String s = "abc";
 // 0 0:ldc1 #16 
 // 1 2:astore_1
 String ss = (new StringBuilder("ok")).append(s).append("xyz").append(5).toString()
 // 2 3:new #18 
 // 3 6:dup
 // 4 7:ldc1 #20 
 // 5 9:invokespecial #22 
 // 6 12:aload_1
 // 7 13:invokevirtual #25 
 // 8 16:ldc1 #29 
 // 9 18:invokevirtual #25 
 // 10 21:iconst_5
 // 11 22:invokevirtual #31 
 // 12 25:invokevirtual #34 
// 13 28:astore_2
System.out.println(ss);
// 14 29:getstatic #38 
// 15 32:aload_2
// 16 33:invokevirtual #44 
// 17 36:return
}
}

독자 들 은 위의 자바 바이트 코드 를 보고 혼 란 스 러 워 할 지 모 르 지만 걱정 하지 마 세 요.본 고의 목적 은 자바 바이트 코드 를 설명 하 는 것 이 아니 기 때문에 구체 적 인 바이트 코드 의 의 미 를 이해 할 필요 가 없다.jad 역 컴 파일 을 사용 하 는 장점 중 하 나 는 바이트 코드 와 소스 코드 를 동시에 생 성 할 수 있다 는 것 이다.이렇게 하면 대조 연 구 를 진행 할 수 있다.위의 코드 에서 쉽게 알 수 있 듯 이 원본 프로그램 에서'+'를 사 용 했 지만 컴 파일 할 때'+'를 StringBuilder 로 변환 합 니 다.따라서 자바 에 서 는 어떤 방식 으로 문자열 연결 을 하 든 사실상 StringBuilder 를 사용 한 다 는 결론 을 얻 을 수 있다.그렇다면 이 결론 에 따라'+'와'StringBuilder'의 효 과 는 같 지 않 을 까?이것 은 두 가지 측면 에서 해석 해 야 한다.실행 결과 로 설명 하면'+'는 StringBuilder 와 똑 같 습 니 다.그러나 운영 효율 과 자원 소 모 를 보면 큰 차이 가 있 을 것 이다.물론 연결 문자 직렬 식 이 간단 하 다 면(위의 순서 구조 와 같이)'+'는 StringBuilder 와 기본적으로 같 지만 구조 가 복잡 하고 순환 으로 문자열 을 연결 하면 자바 Byte Code 가 크게 다 를 수 있 습 니 다.먼저 다음 코드 를 보 여 주세요.
package string;
import java.util.*;
public class TestComplexPlus
{
public static void main(String[] args)
{
String s = "";
Random rand = new Random();
for (int i = 0; i < 10; i++)
{
s = s + rand.nextInt(1000) + " ";
}
System.out.println(s);
}
}

위의 코드 를 컴 파일 한 자바 Byte Code 는 다음 과 같 습 니 다.
 package string;

 import java.io.PrintStream;
 import java.util.Random;

 public class TestComplexPlus
 {

public TestComplexPlus()
 {
 // 0 0:aload_0
 // 1 1:invokespecial #8 
 // 2 4:return
 }

 public static void main(String args[])
 {
 String s = "";
 // 0 0:ldc1 #16 
 // 1 2:astore_1
 Random rand = new Random();
 // 2 3:new #18 
 // 3 6:dup
 // 4 7:invokespecial #20 
 // 5 10:astore_2
 for(int i = 0; i < 10; i++)
 //* 6 11:iconst_0
 //* 7 12:istore_3
 //* 8 13:goto 49
 s = (new StringBuilder(String.valueOf(s))).append(rand.nextInt(1000)).append(" ").t
oString();
 // 9 16:new #21 
 // 10 19:dup
 // 11 20:aload_1
 // 12 21:invokestatic #23 
 // 13 24:invokespecial #29 
 // 14 27:aload_2
// 15 28:sipush 1000
// 16 31:invokevirtual #32 
// 17 34:invokevirtual #36 
// 18 37:ldc1 #40 
// 19 39:invokevirtual #42 
// 20 42:invokevirtual #45 
// 21 45:astore_1
// 22 46:iinc 3 1
// 23 49:iload_3
// 24 50:bipush 10
// 25 52:icmplt 16
System.out.println(s);
// 26 55:getstatic #49 
// 27 58:aload_1
// 28 59:invokevirtual #55 
// 29 62:return
}
}

컴 파일 러 가'+'를 StringBuilder 로 바 꾸 었 지만 StringBuilder 대상 을 만 드 는 위 치 는 for 구문 내부 에 있 음 을 볼 수 있 습 니 다.이 는 순환 을 실행 할 때마다 StringBuilder 대상 을 만 드 는 것 을 의미 합 니 다.(이 경우 StringBuilder 대상 을 10 개 만 들 었 습 니 다)자바 에 쓰레기 수 거 기 는 있 지만 이 수 거 기의 작업 시간 은 정 해 지지 않 습 니 다.만약 이런 쓰레기 가 끊임없이 발생 한다 면,여전히 대량의 자원 을 점용 할 것 이다.이 문 제 를 해결 하 는 방법 은 프로그램 에서 문자열 을 StringBuilder 로 직접 연결 하 는 것 입 니 다.코드 는 다음 과 같 습 니 다.
package string;

import java.util.*;

public class TestStringBuilder
{
public static void main(String[] args)
{
String s = "";
 Random rand = new Random();
 StringBuilder result = new StringBuilder();
 for (int i = 0; i < 10; i++)
 {
 result.append(rand.nextInt(1000));
 result.append(" ");
 }
 System.out.println(result.toString());
 }
 }

위의 코드 를 역 컴 파일 한 결 과 는 다음 과 같다.
20.package string;

import java.io.PrintStream;
import java.util.Random;

public class TestStringBuilder
{

public TestStringBuilder()
 {
 // 0 0:aload_0
 // 1 1:invokespecial #8 
 // 2 4:return
 }

 public static void main(String args[])
 {
 String s = "";
 // 0 0:ldc1 #16 
 // 1 2:astore_1
 Random rand = new Random();
 // 2 3:new #18 
 // 3 6:dup
 // 4 7:invokespecial #20 
 // 5 10:astore_2
 StringBuilder result = new StringBuilder();
 // 6 11:new #21 
 // 7 14:dup
 // 8 15:invokespecial #23 
 // 9 18:astore_3
 for(int i = 0; i < 10; i++)
 //* 10 19:iconst_0
 //* 11 20:istore 4
 //* 12 22:goto 47
 {
 result.append(rand.nextInt(1000));
 // 13 25:aload_3
// 14 26:aload_2
// 15 27:sipush 1000
// 16 30:invokevirtual #24 
// 17 33:invokevirtual #28 
// 18 36:pop
result.append(" ");
// 19 37:aload_3
// 20 38:ldc1 #32 
// 21 40:invokevirtual #34 
// 22 43:pop
}

// 23 44:iinc 4 1
// 24 47:iload 4
// 25 49:bipush 10
// 26 51:icmplt 25
System.out.println(result.toString());
// 27 54:getstatic #37 
// 28 57:aload_3
// 29 58:invokevirtual #43 
// 30 61:invokevirtual #47 
// 31 64:return
}
}

위의 역 컴 파일 결 과 를 보면 StringBuilder 를 만 드 는 코드 가 for 구문 밖 에 놓 여 있 음 을 알 수 있 습 니 다.이렇게 처리 하 는 것 은 소스 프로그램 에서 복잡 해 보이 지만 더욱 높 은 효율 로 바 뀌 었 고 소모 하 는 자원 도 더욱 적 어 졌 다.StringBuilder 를 사용 할 때"+"와 StringBuilder 를 섞 지 않도록 주의해 야 합 니 다.그렇지 않 으 면 다음 코드 와 같은 더 많은 StringBuilder 대상 을 만 들 수 있 습 니 다.
for (int i = 0; i < 10; i++)
{
result.append(rand.nextInt(1000));
result.append(" ");
}

다음 과 같은 형식 으로 바 꿉 니 다.
for (int i = 0; i < 10; i++)
{
result.append(rand.nextInt(1000) + " ");
}

역 컴 파일 후의 결 과 는 다음 과 같다.
for(int i = 0; i < 10; i++)
//* 10 19:iconst_0
//* 11 20:istore 4
//* 12 22:goto 65
{
result.append((new StringBuilder(String.valueOf(rand.nextInt(1000)))).append(" ").toString());
// 13 25:aload_3
// 14 26:new #21 
// 15 29:dup

위의 코드 를 통 해 알 수 있 듯 이 자바 컴 파일 러 는'+'를 StringBuilder 로 컴 파일 했 습 니 다.이렇게 for 문 구 는 순환 할 때마다 StringBuilder 대상 을 만 들 었 습 니 다.위의 코드 를 JDK 1.4 에서 컴 파일 하려 면 StringBuilder 를 StringBuffer 로 바 꿔 야 하 며,JDK 1.4 는'+'를 StringBuffer 로 바 꿔 야 합 니 다(JDK 1.4 는 StringBuilder 클래스 를 제공 하지 않 기 때 문 입 니 다).StringBuffer 와 StringBuilder 의 기능 은 기본적으로 같 습 니 다.StringBuffer 는 스 레 드 가 안전 할 뿐 StringBuilder 는 스 레 드 가 안전 하지 않 습 니 다.따라서 StringBuilder 의 효율 이 더 높다.

좋은 웹페이지 즐겨찾기