จำนวนสตริงที่ถูกสร้างในหน่วยความจำเมื่อเชื่อมสตริงใน Java?


17

ฉันถูกถามเกี่ยวกับสตริงที่ไม่เปลี่ยนรูปแบบใน Java ฉันถูกมอบหมายให้เขียนฟังก์ชั่นที่เชื่อมจำนวน "a" เข้ากับสตริง

สิ่งที่ฉันเขียน:

public String foo(int n) {
    String s = "";
    for (int i = 0; i < n; i++) {
        s = s + "a"
    }
    return s;
}

ฉันถูกถามแล้วว่าจะสร้างโปรแกรมจำนวนเท่าใดสตริงโดยสมมติว่าการรวบรวมขยะไม่ได้เกิดขึ้น ความคิดของฉันสำหรับ n = 3 คือ

  1. ""
  2. "A"
  3. "A"
  4. "AA"
  5. "A"
  6. "AAA"
  7. "A"

โดยพื้นฐานแล้วจะมีการสร้างสตริง 2 สายในการวนซ้ำแต่ละรอบ แต่คำตอบก็คือ n 2 ฟังก์ชั่นนี้จะสร้างสตริงอะไรในหน่วยความจำและทำไมเป็นเช่นนั้น


15
หากคุณได้รับงานนี้วิ่งหนีวิ่งเร็วมาก .......
mattnz

@mattnz ด้วยเหตุผลหลายประการ (ไม่ใช่เพราะรหัสที่เขียน)

3
สิ่งนี้ใช้เวลารันไทม์ O (n ^ 2) ยกเว้นว่า JIT จะปรับลูปให้เหมาะสม แต่จะไม่สร้างสตริง n ^ 2
user2357112 รองรับ Monica

คำตอบ:


26

ฉันถูกถามแล้วว่าจะสร้างโปรแกรมจำนวนเท่าใดสตริงโดยสมมติว่าการรวบรวมขยะไม่ได้เกิดขึ้น ความคิดของฉันสำหรับ n = 3 คือ (7)

Strings 1 ( "") และ 2 ( "a") เป็นค่าคงที่ในโปรแกรมสิ่งเหล่านี้ไม่ได้ถูกสร้างขึ้นเป็นส่วนหนึ่งของสิ่งต่าง ๆ แต่เป็น 'interned' เพราะมันเป็นค่าคงที่คอมไพเลอร์รู้ อ่านเพิ่มเติมเกี่ยวกับสิ่งนี้ได้ที่String interningบน Wikipedia

สิ่งนี้จะลบสตริงที่ 5 และ 7 ออกจากการนับเช่นเดียว"a"กับ String # 2 สิ่งนี้ทำให้สตริง # 3, # 4 และ # 6 คำตอบคือ "3 สายถูกสร้างขึ้นสำหรับ n = 3" โดยใช้รหัสของคุณ

เห็นได้ชัดว่าจำนวนนับของ n 2ผิดเพราะที่ n = 3 นี่จะเป็น 9 และแม้กระทั่งโดยคำตอบกรณีที่เลวร้ายที่สุดนั่นก็คือ 7 เท่านั้นหากสตริงที่ไม่ได้ถูกฝึกงานของคุณถูกต้องคำตอบควรเป็น 2n + 1

ดังนั้นคำถามที่คุณควรทำอย่างไร

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

สิ่งแรกที่ต้องดูคือตัวสร้าง ในกรณีนี้เรารู้ว่าสตริงจะนานแค่ไหนและมีนวกรรมิกStringBuilder(int capacity) ซึ่งหมายความว่าเราจัดสรรเท่าที่เราต้องการ

ถัดไป"a"ไม่จำเป็นต้องเป็นStringแต่สามารถเป็นอักขระ'a'ได้ สิ่งนี้มีการเพิ่มประสิทธิภาพเล็กน้อยเมื่อทำการเรียกappend(String)vs append(char)- ด้วยappend(String)เมธอดต้องการค้นหาว่า String ยาวแค่ไหนและทำงานบางอย่างกับมัน ในอีกด้านcharหนึ่งจะมีความยาวหนึ่งอักขระเสมอ

ความแตกต่างรหัสสามารถมองเห็นที่StringBuilder.append (String) VS StringBuilder.append (ถ่าน) ไม่ได้เป็นสิ่งที่จะเป็นมากเกินไปที่เกี่ยวข้องกับการ แต่ถ้าคุณกำลังพยายามที่จะสร้างความประทับใจให้นายจ้างที่ดีที่สุดคือการใช้วิธีปฏิบัติที่ดีที่สุด

ดังนั้นสิ่งนี้จะมีลักษณะอย่างไรเมื่อคุณรวมเข้าด้วยกัน?

public String foo(int n) {
    StringBuilder sb = new StringBuilder(n);
    for (int i = 0; i < n; i++) {
        sb.append('a');
    }
    return sb.toString();
}

หนึ่ง StringBuilder และหนึ่งสตริงได้ถูกสร้างขึ้น ไม่จำเป็นต้องมีสายอักขระพิเศษ


