การต่อข้อมูลสตริง: ตัวดำเนินการ concat () กับ“ +”


499

สมมติว่า String a และ b:

a += b
a = a.concat(b)

ใต้ฝากระโปรงพวกมันเหมือนกันหรือไม่?

นี่คือ concat decompiled เป็นข้อมูลอ้างอิง ฉันต้องการที่จะสามารถถอดรหัส+ผู้ประกอบการได้เช่นกัน

public String concat(String s) {

    int i = s.length();
    if (i == 0) {
        return this;
    }
    else {
        char ac[] = new char[count + i];
        getChars(0, count, ac, 0);
        s.getChars(0, i, ac, count);
        return new String(0, count + i, ac);
    }
}


3
ฉันไม่แน่ใจว่า+สามารถ decompiled
Galen Nare

1
ใช้javapเพื่อแยกไฟล์คลาส Java
Hot Licks

เนื่องจาก 'ไม่สามารถเปลี่ยนแปลงได้' คุณควรใช้StringBufferหรือStringBuilder- (เธรดไม่ปลอดภัยเร็วขึ้นแทน
Ujjwal Singh

คำตอบ:


560

ไม่เลย

ประการแรกมีความแตกต่างเล็กน้อยในความหมาย ถ้าaเป็นnullแล้วa.concat(b)พ่นNullPointerExceptionแต่a+=bจะรักษาค่าเดิมของราวกับว่ามันเป็นa nullนอกจากนี้concat()วิธีการยอมรับเฉพาะStringค่าในขณะที่+ผู้ประกอบการจะแปลงอาร์กิวเมนต์เป็นเงียบ ๆ (ใช้toString()วิธีการสำหรับวัตถุ) ดังนั้นconcat()วิธีการที่เข้มงวดมากขึ้นในสิ่งที่มันยอมรับ

เพื่อดูภายใต้ประทุนเขียนชั้นเรียนง่ายๆด้วย a += b;

public class Concat {
    String cat(String a, String b) {
        a += b;
        return a;
    }
}

ตอนนี้ถอดแยกชิ้นส่วนด้วยjavap -c(รวมอยู่ใน Sun JDK) คุณควรเห็นรายชื่อรวมถึง:

java.lang.String cat(java.lang.String, java.lang.String);
  Code:
   0:   new     #2; //class java/lang/StringBuilder
   3:   dup
   4:   invokespecial   #3; //Method java/lang/StringBuilder."<init>":()V
   7:   aload_1
   8:   invokevirtual   #4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   11:  aload_2
   12:  invokevirtual   #4; //Method java/lang/StringBuilder.append:(Ljava/lang/String;)Ljava/lang/StringBuilder;
   15:  invokevirtual   #5; //Method java/lang/StringBuilder.toString:()Ljava/lang/    String;
   18:  astore_1
   19:  aload_1
   20:  areturn

ดังนั้นจึงa += bเท่ากับ

a = new StringBuilder()
    .append(a)
    .append(b)
    .toString();

concatวิธีการควรจะเร็ว อย่างไรก็ตามด้วยStringBuilderวิธีการจำนวนมากที่ชนะอย่างน้อยในแง่ของประสิทธิภาพ

ซอร์สโค้ดของStringและStringBuilder(และคลาสฐานแพ็กเกจส่วนตัว) มีอยู่ใน src.zip ของ Sun JDK คุณจะเห็นว่าคุณกำลังสร้างขึ้นแถวถ่าน (ปรับขนาดได้ตามความจำเป็น) Stringแล้วทิ้งมันไปเมื่อคุณสร้างในขั้นตอนสุดท้าย ในทางปฏิบัติการจัดสรรหน่วยความจำนั้นรวดเร็วอย่างน่าประหลาดใจ

อัปเดต:ในฐานะที่เป็นบันทึกของ Pawel Adamski ประสิทธิภาพได้เปลี่ยนไปใน HotSpot ล่าสุด javacยังคงสร้างรหัสเดียวกันทั้งหมด แต่สูตรคอมไพเลอร์ bytecode การทดสอบอย่างง่ายล้มเหลวโดยสิ้นเชิงเนื่องจากรหัสทั้งหมดถูกโยนทิ้งไป การรวมSystem.identityHashCode(ไม่String.hashCode) แสดงStringBufferรหัสมีข้อได้เปรียบเล็กน้อย อาจมีการเปลี่ยนแปลงเมื่อมีการเปิดตัวอัพเดตครั้งถัดไปหรือหากคุณใช้ JVM อื่น จาก@lukaseder , รายการ intrinsics


4
@HyperLink คุณสามารถดูรหัสที่ใช้javap -cกับคลาสที่คอมไพล์ที่ใช้งานได้ (โอ้ในคำตอบคุณเพียงแค่ต้องตีความถอดแยกชิ้นส่วน bytecode ซึ่งไม่ควรเป็นเรื่องยาก)
Tom Hawtin - tackline

1
คุณสามารถปรึกษาข้อกำหนดJVMเพื่อทำความเข้าใจกับแต่ละไบต์ สิ่งที่คุณต้องการอ้างอิงอยู่ในบทที่ 6 เล็กน้อยคลุมเครือ แต่คุณสามารถรับส่วนสำคัญของมันได้อย่างง่ายดาย
Hot Licks

1
ฉันสงสัยว่าทำไมคอมไพเลอร์ Java ใช้StringBuilderแม้เมื่อเข้าร่วมสองสาย? หากStringรวมวิธีการแบบคงที่เพื่อต่อกันสตริงสูงสุดสี่สายหรือสตริงทั้งหมดใน a String[]โค้ดสามารถผนวกสตริงได้สูงสุดสี่รายการโดยมีการจัดสรรวัตถุสองรายการ (ผลลัพธ์Stringและการสำรองข้อมูลchar[]ไม่มีหนึ่งซ้ำซ้อน) และจำนวนสตริงใด ๆ ที่มีการจัดสรรสามรายการ ( String[]ผลStringและสนับสนุนchar[]มีเพียงความเป็นอยู่ที่ซ้ำซ้อนแรก) มันคือการใช้StringBuilderน้ำพระทัยที่ดีต้องมีสี่จัดสรรและจะต้องมีการคัดลอกตัวละครทุกตัวเป็นครั้งที่สอง
supercat

นิพจน์นั้น, a + = b ไม่ได้หมายความว่า: a = a + b?
ท่านที่เคารพมากที่สุด

3
สิ่งต่าง ๆ มีการเปลี่ยนแปลงตั้งแต่เมื่อคำตอบนี้ถูกสร้างขึ้น โปรดอ่านคำตอบของฉันร้อง
Paweł Adamski

90

Niyazถูกต้อง แต่ก็น่าสังเกตว่าตัวดำเนินการ + พิเศษสามารถแปลงเป็นสิ่งที่มีประสิทธิภาพมากขึ้นโดยคอมไพเลอร์ Java Java มีคลาส StringBuilder ซึ่งแสดงถึงสตริงที่ไม่สามารถเปลี่ยนแปลงได้และไม่ปลอดภัย เมื่อทำการรวมสตริงจำนวนมากคอมไพเลอร์ Java จะทำการแปลงอย่างเงียบ ๆ

String a = b + c + d;

เข้าไป

String a = new StringBuilder(b).append(c).append(d).toString();

ซึ่งสำหรับสตริงขนาดใหญ่นั้นมีประสิทธิภาพมากกว่า เท่าที่ฉันรู้สิ่งนี้จะไม่เกิดขึ้นเมื่อคุณใช้วิธี concat

อย่างไรก็ตามวิธีการ concat นั้นมีประสิทธิภาพมากขึ้นเมื่อทำการต่อสตริงว่างเข้ากับสตริงที่มีอยู่ ในกรณีนี้ JVM ไม่จำเป็นต้องสร้างวัตถุ String ใหม่และสามารถส่งคืนวัตถุที่มีอยู่ได้ ดูเอกสารประกอบ concatเพื่อยืนยันสิ่งนี้

ดังนั้นหากคุณกังวลอย่างยิ่งเกี่ยวกับประสิทธิภาพคุณควรใช้วิธี concat เมื่อทำการต่อสาย Strat ที่อาจว่างเปล่าแล้วใช้ + อย่างอื่น อย่างไรก็ตามความแตกต่างของประสิทธิภาพควรไม่สำคัญและคุณอาจไม่ต้องกังวลเกี่ยวกับเรื่องนี้


concat infact ไม่ได้ทำอย่างนั้น ฉันได้แก้ไขโพสต์ของฉันกับ decompilation ของวิธี concat ที่
shsteimer

10
มันจะทำ ดูบรรทัดแรกของรหัส concat ของคุณ ปัญหาของ concat คือมันจะสร้าง String ใหม่ ()
Marcio Aguiar

2
@MarcioAguiar: บางทีคุณอาจหมายถึง + ที่มักจะสร้างใหม่String- ตามที่คุณบอกว่าconcatมีหนึ่งข้อยกเว้นเมื่อคุณ concat Stringที่ว่างเปล่า
Blaisorblade

45

ฉันรันการทดสอบที่คล้ายกันเป็น @marcio แต่ใช้ลูปต่อไปนี้แทน:

String c = a;
for (long i = 0; i < 100000L; i++) {
    c = c.concat(b); // make sure javac cannot skip the loop
    // using c += b for the alternative
}

สำหรับการวัดที่ดีฉันก็ขว้างด้วยStringBuilder.append()เช่นกัน การทดสอบแต่ละครั้งรัน 10 ครั้งโดยมี 100 reps สำหรับการทดสอบแต่ละครั้ง นี่คือผลลัพธ์:

  • StringBuilderชนะมือลง ผลลัพธ์เวลานาฬิกาเป็น 0 สำหรับการวิ่งส่วนใหญ่และที่ยาวที่สุดใช้เวลา 16ms
  • a += b ใช้เวลาประมาณ 40000ms (40 วินาที) สำหรับการวิ่งแต่ละครั้ง
  • concat ต้องการเพียง 10,000 มิลลิวินาที (10 วินาที) ต่อการวิ่งหนึ่งครั้ง

ฉันไม่ได้ decompiled ชั้นเรียนเพื่อดู internals หรือเรียกใช้ผ่าน Profiler แต่ฉันสงสัยว่าa += bใช้เวลามากเวลาสร้างวัตถุใหม่แล้วแปลงให้กลับไปStringBuilderString


4
เวลาสร้างวัตถุมีความสำคัญจริงๆ นั่นเป็นเหตุผลในหลาย ๆ สถานการณ์ที่เราใช้ StringBuilder โดยตรงแทนที่จะใช้ประโยชน์จาก StringBuilder ที่อยู่ด้านหลัง +
coolcfan

1
@coolcfan: เมื่อ+ใช้สำหรับสองสายมีกรณีใดบ้างที่ใช้StringBuilderดีกว่าจะเป็นString.valueOf(s1).concat(s2)อย่างไร ความคิดใดที่ว่าทำไมคอมไพเลอร์จะไม่ใช้คำสั่งหลัง [หรือมิฉะนั้นจะไม่รับvalueOfสายในกรณีที่s1ทราบว่าไม่เป็นโมฆะ]?
supercat

1
@supercat ขอโทษฉันไม่รู้ บางทีคนที่อยู่เบื้องหลังน้ำตาลนี้อาจเป็นคนที่ดีที่สุดที่จะตอบคำถามนี้
coolcfan

25

คำตอบส่วนใหญ่มาจาก 2008 ดูเหมือนว่าสิ่งต่าง ๆ มีการเปลี่ยนแปลงตลอดเวลา มาตรฐานล่าสุดของฉันทำกับการแสดง JMH ว่าเมื่อวันที่ 8 Java เป็นรอบสองครั้งเร็วกว่า+concat

มาตรฐานของฉัน:

@Warmup(iterations = 5, time = 200, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 5, time = 200, timeUnit = TimeUnit.MILLISECONDS)
public class StringConcatenation {

    @org.openjdk.jmh.annotations.State(Scope.Thread)
    public static class State2 {
        public String a = "abc";
        public String b = "xyz";
    }

    @org.openjdk.jmh.annotations.State(Scope.Thread)
    public static class State3 {
        public String a = "abc";
        public String b = "xyz";
        public String c = "123";
    }


    @org.openjdk.jmh.annotations.State(Scope.Thread)
    public static class State4 {
        public String a = "abc";
        public String b = "xyz";
        public String c = "123";
        public String d = "!@#";
    }

    @Benchmark
    public void plus_2(State2 state, Blackhole blackhole) {
        blackhole.consume(state.a+state.b);
    }

    @Benchmark
    public void plus_3(State3 state, Blackhole blackhole) {
        blackhole.consume(state.a+state.b+state.c);
    }

    @Benchmark
    public void plus_4(State4 state, Blackhole blackhole) {
        blackhole.consume(state.a+state.b+state.c+state.d);
    }

    @Benchmark
    public void stringbuilder_2(State2 state, Blackhole blackhole) {
        blackhole.consume(new StringBuilder().append(state.a).append(state.b).toString());
    }

    @Benchmark
    public void stringbuilder_3(State3 state, Blackhole blackhole) {
        blackhole.consume(new StringBuilder().append(state.a).append(state.b).append(state.c).toString());
    }

    @Benchmark
    public void stringbuilder_4(State4 state, Blackhole blackhole) {
        blackhole.consume(new StringBuilder().append(state.a).append(state.b).append(state.c).append(state.d).toString());
    }

    @Benchmark
    public void concat_2(State2 state, Blackhole blackhole) {
        blackhole.consume(state.a.concat(state.b));
    }

    @Benchmark
    public void concat_3(State3 state, Blackhole blackhole) {
        blackhole.consume(state.a.concat(state.b.concat(state.c)));
    }


    @Benchmark
    public void concat_4(State4 state, Blackhole blackhole) {
        blackhole.consume(state.a.concat(state.b.concat(state.c.concat(state.d))));
    }
}

ผล:

Benchmark                             Mode  Cnt         Score         Error  Units
StringConcatenation.concat_2         thrpt   50  24908871.258 ± 1011269.986  ops/s
StringConcatenation.concat_3         thrpt   50  14228193.918 ±  466892.616  ops/s
StringConcatenation.concat_4         thrpt   50   9845069.776 ±  350532.591  ops/s
StringConcatenation.plus_2           thrpt   50  38999662.292 ± 8107397.316  ops/s
StringConcatenation.plus_3           thrpt   50  34985722.222 ± 5442660.250  ops/s
StringConcatenation.plus_4           thrpt   50  31910376.337 ± 2861001.162  ops/s
StringConcatenation.stringbuilder_2  thrpt   50  40472888.230 ± 9011210.632  ops/s
StringConcatenation.stringbuilder_3  thrpt   50  33902151.616 ± 5449026.680  ops/s
StringConcatenation.stringbuilder_4  thrpt   50  29220479.267 ± 3435315.681  ops/s

ผมสงสัยว่าทำไม Java ไม่รวมถึงฟังก์ชั่นแบบคงที่ในรูปแบบสตริงโดยเชื่อมโยงองค์ประกอบของที่String String[]โดยใช้+การเชื่อม 8 สตริงโดยใช้ฟังก์ชั่นดังกล่าวจะต้องมีการสร้างและต่อมาทิ้งString[8]แต่ที่จะเป็นวัตถุเดียวที่จะต้องมีการสร้างที่ถูกทอดทิ้งในขณะที่ใช้StringBuilderจะต้องมีการสร้างและการทิ้งStringBuilderเช่นและอย่างน้อยหนึ่งในchar[]การจัดเก็บสำรองข้อมูล
supercat

@supercat String.join()วิธีการคงที่บางส่วนถูกเพิ่มเข้ามาใน Java 8 เนื่องจากมีการล้อมรอบไวยากรณ์ของjava.util.StringJoinerคลาสอย่างรวดเร็ว
Ti Strga

@TiStrga: มีการจัดการการ+เปลี่ยนแปลงที่จะใช้ฟังก์ชั่นดังกล่าวหรือไม่?
supercat

@supercat ที่จะทำลายความเข้ากันได้แบบไบนารีย้อนหลังดังนั้นไม่ มันเป็นเพียงในการตอบกลับของคุณ "ทำไมสตริงไม่รวมถึงฟังก์ชั่นแบบคงที่" ความคิดเห็น: ตอนนี้มีเป็นเช่นฟังก์ชั่น ส่วนที่เหลือของข้อเสนอของคุณ (การเปลี่ยน+การใช้งานใหม่) จะต้องการมากกว่าสิ่งที่ Java devs เต็มใจที่จะเปลี่ยนแปลงเศร้า
Ti Strga

@TiStrga: มีวิธีใดที่ไฟล์ Java bytecode สามารถระบุ "ถ้า function X พร้อมใช้งานให้เรียกมันหรือทำอย่างอื่น" ในวิธีที่สามารถแก้ไขได้ในระหว่างการโหลดคลาส? การสร้างโค้ดด้วยวิธีสแตติกที่สามารถโยงกับวิธีสแตติกของ Java หรือใช้ตัวสร้างสตริงหากไม่สามารถใช้งานได้ดูเหมือนว่าจะเป็นทางออกที่ดีที่สุด
supercat

22

ทอมถูกต้องในการอธิบายสิ่งที่ผู้ประกอบการ + ทำ มันจะสร้างชั่วคราวStringBuilder, toString()ผนวกชิ้นส่วนและเสร็จสิ้นด้วย

อย่างไรก็ตามคำตอบทั้งหมดจนถึงขณะนี้ไม่สนใจผลของการปรับให้เหมาะสมแบบ HotSpot โดยเฉพาะการดำเนินการชั่วคราวเหล่านี้ได้รับการยอมรับว่าเป็นรูปแบบทั่วไปและถูกแทนที่ด้วยรหัสเครื่องที่มีประสิทธิภาพมากขึ้นในเวลาทำงาน

@marcio: คุณได้สร้างเกณฑ์มาตรฐานขนาดเล็กแล้ว ด้วย JVM ที่ทันสมัยนี่ไม่ใช่วิธีที่ถูกต้องในรหัสโปรไฟล์

เหตุผลในการปรับแต่งให้เหมาะสมนั้นสำคัญมากเพราะความแตกต่างของรหัส - รวมถึงการสร้างวัตถุ - นั้นแตกต่างกันอย่างสมบูรณ์เมื่อ HotSpot เริ่มทำงาน วิธีเดียวที่จะทราบว่าเป็น profiling รหัสของคุณในแหล่งกำเนิด

ในที่สุดวิธีการทั้งหมดเหล่านี้ในความเป็นจริงอย่างรวดเร็วอย่างไม่น่าเชื่อ นี่อาจเป็นกรณีของการปรับให้เหมาะสมก่อนเวลาอันควร หากคุณมีรหัสที่เชื่อมโยงสตริงเป็นจำนวนมากวิธีการรับความเร็วสูงสุดอาจไม่เกี่ยวข้องกับโอเปอเรเตอร์ที่คุณเลือกและอัลกอริทึมที่คุณใช้แทนการใช้!


ฉันเดาโดย "การดำเนินการชั่วคราวเหล่านี้" คุณหมายถึงการใช้การวิเคราะห์การหลบหนีเพื่อจัดสรรวัตถุ "heap" บนสแต็กที่สามารถแก้ไขได้ แม้ว่าการวิเคราะห์การหลบหนีจะมีอยู่ใน HotSpot (มีประโยชน์สำหรับการลบการซิงโครไนซ์บางอย่าง) แต่ฉันไม่เชื่อว่ามันเป็นเวลาของการเขียน u
Tom Hawtin - tackline

21

วิธีการเกี่ยวกับการทดสอบง่ายๆ? ใช้รหัสด้านล่าง:

long start = System.currentTimeMillis();

String a = "a";

String b = "b";

for (int i = 0; i < 10000000; i++) { //ten million times
     String c = a.concat(b);
}

long end = System.currentTimeMillis();

System.out.println(end - start);
  • "a + b"รุ่นดำเนินการใน2500ms
  • a.concat(b)ดำเนินการใน1200ms

ผ่านการทดสอบหลายครั้ง การconcat()ดำเนินการเวอร์ชันใช้เวลาครึ่งหนึ่งโดยเฉลี่ย

ผลลัพธ์นี้ทำให้ฉันประหลาดใจเพราะconcat()วิธีการสร้างสตริงใหม่เสมอ (มันคืนค่า " new String(result)" เป็นที่รู้จักกันดีว่า:

String a = new String("a") // more than 20 times slower than String a = "a"

ทำไมคอมไพเลอร์ไม่สามารถเพิ่มประสิทธิภาพการสร้างสตริงในรหัส "a + b" โดยรู้ว่ามันส่งผลให้เกิดสตริงเดียวกันเสมอ? มันสามารถหลีกเลี่ยงการสร้างสตริงใหม่ หากคุณไม่เชื่อข้อความข้างต้นลองทดสอบด้วยตนเอง


ฉันทดสอบกับ java jdk1.8.0_241 รหัสของคุณสำหรับฉันรหัส "a + b" คือการให้ผลลัพธ์ที่ดีที่สุด ด้วย concat (): 203msและ "+": 113ms ฉันเดาว่าในรุ่นก่อนหน้านี้มันไม่ได้ปรับให้เหมาะสม
Akki

6

โดยทั่วไปมีความแตกต่างที่สำคัญสองประการระหว่าง + และconcatวิธีการ

  1. หากคุณกำลังใช้เมธอดconcatคุณจะสามารถเชื่อมสตริงได้เฉพาะในกรณีของตัวดำเนินการ+คุณสามารถต่อสตริงกับชนิดข้อมูลใด ๆ ได้

    ตัวอย่างเช่น:

    String s = 10 + "Hello";

    ในกรณีนี้การส่งออกควรจะ10Hello

    String s = "I";
    String s1 = s.concat("am").concat("good").concat("boy");
    System.out.println(s1);

    ในกรณีข้างต้นคุณจะต้องบังคับให้มีสองสาย

  2. ความแตกต่างที่สองและหลักระหว่าง+กับconcatคือ:

    กรณีที่ 1: สมมติว่าฉัน concat สายเดียวกันกับconcatผู้ประกอบการในลักษณะนี้

    String s="I";
    String s1=s.concat("am").concat("good").concat("boy");
    System.out.println(s1);

    ในกรณีนี้จำนวนวัตถุทั้งหมดที่สร้างขึ้นในพูลเท่ากับ 7 ดังนี้:

    I
    am
    good
    boy
    Iam
    Iamgood
    Iamgoodboy

    กรณีที่ 2:

    ตอนนี้ฉันจะทำการคอมไพน์สตริงเดียวกันผ่านตัวดำเนินการ+

    String s="I"+"am"+"good"+"boy";
    System.out.println(s);

    ในกรณีข้างต้นจำนวนวัตถุทั้งหมดที่สร้างเป็นเพียง 5

    ที่จริงแล้วเมื่อเราเชื่อมสตริงด้วยตัวดำเนินการ+มันจะรักษาคลาส StringBuffer เพื่อทำงานเดียวกันดังนี้: -

    StringBuffer sb = new StringBuffer("I");
    sb.append("am");
    sb.append("good");
    sb.append("boy");
    System.out.println(sb);

    ด้วยวิธีนี้มันจะสร้างวัตถุห้าชิ้นเท่านั้น

ดังนั้นคนเหล่านี้คือความแตกต่างขั้นพื้นฐานระหว่าง+และconcatวิธี สนุก :)


ที่รักของฉันคุณรู้ดีว่าสตริงตัวอักษรใด ๆ ถือว่าเป็นวัตถุ String ตัวเองซึ่งเก็บไว้ใน String pool ดังนั้นในกรณีนี้เรามีตัวอักษรสตริงที่ 4 ดังนั้นเห็นได้ชัดว่าอย่างน้อย 4 วัตถุควรจะสร้างในสระว่ายน้ำ
Deepak Sharma

1
ฉันไม่คิดอย่างนั้น: String s="I"+"am"+"good"+"boy"; String s2 = "go".concat("od"); System.out.println(s2 == s2.intern());พิมพ์trueซึ่งหมายความ"good"ว่าไม่ได้อยู่ในกลุ่มสตริงก่อนโทรintern()
fabian

ฉันกำลังพูดถึงบรรทัดนี้เท่านั้นสตริง s = "ฉัน" + "ฉัน" + "ดี" + "เด็ก"; ในกรณีนี้ทั้ง 4 ตัวเป็นสตริงตัวอักษรจะถูกเก็บไว้ในพูลวัตถุ 4 รายการควรถูกสร้างในพูล
Deepak Sharma

4

เพื่อความสมบูรณ์ฉันต้องการเพิ่มว่านิยามของตัวดำเนินการ '+' สามารถพบได้ในJLS SE8 15.18.1 :

หากมีเพียงหนึ่งนิพจน์ตัวถูกดำเนินการเป็นประเภทสตริงดังนั้นการแปลงสตริง (.15.1.11) จะถูกดำเนินการในตัวถูกดำเนินการอื่นเพื่อสร้างสตริงในเวลาทำงาน

ผลลัพธ์ของการต่อข้อมูลสตริงเป็นการอ้างอิงไปยังวัตถุ String ที่เป็นการต่อข้อมูลของสตริงตัวถูกดำเนินการสองตัว อักขระของตัวถูกดำเนินการทางซ้ายนำหน้าอักขระของตัวถูกดำเนินการทางขวาในสตริงที่สร้างขึ้นใหม่

วัตถุสตริงถูกสร้างขึ้นใหม่ (§12.5) เว้นแต่ว่าการแสดงออกเป็นแสดงออกคงที่ (§15.28)

เกี่ยวกับการดำเนินการ JLS กล่าวว่าต่อไปนี้:

การนำไปใช้อาจเลือกที่จะทำการแปลงและการต่อข้อมูลในขั้นตอนเดียวเพื่อหลีกเลี่ยงการสร้างแล้วละทิ้งวัตถุสตริงกลาง เพื่อเพิ่มประสิทธิภาพของการต่อสตริงซ้ำซ้ำคอมไพเลอร์ Java อาจใช้คลาส StringBuffer หรือเทคนิคที่คล้ายกันเพื่อลดจำนวนของวัตถุสตริงกลางที่สร้างขึ้นโดยการประเมินผลของการแสดงออก

สำหรับชนิดดั้งเดิมการใช้งานอาจปรับการสร้างวัตถุห่อหุ้มให้เหมาะสมโดยการแปลงจากชนิดดั้งเดิมเป็นสตริงโดยตรง

ดังนั้นการตัดสินจาก 'คอมไพเลอร์ Java อาจใช้คลาส StringBuffer หรือเทคนิคที่คล้ายกันเพื่อลด' คอมไพเลอร์ต่าง ๆ สามารถสร้างโค้ดไบต์ที่แตกต่างกัน


2

ตัวดำเนินการ +สามารถทำงานระหว่างสตริงและสตริง, อักขระถ่าน, จำนวนเต็ม, สองเท่าหรือค่าชนิดข้อมูลลอย มันเพียงแปลงค่าให้เป็นตัวแทนสตริงก่อนที่จะต่อกัน

ตัวดำเนินการ concatสามารถทำได้บนและกับสตริงเท่านั้น ตรวจสอบความเข้ากันได้ของชนิดข้อมูลและส่งข้อผิดพลาดหากไม่ตรงกัน

ยกเว้นสิ่งนี้รหัสที่คุณระบุจะทำสิ่งเดียวกัน


2

ฉันไม่คิดอย่างนั้น

a.concat(b)มีการใช้งานใน String และฉันคิดว่าการใช้งานไม่ได้เปลี่ยนแปลงไปมากนักตั้งแต่ต้นจาวาเครื่อง +การดำเนินการดำเนินการขึ้นอยู่กับรุ่น Java และคอมไพเลอร์ ปัจจุบัน+มีการใช้งานStringBufferเพื่อให้การดำเนินการเป็นไปอย่างรวดเร็วที่สุด บางทีในอนาคตอาจมีการเปลี่ยนแปลง ใน java เวอร์ชันก่อนหน้า+การทำงานบน Strings นั้นช้ากว่ามากเนื่องจากมันให้ผลลัพธ์ระดับกลาง

ฉันเดาว่า+=มีการใช้งาน+และปรับให้เหมาะสมเช่นเดียวกัน


7
"มีการใช้งาน + ในปัจจุบันโดยใช้ StringBuffer" False มันเป็น StringBuilder StringBuffer เป็นความหมายของ threadsafe ของ StringBuilder
Frederic Morin

1
มันเคยเป็น StringBuffer ก่อนจาวา 1.5 ซึ่งเป็นรุ่นที่เมื่อมีการเปิดตัว StringBuilder เป็นครั้งแรก
ccpizza

0

เมื่อใช้ + ความเร็วจะลดลงเมื่อความยาวของสตริงเพิ่มขึ้น แต่เมื่อใช้ concat ความเร็วจะเสถียรมากขึ้นและตัวเลือกที่ดีที่สุดคือการใช้คลาส StringBuilder ซึ่งมีความเร็วคงที่เพื่อทำเช่นนั้น

ฉันเดาว่าคุณสามารถเข้าใจว่าทำไม แต่วิธีที่ดีที่สุดในการสร้างสตริงแบบยาวคือการใช้ StringBuilder () และผนวก () ความเร็วจะไม่สามารถยอมรับได้


1
ใช้ตัวดำเนินการ + เทียบเท่ากับการใช้ StringBuilder ( docs.oracle.com/javase/specs/jls/se8/html/… )
ihebiheb
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.