toString()
아래 두 가지 구현을 고려할 때 어느 것이 선호됩니까?
public String toString(){
return "{a:"+ a + ", b:" + b + ", c: " + c +"}";
}
또는
public String toString(){
StringBuilder sb = new StringBuilder(100);
return sb.append("{a:").append(a)
.append(", b:").append(b)
.append(", c:").append(c)
.append("}")
.toString();
}
?
더 중요한 것은 3 개의 속성 만 있으면 차이가 없을 수 있지만 어느 시점에서 +
concat에서 StringBuilder
?
답변
버전 1은 더 짧기 때문에 바람직 하며 컴파일러는 실제로 버전 2로 변환하므로 성능 차이는 없습니다.
더 중요한 것은 우리에게 단지 3 개의 속성 만 있다면 차이가 없을 수 있지만, concat에서 builder로 어느 시점에서 전환합니까?
루프에서 연결하는 시점에서-일반적으로 컴파일러 StringBuilder
가 자체적으로 대체 할 수없는 경우 입니다.
답변
핵심은 단일 연결을 한 곳에 작성하거나 시간이 지남에 따라 누적하는 것입니다.
예를 들어 StringBuilder를 명시 적으로 사용하는 것은 중요하지 않습니다. (첫 번째 경우의 컴파일 된 코드를보십시오.)
그러나 루프 내부에서 문자열을 작성하는 경우 StringBuilder를 사용하십시오.
분명히 설명하기 위해 hugeArray에 수천 개의 문자열이 포함되어 있다고 가정하면 다음과 같은 코드가 작성됩니다.
...
String result = "";
for (String s : hugeArray) {
result = result + s;
}
다음과 비교할 때 시간과 메모리가 낭비됩니다.
...
StringBuilder sb = new StringBuilder();
for (String s : hugeArray) {
sb.append(s);
}
String result = sb.toString();
답변
나는 선호한다:
String.format( "{a: %s, b: %s, c: %s}", a, b, c );
… 짧고 읽을 수 있기 때문입니다.
내가 것 없는 당신은 매우 높은 반복 횟수와 루프 내부에 그것을 사용하지 않는 한 속도에 대해이 작업을 최적화 하고 성능 차이를 측정했다.
많은 매개 변수를 출력 해야하는 경우이 양식이 혼란 스러울 수 있음에 동의합니다 (의견 중 하나가 말한 것처럼). 이 경우 더 읽기 쉬운 형식으로 전환하고 ( 아파치 커먼즈의 ToStringBuilder 를 사용하여 매트 b의 대답에서 가져옴) 성능을 다시 무시합니다.
답변
대부분의 경우 두 접근 방식간에 실제 차이는 보이지 않지만 다음과 같은 최악의 시나리오를 구성하는 것은 쉽습니다.
public class Main
{
public static void main(String[] args)
{
long now = System.currentTimeMillis();
slow();
System.out.println("slow elapsed " + (System.currentTimeMillis() - now) + " ms");
now = System.currentTimeMillis();
fast();
System.out.println("fast elapsed " + (System.currentTimeMillis() - now) + " ms");
}
private static void fast()
{
StringBuilder s = new StringBuilder();
for(int i=0;i<100000;i++)
s.append("*");
}
private static void slow()
{
String s = "";
for(int i=0;i<100000;i++)
s+="*";
}
}
출력은 다음과 같습니다.
slow elapsed 11741 ms
fast elapsed 7 ms
문제는 문자열에 + =를 추가하면 새 문자열을 재구성하므로 문자열 길이에 비례하는 비용이 든다는 것입니다 (둘 다 합산).
그래서-당신의 질문에 :
두 번째 방법은 더 빠르지 만 읽기 어렵고 유지하기가 더 어렵습니다. 내가 말했듯이, 특정 경우에는 아마도 차이가 보이지 않을 것입니다.
답변
또한 append 또는 +를 사용할 지에 대한 사실과 상사와 충돌했습니다 .Append를 사용하고 있기 때문에 (새로운 객체가 생성 될 때마다 말하는 것처럼 여전히 파악할 수 없습니다). 그래서 저는 R & D를 할 생각을했지만 Michael Borgwardt의 설명을 좋아하지만 누군가가 나중에 정말로 알아야 할 경우 설명을 보여주고 싶었습니다.
/**
*
* @author Perilbrain
*/
public class Appc {
public Appc() {
String x = "no name";
x += "I have Added a name" + "We May need few more names" + Appc.this;
x.concat(x);
// x+=x.toString(); --It creates new StringBuilder object before concatenation so avoid if possible
//System.out.println(x);
}
public void Sb() {
StringBuilder sbb = new StringBuilder("no name");
sbb.append("I have Added a name");
sbb.append("We May need few more names");
sbb.append(Appc.this);
sbb.append(sbb.toString());
// System.out.println(sbb.toString());
}
}
위 클래스의 분해는 다음과 같이 나옵니다.
.method public <init>()V //public Appc()
.limit stack 2
.limit locals 2
met001_begin: ; DATA XREF: met001_slot000i
.line 12
aload_0 ; met001_slot000
invokespecial java/lang/Object.<init>()V
.line 13
ldc "no name"
astore_1 ; met001_slot001
.line 14
met001_7: ; DATA XREF: met001_slot001i
new java/lang/StringBuilder //1st object of SB
dup
invokespecial java/lang/StringBuilder.<init>()V
aload_1 ; met001_slot001
invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
ldc "I have Added a nameWe May need few more names"
invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
aload_0 ; met001_slot000
invokevirtual java/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lan\
g/StringBuilder;
invokevirtual java/lang/StringBuilder.toString()Ljava/lang/String;
astore_1 ; met001_slot001
.line 15
aload_1 ; met001_slot001
aload_1 ; met001_slot001
invokevirtual java/lang/String.concat(Ljava/lang/String;)Ljava/lang/Strin\
g;
pop
.line 18
return //no more SB created
met001_end: ; DATA XREF: met001_slot000i ...
; ===========================================================================
;met001_slot000 ; DATA XREF: <init>r ...
.var 0 is this LAppc; from met001_begin to met001_end
;met001_slot001 ; DATA XREF: <init>+6w ...
.var 1 is x Ljava/lang/String; from met001_7 to met001_end
.end method
;44-1=44
; ---------------------------------------------------------------------------
; Segment type: Pure code
.method public Sb()V //public void Sb
.limit stack 3
.limit locals 2
met002_begin: ; DATA XREF: met002_slot000i
.line 21
new java/lang/StringBuilder
dup
ldc "no name"
invokespecial java/lang/StringBuilder.<init>(Ljava/lang/String;)V
astore_1 ; met002_slot001
.line 22
met002_10: ; DATA XREF: met002_slot001i
aload_1 ; met002_slot001
ldc "I have Added a name"
invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
pop
.line 23
aload_1 ; met002_slot001
ldc "We May need few more names"
invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
pop
.line 24
aload_1 ; met002_slot001
aload_0 ; met002_slot000
invokevirtual java/lang/StringBuilder.append(Ljava/lang/Object;)Ljava/lan\
g/StringBuilder;
pop
.line 25
aload_1 ; met002_slot001
aload_1 ; met002_slot001
invokevirtual java/lang/StringBuilder.toString()Ljava/lang/String;
invokevirtual java/lang/StringBuilder.append(Ljava/lang/String;)Ljava/lan\
g/StringBuilder;
pop
.line 28
return
met002_end: ; DATA XREF: met002_slot000i ...
;met002_slot000 ; DATA XREF: Sb+25r
.var 0 is this LAppc; from met002_begin to met002_end
;met002_slot001 ; DATA XREF: Sb+9w ...
.var 1 is sbb Ljava/lang/StringBuilder; from met002_10 to met002_end
.end method
;96-49=48
; ---------------------------------------------------------------------------
위의 두 코드에서 Michael이 옳다는 것을 알 수 있습니다. 각각 하나의 SB 객체 만 생성됩니다.
답변
Java 1.5부터 “+”및 StringBuilder.append ()를 사용한 간단한 한 줄 연결은 정확히 동일한 바이트 코드를 생성합니다.
코드 가독성을 위해 “+”를 사용하십시오.
2 예외 :
- 멀티 스레드 환경 : StringBuffer
- 루프 연결 : StringBuilder / StringBuffer
답변
최신 버전의 Java (1.8)를 사용하는 disassembly ( javap -c
)는 컴파일러가 도입 한 최적화를 보여줍니다. +
또한 sb.append()
매우 유사한 코드를 생성합니다. 그러나 우리가 사용하는 경우 동작을 검사하는 것이 좋습니다.+
for 루프에서 하는 .
for 루프에서 +를 사용하여 문자열 추가
자바:
public String myCatPlus(String[] vals) {
String result = "";
for (String val : vals) {
result = result + val;
}
return result;
}
바이트 코드 🙁 for
루프 발췌)
12: iload 5
14: iload 4
16: if_icmpge 51
19: aload_3
20: iload 5
22: aaload
23: astore 6
25: new #3 // class java/lang/StringBuilder
28: dup
29: invokespecial #4 // Method java/lang/StringBuilder."<init>":()V
32: aload_2
33: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: aload 6
38: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
41: invokevirtual #6 // Method java/lang/StringBuilder.toString:()Ljava/lang/String;
44: astore_2
45: iinc 5, 1
48: goto 12
stringbuilder.append를 사용하여 문자열 추가
자바:
public String myCatSb(String[] vals) {
StringBuilder sb = new StringBuilder();
for(String val : vals) {
sb.append(val);
}
return sb.toString();
}
ByteCdoe 🙁 for
루프 발췌)
17: iload 5
19: iload 4
21: if_icmpge 43
24: aload_3
25: iload 5
27: aaload
28: astore 6
30: aload_2
31: aload 6
33: invokevirtual #5 // Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
36: pop
37: iinc 5, 1
40: goto 17
43: aload_2
그래도 눈 에 띄는 차이 가 있습니다. 사용 된 첫 번째 경우에는 루프 반복마다 +
새로 StringBuilder
작성되고 생성 된 결과는 toString()
호출 (29-41)을 수행하여 저장됩니다 . 따라서 +
연산자를 for
루프로 사용하는 동안 실제로 필요하지 않은 중간 문자열을 생성하고 있습니다 .