ฉันเชื่อว่าทุกคนมีความเข้าใจที่ดีถึงความแตกต่างระหว่าง String และ StringBuffer แต่คาดว่ายังมีสหายหลายคนที่ไม่ชัดเจนเกี่ยวกับหลักการทำงานของทั้งสองคลาส วันนี้ฉันจะทบทวนแนวคิดนี้สำหรับทุกคนและโดย วิธี มีคลาสการดำเนินการอักขระใหม่ที่แนะนำใน J2SE 5.0 - StringBuilder แล้วอะไรคือความแตกต่างระหว่าง StringBuilder และ StringBuffer และคลาส String ที่เราพบกันครั้งแรก? เราควรใช้อันไหนในสถานการณ์ที่แตกต่างกัน? ฉันอยากจะแบ่งปันความคิดเห็นของฉันเกี่ยวกับหมวดหมู่เหล่านี้ และฉันก็หวังว่าทุกคนจะสามารถแสดงความคิดเห็นได้ ทุกคนเคยทำผิดพลาด และในขณะที่แก้ไขให้ถูกต้อง มันก็เป็นโอกาสที่ดีที่จะเรียนรู้
กล่าวโดยย่อ ความแตกต่างด้านประสิทธิภาพหลักระหว่างประเภท String และประเภท StringBuffer ก็คือ แท้จริงแล้ว String เป็นอ็อบเจ็กต์ที่ไม่เปลี่ยนรูป (ทำไม? ถามนักออกแบบของ Java เหตุใด String จึงไม่ใช่ประเภทเนทิฟ) ดังนั้น ทุกครั้งที่มีการเปลี่ยนแปลงประเภท String ที่จริงแล้วจะเทียบเท่ากับการสร้างออบเจ็กต์ String ใหม่ จากนั้นชี้ตัวชี้ไปที่ออบเจ็กต์ String ใหม่ ดังนั้นจึงเป็นการดีที่สุดที่จะไม่ใช้ String สำหรับสตริงที่มักจะเปลี่ยนเนื้อหา เพราะทุกครั้งที่มีการสร้างอ็อบเจ็กต์ มันจะส่งผลกระทบต่อประสิทธิภาพของระบบ โดยเฉพาะอย่างยิ่งเมื่อมีอ็อบเจ็กต์ที่ไม่ได้อ้างอิงมากเกินไปในหน่วยความจำ GC ของ JVM จะเริ่มทำงาน และความเร็วจะค่อนข้างช้าอย่างแน่นอน นี่คือตัวอย่างที่ไม่เหมาะสมอย่างยิ่ง:
หากเป็นกรณีนี้หลังจาก for loop เสร็จสิ้น หากวัตถุในหน่วยความจำยังไม่ถูกล้างโดย GC ก็จะมีหน่วยความจำมากกว่า 20,000 รายการในหน่วยความจำเป็นตัวเลขที่น่าประหลาดใจและหากเป็นระบบที่หลาย ๆ คนใช้ คนแล้วจำนวนไม่มากดังนั้นทุกคนจึงต้องระมัดระวังในการใช้งาน
หากคุณใช้คลาส StringBuffer ผลลัพธ์จะแตกต่างออกไป แต่ละครั้งผลลัพธ์จะเป็นการดำเนินการบนออบเจ็กต์ StringBuffer เอง แทนที่จะสร้างออบเจ็กต์ใหม่แล้วเปลี่ยนการอ้างอิงออบเจ็กต์ โดยทั่วไปแล้ว เราขอแนะนำให้ใช้ StringBuffer โดยเฉพาะอย่างยิ่งเมื่อวัตถุสตริงมีการเปลี่ยนแปลงบ่อยครั้ง ในบางกรณีพิเศษ การต่อสตริงของอ็อบเจ็กต์ String จะถูกตีความโดย JVM ว่าเป็นการต่อกันของอ็อบเจ็กต์ StringBuffer ดังนั้นในกรณีเหล่านี้ ความเร็วของอ็อบเจ็กต์ String จะไม่ช้ากว่าอ็อบเจ็กต์ StringBuffer และโดยเฉพาะอ็อบเจ็กต์สตริงต่อไปนี้ สร้างขึ้นในหมู่พวกเขาประสิทธิภาพของสตริงนั้นเร็วกว่า StringBuffer มาก:
คุณจะประหลาดใจที่พบว่าความเร็วในการสร้างออบเจ็กต์ String S1 นั้นเร็วเกินไป และในเวลานี้ StringBuffer ไม่มีข้อได้เปรียบในเรื่องความเร็วเลย อันที่จริงนี่เป็นเคล็ดลับของ JVM ในสายตาของ JVM
จากนี้เราจะได้ข้อสรุปขั้นตอนแรก: ในกรณีส่วนใหญ่ StringBuffer > String
และ StringBuilder เปรียบเทียบกับพวกมันได้อย่างไร? ให้ฉันแนะนำสั้น ๆ ก่อน StringBuilder เป็นคลาสที่เพิ่มใหม่ใน JDK5.0 ความแตกต่างระหว่างมันกับ StringBuffer มีดังนี้ (ที่มา: JavaWorld):
Java.lang.StringBuffer ลำดับอักขระที่ไม่แน่นอนของเธรด บัฟเฟอร์สตริงคล้ายกับ String แต่ไม่สามารถแก้ไขได้ บัฟเฟอร์สตริงสามารถใช้ได้อย่างปลอดภัยโดยหลายเธรด วิธีการเหล่านี้สามารถซิงโครไนซ์ได้เมื่อจำเป็น เพื่อให้การดำเนินการทั้งหมดบนอินสแตนซ์ใด ๆ ปรากฏขึ้นตามลำดับอนุกรมที่สอดคล้องกับลำดับการเรียกเมธอดที่ทำโดยแต่ละเธรดที่เกี่ยวข้อง
บัฟเฟอร์สตริงแต่ละตัวมีความจุที่แน่นอน ตราบใดที่ความยาวของลำดับอักขระที่มีอยู่ในบัฟเฟอร์สตริงไม่เกินความจุนี้ ก็ไม่จำเป็นต้องจัดสรรอาร์เรย์บัฟเฟอร์ภายในใหม่ ความจุนี้จะเพิ่มขึ้นโดยอัตโนมัติหากบัฟเฟอร์ภายในล้น เริ่มต้นจาก JDK 5.0 ซึ่งเป็นคลาสที่เทียบเท่าสำหรับการใช้เธรดเดี่ยว StringBuilder ได้ถูกเพิ่มให้กับคลาสนี้ โดยทั่วไปควรใช้คลาส StringBuilder ในการกำหนดค่าตามความชอบสำหรับคลาสนี้ เนื่องจากคลาสนี้สนับสนุนการดำเนินการเดียวกันทั้งหมด แต่จะเร็วกว่าเนื่องจากไม่ได้ทำการซิงโครไนซ์
แต่ไม่ปลอดภัยที่จะใช้อินสแตนซ์ของ StringBuilder กับหลายเธรด หากจำเป็นต้องมีการซิงโครไนซ์ ขอแนะนำให้ใช้ StringBuffer
จากที่กล่าวไป ฉันคิดว่าทุกคนสามารถเข้าใจความแตกต่างระหว่างพวกเขาได้ ดังนั้นเรามาสร้างความแตกต่างโดยทั่วไปด้านล่าง:
ในกรณีส่วนใหญ่ StringBuilder > StringBuffer
ดังนั้น ตามทฤษฎีบทสกรรมกริยาของอสมการนี้: ในกรณีส่วนใหญ่ StringBuilder > StringBuffer > String
ตอนนี้เราได้ผลการได้มาดังกล่าวแล้ว เรามาทำการทดสอบเพื่อตรวจสอบกัน:
รหัสทดสอบมีดังนี้:
/** สร้างอินสแตนซ์ใหม่ของ testsb */
int ttime คงที่สุดท้าย = 10,000; // จำนวนลูปทดสอบ
การทดสอบสาธารณะ () {
-
การทดสอบโมฆะสาธารณะ (สตริง s) {
เริ่มต้นนาน = System.currentTimeMillis();
สำหรับ(int i=0;i<ttime;i++){
s += "เพิ่ม";
-
นานเกิน = System.currentTimeMillis();
System.out.println("เวลาที่ใช้โดยการดำเนินการ "+s.getClass().getName()+" คือ: " + (over - start) + " มิลลิวินาที" );
-
การทดสอบโมฆะสาธารณะ (StringBuffer s) {
เริ่มต้นนาน = System.currentTimeMillis();
สำหรับ(int i=0;i<ttime;i++){
s.ผนวก("เพิ่ม");
-
นานเกิน = System.currentTimeMillis();
System.out.println("เวลาที่ใช้โดยการดำเนินการ "+s.getClass().getName()+" คือ: " + (over - start) + " มิลลิวินาที" );
-
การทดสอบโมฆะสาธารณะ (StringBuilder s) {
เริ่มต้นนาน = System.currentTimeMillis();
สำหรับ(int i=0;i<ttime;i++){
s.ผนวก("เพิ่ม");
-
นานเกิน = System.currentTimeMillis();
System.out.println("เวลาที่ใช้โดยการดำเนินการ "+s.getClass().getName()+" คือ: " + (over - start) + " มิลลิวินาที" );
-
// ทดสอบการต่อสตริงโดยตรงบน String
โมฆะสาธารณะ test2(){
สตริง s2 = "abadf";
เริ่มต้นนาน = System.currentTimeMillis();
สำหรับ(int i=0;i<ttime;i++){
สตริง s = s2 + s2 + s2 ;
-
นานเกิน = System.currentTimeMillis();
System.out.println("เวลาที่ใช้ในการดำเนินการประเภทการเพิ่มการอ้างอิงวัตถุสตริงคือ: " + (เกิน - เริ่มต้น) + " มิลลิวินาที" );
-
โมฆะสาธารณะ test3(){
เริ่มต้นนาน = System.currentTimeMillis();
สำหรับ(int i=0;i<ttime;i++){
สตริง s = "abadf" + "abadf" + "abadf" ;
-
นานเกิน = System.currentTimeMillis();
System.out.println("เวลาที่ใช้ในการเพิ่มสตริงการดำเนินการคือ: "+ (เกิน - เริ่มต้น) + " มิลลิวินาที" );
-
โมฆะคงที่สาธารณะ main (String [] args) {
สตริง s1="abc";
StringBuffer sb1 = StringBuffer ใหม่ ("abc");
StringBuilder sb2 = StringBuilder ใหม่ ("abc");
testsb t = ใหม่ testssb();
t.ทดสอบ(s1);
t.ทดสอบ(sb1);
t.ทดสอบ(sb2);
t.test2();
t.test3();
-
-
ดูเหมือนว่าคุณยังไม่เห็นความแตกต่างระหว่าง StringBuffer และ StringBuilder เพิ่ม ttime เป็น 30,000 ครั้งและดู:
เวลาที่ใช้ในการดำเนินการประเภท java.lang.String คือ: 53444 มิลลิวินาที เวลาที่ใช้ในการดำเนินการประเภท java.lang.StringBuffer คือ: 15 มิลลิวินาที เวลาที่ใช้ในการดำเนินการประเภท java.lang.StringBuilder คือ: 15 มิลลิวินาที เวลา ใช้เพื่อดำเนินการประเภทการเพิ่มการอ้างอิงวัตถุสตริง เวลาที่ใช้: 31 มิลลิวินาที ใช้เวลาในการเพิ่มสตริง: 0 มิลลิวินาที
ประสิทธิภาพระหว่าง StringBuffer และ StringBuilder ยังคงไม่แตกต่างกันมากนัก ลองเพิ่มเป็น 100,000 แล้วลองดู เราจะไม่เพิ่มการทดสอบสำหรับประเภท String ที่นี่ เนื่องจากการทดสอบข้อมูลจำนวนมากสำหรับประเภท String จะเป็นเช่นนั้น ช้ามาก...
เวลาที่ใช้ในการดำเนินการประเภท java.lang.StringBuffer คือ: 31 มิลลิวินาที เวลาที่ใช้ในการดำเนินการประเภท java.lang.StringBuilder คือ: 16 มิลลิวินาที
คุณสามารถเห็นความแตกต่าง แต่ผลการทดสอบหลายรายการแสดงให้เห็นว่า StringBuffer เร็วกว่า StringBuilder เรามาเพิ่มค่าเป็น 1000000 กันดีกว่า (ไม่น่าจะพังใช่ไหม):
เวลาที่ใช้ในการดำเนินการประเภท java.lang.StringBuffer คือ: 265 มิลลิวินาที เวลาที่ใช้ในการดำเนินการประเภท java.lang.StringBuilder คือ: 219 มิลลิวินาที
มีความแตกต่างน้อยกว่า และผลลัพธ์ก็มีเสถียรภาพมาก ลองดูให้มากขึ้นหน่อย ttime = 5000000:
・・・・・・ ข้อยกเว้นในเธรด "main" java.lang.OutOfMemoryError: พื้นที่ฮีป Java ・・・・・・
ฮ่าๆ ลืมไปเถอะ ฉันจะไม่ทดสอบมันอีกต่อไป โดยพื้นฐานแล้ว ประสิทธิภาพคือ StringBuilder > StringBuffer > String