ฉันควรคัดลอก Strings ใน Java ได้อย่างไร


199
    String s = "hello";
    String backup_of_s = s;
    s = "bye";

ณ จุดนี้ตัวแปรสำรองยังคงมีค่าเดิม "hello" (นี่เป็นเพราะการเปลี่ยนรูปแบบของสตริงใช่ไหม?)

แต่มันปลอดภัยที่จะคัดลอก Strings ด้วยวิธีนี้ (ซึ่งแน่นอนว่าไม่ปลอดภัยในการคัดลอกวัตถุที่ไม่แน่นอนปกติ) หรือดีกว่าที่จะเขียนสิ่งนี้? :

    String s = "hello";
    String backup_of_s = new String(s);
    s = "bye";

กล่าวอีกนัยหนึ่งความแตกต่าง (ถ้ามี) ระหว่างตัวอย่างสองรายการนี้คืออะไร


แก้ไข - สาเหตุที่ข้อมูลโค้ดแรกปลอดภัย:

ให้ฉันอธิบายสิ่งต่าง ๆ โดยมีรายละเอียดเล็ก ๆ น้อย ๆ ตามคำตอบที่ดีที่ได้ให้ไปแล้ว (ซึ่งมุ่งเน้นไปที่คำถามของความแตกต่างของประสิทธิภาพระหว่าง 2 ตัวอย่าง)

สตริงไม่เปลี่ยนรูปใน Java ซึ่งหมายความว่าวัตถุ String ไม่สามารถแก้ไขได้หลังจากการก่อสร้าง ดังนั้น

String s = "hello";สร้างอินสแตนซ์ของสตริงใหม่และกำหนดที่อยู่ให้กับs( sเป็นการอ้างอิงไปยังอินสแตนซ์ / วัตถุ)

String backup_of_s = s;สร้างตัวแปรใหม่และเริ่มต้นเพื่อที่จะอ้างอิงวัตถุที่อ้างถึงในขณะนี้โดยbackup_of_ss

หมายเหตุ: สตริงที่ไม่สามารถเปลี่ยนแปลงได้รับประกันว่าวัตถุนี้จะไม่ถูกแก้ไข: การสำรองข้อมูลของเราปลอดภัย

หมายเหตุ 2: กลไกการรวบรวมขยะ Java รับประกันว่าวัตถุนี้จะไม่ถูกทำลายตราบใดที่มีการอ้างอิงโดยตัวแปรอย่างน้อยหนึ่งตัว ( backup_of_sในกรณีนี้)

ในที่สุดs = "bye";สร้างอินสแตนซ์ของสตริงอื่น (เนื่องจากความไม่สามารถเปลี่ยนได้มันเป็นวิธีเดียว) และปรับเปลี่ยนsตัวแปรเพื่อให้มันอ้างอิงวัตถุใหม่ในขณะนี้

คำตอบ:


141

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

เมื่อคำนึงถึงสิ่งนี้ควรเลือกรุ่นแรก


15
ความไม่สามารถเปลี่ยนแปลงได้ไม่มีอะไรเกี่ยวข้องกับมัน มันเป็นเพียงการอ้างอิงวัตถุทำงานอย่างไร ฉันสามารถให้ตัวอย่าง StringBuilder ที่เทียบเท่าได้
GriffeyDog

1
@BalusC ฉันไม่สามารถดูว่า String ใหม่ () สามารถสร้างสิ่งใดใน String Pool ของ JVM ได้ เฉพาะตัวกรองสตริงและตัวที่คอมมิตให้กับพูลผ่าน intern () อยู่ในพูล
Snicolas

3
@GriffeyDog: ฉันกำลังอ่านคำถามน้อยลงอย่างแท้จริง สิ่งที่ฉันพูดคือปลอดภัยที่จะให้การอ้างอิงถึงวัตถุสตริงโดยไม่ต้องกลัวว่าใครบางคนอาจแก้ไขสตริง
NPE

2
@GriffeyDog ฉันพบความคิดเห็นของคุณที่ทำให้เข้าใจผิดมาก: การไม่เปลี่ยนรูปเป็นสิ่งที่ทำให้ตัวอย่างข้อมูลแรกปลอดภัยทำไมคุณถึงบอกว่ามันไม่มีส่วนเกี่ยวข้องกับ "มัน"?
Sébastien

5
@Sebastien สิ่งที่ทำก็คือการกำหนดตัวแปรอ้างอิงอีกครั้งsเพื่ออ้างถึงวัตถุอื่น ( String"bye") ไม่ส่งผลต่อตัวแปรอ้างอิงที่อ้างอิงbackup_of_sถึง ( String"สวัสดี") อย่างที่ฉันพูดฉันสามารถยกตัวอย่างที่เทียบเท่ากับStringBuilders ซึ่งไม่เปลี่ยนรูปได้ ความคิดเห็นของฉันส่วนใหญ่เกี่ยวกับคำสั่ง OPs: ณ จุดนี้ตัวแปรสำรองยังคงมีค่าเดิม "hello" (นี่เป็นเพราะการเปลี่ยนไม่ได้ของ String ใช่ไหม?)
GriffeyDog

22

สตริงเป็นวัตถุที่ไม่เปลี่ยนรูปดังนั้นคุณสามารถคัดลอกมันได้แค่จัดการอ้างอิงกับพวกมันเพราะวัตถุที่อ้างอิงไม่สามารถเปลี่ยน ...

ดังนั้นคุณสามารถคัดลอกในตัวอย่างแรกของคุณโดยไม่มีปัญหา:

String s = "hello";
String backup_of_s = s;
s = "bye";

10

รุ่นที่สองของคุณมีประสิทธิภาพน้อยลงเพราะสร้างวัตถุสตริงพิเศษเมื่อไม่จำเป็นต้องทำเช่นนั้น

การเปลี่ยนไม่ได้หมายความว่ารุ่นแรกของคุณทำงานในแบบที่คุณคาดหวังและเป็นวิธีการที่ต้องการ


0

กรณีที่สองยังไม่มีประสิทธิภาพในแง่ของ String pool คุณต้องโทรไปฝึกงานอย่างชัดเจน () ในการอ้างอิงผลตอบแทนเพื่อให้มันฝึกงาน


-16
String str1="this is a string";
String str2=str1.clone();

วิธีการคัดลอกเช่นนี้? ฉันคิดว่าการได้รับสำเนาใหม่นั้นดีกว่าดังนั้นข้อมูลของstr1จะไม่ได้รับผลกระทบเมื่อstr2มีการอ้างอิงและแก้ไขในการกระทำที่ละเอียดกว่านี้


3
Stringไม่เปลี่ยนรูป การโคลนสตริงไม่สมเหตุสมผล
Vladimir
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.