เขียนโปรแกรมง่ายๆอื่น ๆ ใน Eclipse ติดตั้งpmdและรันบนโค้ดที่คุณเขียน สังเกตสิ่งที่มันบ่นและแก้ไขสิ่งเหล่านั้น มันจะได้พบการดัดแปลงของ String ที่มี + ในลูปและถ้าคุณเปลี่ยนเป็น StringBuilder ก็อาจจะพบความจุเริ่มต้น แต่มันจะจับความแตกต่างระหว่าง.append("a")และ.append('a')


9

ในแต่ละซ้ำใหม่Stringจะถูกสร้างขึ้นโดยผู้ประกอบการและมอบหมายให้+ sหลังจากกลับมาพวกเขาทั้งหมด แต่คนสุดท้ายเป็นคนเก็บขยะ

ค่าคงที่สตริงชอบ""และ"a"ไม่ได้สร้างขึ้นทุกครั้งเหล่านี้สตริงฝึกงาน เนื่องจากสตริงไม่เปลี่ยนรูปพวกเขาจึงสามารถแชร์ได้อย่างอิสระ สิ่งนี้เกิดขึ้นกับค่าคงที่สตริง

สตริง concatenate StringBuilderอย่างมีประสิทธิภาพการใช้งาน


ผู้คนในการสัมภาษณ์ได้ถกเถียงกันว่าตัวอักษรนั้นจริงหรือไม่และตัดสินใจว่าตัวอักษรถูกสร้างขึ้นทุกครั้ง แต่มันก็สมเหตุสมผลดีกว่า
ahalbert

6
คุณ "อภิปราย" อย่างไรในสิ่งที่ภาษาทำแน่นอนคุณอ่านข้อมูลจำเพาะและรู้แน่นอนหรือไม่ได้กำหนดไว้และดังนั้นจึงไม่มีคำตอบที่ถูกต้อง .....
mattnz

@mattnz มันอาจเป็นเรื่องที่น่าสนใจที่จะรู้ว่าคอมไพเลอร์ / รันไทม์ที่คุณใช้อยู่ทำอะไรได้บ้าง สิ่งนี้ใช้กับประสิทธิภาพโดยเฉพาะ
svick

1
@svick: คุณสามารถได้รับข้อเสนอที่ยอดเยี่ยมโดยการตั้งสมมติฐานจากนั้นคอมไพเลอร์จะได้รับการอัพเกรดการปรับให้เหมาะสม ฯลฯ การเปลี่ยนแปลงพฤติกรรมทำให้เกิดข้อบกพร่องเพราะคุณต้องพึ่งพาพฤติกรรมที่ไม่ได้ระบุมากกว่าพฤติกรรมที่กำหนด คุณรู้ว่าสิ่งที่พวกเขาพูดเกี่ยวกับการเพิ่มประสิทธิภาพ -) ปล่อยให้ผู้เชี่ยวชาญและ b) ยังไม่เป็นผู้เชี่ยวชาญของคุณ :) ถ้าความเชื่อมั่นนั้นขึ้นอยู่กับประสิทธิภาพเท่านั้น แต่ยังคงเป็นไปตามข้อกำหนดทางภาษาคุณจะเสียประสิทธิภาพเท่านั้น หลายครั้งที่ฉันเห็นโค้ดที่อาศัยพฤติกรรมที่ไม่ระบุหรือคอมไพเลอร์ผิดพลาดในรูปแบบที่ไม่คาดคิด (ส่วนใหญ่คือ C และ C ++)
mattnz

@mattnz แล้วคุณจะเสนอให้ตัดสินใจเกี่ยวกับประสิทธิภาพได้อย่างไร โดยปกติแล้วสิ่งที่ดีที่สุดที่คุณจะได้รับจากข้อกำหนด / เอกสารคือความซับซ้อนของ O ขนาดใหญ่ แต่ก็ไม่เพียงพอ ไม่ว่าในกรณีใดประสิทธิภาพการทำงานจะขึ้นอยู่กับการติดตั้งใช้งานอยู่เสมอดังนั้นฉันคิดว่าการพึ่งพารายละเอียดการใช้งานจะขึ้นอยู่กับประสิทธิภาพเมื่อใช้งาน
svick

4

ดังที่ MichaelT อธิบายไว้ในคำตอบของเขารหัสของคุณจะจัดสรรสตริง O (n) แต่ยังจัดสรรหน่วยความจำO (n 2 ) ไบต์และทำงานในเวลา O (n 2 )

มันจัดสรร O (n 2 ) ไบต์เนื่องจากสตริงที่คุณจัดสรรมีความยาว 0, 1, 2, …, n-1, n ซึ่งจะรวมเป็น (n 2 + n) / 2 = O (n 2 )

เวลายังเป็น O (n 2 ) เนื่องจากการจัดสรรสตริง i-th ต้องคัดลอกสตริง (i-1) -th ซึ่งมีความยาว i-1 ซึ่งหมายความว่าต้องคัดลอกแต่ละไบต์ซึ่งจะใช้เวลา O (n 2 )

บางทีนี่อาจเป็นสิ่งที่ผู้สัมภาษณ์หมายถึง?


สมการไม่ควรเป็น (n ^ 2 + n) / 2 เช่นนี้ใช่ไหม
HeyJude
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.