CharSequence VS String ใน Java?


421

การเขียนโปรแกรมใน Android ค่าข้อความส่วนใหญ่คาดว่าจะCharSequenceมา

ทำไมถึงเป็นอย่างนั้น? ประโยชน์คืออะไรและผลกระทบหลักของการใช้คืออะไรCharSequenceเกินStringคืออะไร?

อะไรคือความแตกต่างหลักและคาดว่าจะมีปัญหาอะไรในขณะที่ใช้มันและการแปลงจากที่หนึ่งไปยังอีกที่หนึ่ง?


1
คำตอบที่ดีกว่าสามารถพบได้ที่ความแตกต่าง
Suragch

คำตอบ:


343

Strings เป็น CharSequencesดังนั้นคุณสามารถใช้ Strings และไม่ต้องกังวล Android เป็นเพียงการพยายามให้ความช่วยเหลือโดยอนุญาตให้คุณระบุ CharSequence วัตถุอื่น ๆ เช่น StringBuffers


94
ยกเว้นเมื่อ Android ส่ง CharSequence ให้ฉันในการติดต่อกลับและฉันต้องการสตริง - โทร charSeq.toString ()
Martin Konicek

100
แต่โปรดจำไว้ว่าคำเตือนนี้มาจากCharSequencejavadoc: อินเทอร์เฟซนี้ไม่ปรับแต่งสัญญาทั่วไปของequalsและhashCodeวิธีการ ผลจากการเปรียบเทียบสองวัตถุที่ใช้CharSequenceจึงเป็นโดยทั่วไปไม่ได้กำหนด แต่ละวัตถุอาจถูกใช้งานโดยคลาสที่แตกต่างกันและไม่มีการรับประกันว่าแต่ละคลาสจะสามารถทดสอบอินสแตนซ์ของมันเพื่อความเท่าเทียมกันกับคลาสอื่น ๆ ดังนั้นจึงไม่เหมาะสมที่จะใช้CharSequenceอินสแตนซ์โดยพลการเป็นองค์ประกอบในชุดหรือเป็นคีย์ในแผนที่
Trevor Robinson

3
@Pierier: ฉันคิดว่ามันเป็นข้อ จำกัด ทางปฏิบัติมากกว่า CharSequenceเป็นชุดติดตั้งเพิ่มใน JDK 1.4 เพื่อแนะนำอินเตอร์เฟสทั่วไปแบบ จำกัด วัตถุประสงค์ให้กับวัตถุที่มีลำดับอักขระ วัตถุเหล่านั้นบางส่วนมีสถานะอื่นดังนั้นจึงอาจไม่เหมาะสมที่จะนิยามObject.equalsว่า "มีลำดับอักขระเหมือนกัน" CharBufferตัวอย่างเช่นNIO จะเปิดเผยเฉพาะอักขระระหว่างอักขระpositionและlimitเป็นCharSequenceแม้ว่าจะมีอักขระอื่น ๆ อีกมากมาย
Trevor Robinson

6
@TrevorRobinson ดังนั้นข้อผิดพลาดในการออกแบบจะมีequals/ hashCodeบนObjectในสถานที่แรก ....
Pacerier

4
@Pacerier: IMHO ไม่มีข้อผิดพลาดในการออกแบบในObjectหรือCharSequenceไม่จำเป็นต้องมีส่วนต่อประสานเพื่อให้เกิดความเท่าเทียมกันระหว่างการใช้งาน ไม่Collectionจำเป็นต้องมีสองs เพื่อให้ความเท่าเทียมกันระหว่างCollectionอินเตอร์เฟส แต่สามารถทำได้หากเลือก IMHO CharSequenceควร จำกัด เฉพาะอินพุตและใช้น้อยกว่าสำหรับประเภทส่งคืน
Brett Ryan

58

CharSequence= อินเตอร์เฟส
String= การนำไปปฏิบัติที่เป็นรูปธรรม

  • CharSequenceเป็นส่วนต่อประสานอินเตอร์เฟซ
  • หลายคลาสใช้อินเตอร์เฟสนี้
    • StringCharSequenceเป็นหนึ่งในชั้นเรียนเช่นการดำเนินงานที่เป็นรูปธรรมของ

คุณพูดว่า:

แปลงจากที่หนึ่งไปยังอีก

Stringไม่มีการแปลงจาก

  • ทุกStringวัตถุเป็นCharSequence
  • ทุกคนสามารถผลิตCharSequence StringโทรCharSequence::toString. หากCharSequenceเกิดขึ้นเป็น a Stringแล้ววิธีการส่งกลับการอ้างอิงไปยังวัตถุของตัวเอง

ในคำอื่น ๆ ทุกคนStringเป็นCharSequenceแต่ไม่ใช่ทุกคนCharSequenceเป็นStringเป็น

การเขียนโปรแกรมไปยังส่วนต่อประสาน

การเขียนโปรแกรมใน Android ค่าข้อความส่วนใหญ่คาดว่าเป็น CharSequence

ทำไมถึงเป็นอย่างนั้น? ประโยชน์คืออะไรและผลกระทบหลักของการใช้ CharSequence ผ่านสายอักขระคืออะไร

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

เมื่อพัฒนาAPIเพื่อให้โปรแกรมเมอร์ต่าง ๆ ใช้ในสถานการณ์ต่าง ๆ ให้เขียนโค้ดของคุณเพื่อมอบและใช้อินเทอร์เฟซทั่วไปที่เป็นไปได้ สิ่งนี้จะทำให้ผู้เรียกโปรแกรมเมอร์มีอิสระในการใช้การใช้งานที่หลากหลายของอินเทอร์เฟซนั้นการดำเนินการใดก็ตามที่ดีที่สุดสำหรับบริบทเฉพาะ

ยกตัวอย่างให้ดูที่Java คอลเลกชันกรอบ หาก API ของคุณให้หรือใช้เวลาคอลเลกชันที่สั่งซื้อของวัตถุประกาศวิธีการของคุณกับการใช้Listมากกว่าArrayList, LinkedListหรือใด ๆ การดำเนินการของบุคคลที่ 3 อื่น ๆ Listของ

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

อะไรคือความแตกต่างที่สำคัญและปัญหาที่คาดหวังขณะใช้งาน

  • ด้วยStringคุณรู้ว่าคุณมีชิ้นส่วนของข้อความทั้งหมดในหน่วยความจำและจะไม่เปลี่ยนรูป
  • ด้วยCharSequenceคุณไม่ทราบว่าคุณลักษณะเฉพาะของการใช้งานที่เป็นรูปธรรมอาจเป็นอย่างไร

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

และแปลงจากที่หนึ่งไปยังอีกที่?

โดยทั่วไปคุณจะไม่แปลงไปมา String เป็น CharSequenceหากวิธีการที่คุณบอกว่ามันต้องใช้CharSequenceโปรแกรมเมอร์โทรอาจผ่านStringวัตถุหรืออาจจะผ่านอะไรอย่างอื่นเช่นหรือStringBuffer StringBuilderรหัสของวิธีการของคุณจะใช้สิ่งใดก็ตามที่ผ่านไปแล้วเรียกใช้CharSequenceวิธีการใด ๆ

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

ประวัติบิดเบี้ยว

จดบันทึกความคิดเห็นที่ทำไว้ในคำตอบที่ยอมรับได้ CharSequenceอินเตอร์เฟซที่ได้รับการดัดแปลงโครงสร้างบนชั้นที่มีอยู่จึงมีบางรายละเอียดปลีกย่อยที่สำคัญ ( equals()และhashCode()) สังเกตุรุ่นต่างๆของ Java (1, 2, 4 & 5) ที่ติดแท็กในคลาส / อินเตอร์เฟส - ค่อนข้างปั่นป่วนตลอดหลายปีที่ผ่านมา CharSequenceคงจะเป็นการดีที่จะได้อยู่ในจุดเริ่มต้น แต่นั่นคือชีวิต

แผนภาพคลาสของฉันด้านล่างอาจช่วยให้คุณเห็นภาพขนาดใหญ่ของประเภทสตริงใน Java 7/8 ฉันไม่แน่ใจว่าสิ่งเหล่านี้มีอยู่ใน Android หรือไม่ แต่บริบทโดยรวมอาจยังคงมีประโยชน์สำหรับคุณ

แผนภาพของคลาสและอินเตอร์เฟสที่เกี่ยวข้องกับสตริงต่างๆ


2
คุณทำแผนภาพนี้ด้วยตัวเองหรือไม่? สงสัยว่ามีแคตตาล็อกของไดอะแกรมเหล่านี้สำหรับโครงสร้างข้อมูลที่ต่างกันหรือไม่
171943

4
@ user171943 แต่งโดยฉันสร้างขึ้นด้วยมือโดยใช้แอพOmniGraffleจาก OmniGroup
Basil Bourque

37

ฉันเชื่อว่าเป็นการดีที่สุดที่จะใช้ CharSequence เหตุผลก็คือสตริงใช้ CharSequence ดังนั้นคุณสามารถส่งสตริงไปยัง CharSequence ได้ แต่คุณไม่สามารถส่ง CharSequence ไปยังสตริงได้เนื่องจาก CharSequence ไม่ได้ใช้สตริง นอกจากนี้ใน Android EditText.getText()จะส่งกลับวิธีการแก้ไขซึ่งยังใช้ CharSequence และสามารถส่งผ่านเป็นหนึ่งได้อย่างง่ายดายในขณะที่ไม่สามารถเข้าสู่สตริงได้อย่างง่ายดาย CharSequence จัดการทั้งหมด!


7
คุณสามารถทำได้charSequence.toString()
Jorge Fuentes González

1
@ จอร์จ: ยกเว้นว่าจะค่อนข้างไม่มีประสิทธิภาพหากลำดับไม่แน่นอน (หรือด้วยเหตุผลใดก็ตามต้องใช้สำเนาของตัวละครเพื่อที่จะทำให้ในสตริงที่ไม่เปลี่ยนรูป)
Lawrence Dol

คำอธิบายที่ดีมาก .. !
majurageerthan

23

โดยทั่วไปการใช้อินเทอร์เฟซจะช่วยให้คุณสามารถปรับใช้การดำเนินงานด้วยความเสียหายของหลักประกันน้อยที่สุด แม้ว่า java.lang.String นั้นเป็นที่นิยมอย่างมากอาจเป็นไปได้ว่าในบริบทบางอย่างหนึ่งอาจต้องการใช้การใช้งานอื่น ด้วยการสร้าง API รอบ ๆ CharSequences แทนที่จะใช้ Strings โค้ดจะให้โอกาสในการทำเช่นนั้น


8

นี่คือเหตุผลประสิทธิภาพเกือบแน่นอน ตัวอย่างเช่นลองนึกภาพ parser ที่ผ่านไบต์ 500 ไบต์ที่มีสตริง

มี 3 วิธีในการส่งคืนเนื้อหาสตริง:

  1. สร้าง String [] ณ เวลาแยกวิเคราะห์หนึ่งตัวอักษรในแต่ละครั้ง การดำเนินการนี้จะใช้เวลานาน เราสามารถใช้ == แทน. equals เพื่อเปรียบเทียบการอ้างอิงแคช

  2. สร้าง int [] ด้วย offsets ในเวลาในการแยกวิเคราะห์แล้วสร้าง String แบบไดนามิกเมื่อเกิดการรับ () แต่ละสตริงจะเป็นวัตถุใหม่ดังนั้นจึงไม่มีค่าแคชส่งคืนและใช้ ==

  3. สร้าง CharSequence [] ในเวลาที่แยกวิเคราะห์ เนื่องจากไม่มีการจัดเก็บข้อมูลใหม่ (นอกเหนือจากการปรับลดลงในบัฟเฟอร์ไบต์) การแยกจึงต่ำกว่า # 1 มาก ณ เวลาที่รับเราไม่จำเป็นต้องสร้างสตริงดังนั้นรับประสิทธิภาพเท่ากับ # 1 (ดีกว่า # 2) เนื่องจากเราจะส่งคืนการอ้างอิงไปยังวัตถุที่มีอยู่เท่านั้น

นอกเหนือจากการประมวลผลที่คุณได้รับโดยใช้ CharSequence คุณยังลดการปล่อยหน่วยความจำโดยไม่ทำซ้ำข้อมูล ตัวอย่างเช่นหากคุณมีบัฟเฟอร์ที่มีข้อความ 3 ย่อหน้าและต้องการส่งคืนทั้ง 3 หรือย่อหน้าเดียวคุณต้องมี 4 Strings เพื่อแสดงสิ่งนี้ การใช้ CharSequence คุณต้องการเพียง 1 บัฟเฟอร์กับข้อมูลและอินสแตนซ์ 4 ตัวของการใช้งาน CharSequence ที่ติดตามการเริ่มต้นและความยาว


6
อ้างอิง ดูเหมือนจะเป็นการคาดเดาสิ่งที่เกิดขึ้นแบบสุ่ม ฉันยังไม่พบข้อโต้แย้งของคุณที่ถูกต้อง ใคร ๆ ก็สามารถเก็บ bytebuffer 500k เป็นสตริงในสถานที่แรกและเพียงแค่กลับสารตั้งต้นซึ่งเป็นบ้าอย่างรวดเร็วและร่วมกันมากขึ้น
kritzikratzi

6
@kritzikratzi - ในฐานะของ JDK7 สตริงย่อยบน String จะไม่แชร์อาร์เรย์ต้นแบบอีกต่อไปและจะไม่ "เร็วเร็ว" มันใช้เวลา O (N) ในความยาวของสตริงย่อยและสร้างสำเนาของอักขระพื้นฐานในแต่ละครั้งที่คุณเรียกใช้ (ขยะจำนวนมาก)
BeeOnRope

@ kritzikratzi ฉันเชื่อว่าเหตุผลของการเปลี่ยนแปลงคือถ้าไม่มีการคัดลอกสตริงเดิมจะคงอยู่ตลอดอายุการใช้งานของสตริงย่อยทั้งหมด เนื่องจากปกติแล้ว substrings จะเป็นเพียงส่วนเล็ก ๆ ของต้นฉบับและสามารถคงอยู่ได้ตลอดไปทั้งนี้ขึ้นอยู่กับวิธีการใช้งานซึ่งจะทำให้ขยะเพิ่มมากขึ้นหากมีการใช้ substrings นานกว่าสตริงเดิมมาก alt ที่น่าสนใจอาจจะพิจารณาว่าจะคัดลอกหรือไม่ตามอัตราส่วนของสตริงย่อยต่อขนาดสตริงหลัก แต่คุณต้องหมุนCharSequenceการใช้งานของคุณเอง
JAB

1
บางทีฉันอาจพลาดประเด็นนี้ไป แต่คำตอบนี้ไร้สาระ A CharSequenceเป็นอินเทอร์เฟซ - ตามคำนิยามไม่มีรายละเอียดการใช้งานที่คุณพูดถึงเพราะไม่มีการใช้งานของตัวเอง A Stringเป็นหนึ่งในคลาสคอนกรีตหลายคลาสที่ใช้CharSequenceอินเตอร์เฟส ดังนั้นString เป็น CharSequenceคุณสามารถเปรียบเทียบรายละเอียดการปฏิบัติงานของStringVS StringBufferVS แต่ไม่StringBuilder CharSequenceการเขียน“ การประมวลผลที่คุณได้รับใช้ CharSequence” นั้นไร้ความหมาย
Basil Bourque

7

ปัญหาที่เกิดขึ้นในรหัส Android ที่ใช้งานได้จริงคือการเปรียบเทียบกับ CharSequence.equals นั้นถูกต้อง แต่ไม่จำเป็นต้องทำงานตามที่ตั้งใจไว้

EditText t = (EditText )getView(R.id.myEditText); // Contains "OK"
Boolean isFalse = t.getText().equals("OK"); // will always return false.

ควรทำการเปรียบเทียบโดย

("OK").contentEquals(t.GetText()); 

5

CharSequence

A CharSequenceเป็นอินเตอร์เฟสไม่ใช่คลาสจริง อินเทอร์เฟซเป็นเพียงชุดของกฎ (เมธอด) ที่คลาสต้องมีหากใช้อินเทอร์เฟซ ใน Android a CharSequenceเป็นสิ่งที่สำคัญสำหรับสตริงข้อความประเภทต่างๆ นี่คือบางส่วนของคนทั่วไป:

  • String (ข้อความที่ไม่เปลี่ยนรูปไม่มีช่วงสไตล์)
  • StringBuilder (ข้อความที่ไม่แน่นอนที่ไม่มีช่วงการออกแบบ)
  • SpannableString (ข้อความที่ไม่เปลี่ยนรูปพร้อมช่วงการกำหนดสไตล์)
  • SpannableStringBuilder (ข้อความที่ไม่แน่นอนพร้อมช่วงการจัดแต่งทรงผม)

(คุณสามารถอ่านเพิ่มเติมเกี่ยวกับความแตกต่างระหว่างสิ่งเหล่านี้ได้ที่นี่ )

หากคุณมีวัตถุแล้วมันเป็นจริงของวัตถุหนึ่งในชั้นเรียนที่ดำเนินการCharSequence CharSequenceตัวอย่างเช่น:

CharSequence myString = "hello";
CharSequence mySpannableStringBuilder = new SpannableStringBuilder();

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

public int getLength(CharSequence text) {
    return text.length();
}

เชือก

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

String myString = new String();

แต่คุณไม่สามารถทำสิ่งนี้:

CharSequence myCharSequence = new CharSequence(); // error: 'CharSequence is abstract; cannot be instantiated

เนื่องจากCharSequenceเป็นเพียงรายการของกฎที่Stringสอดคล้องกับคุณสามารถทำได้:

CharSequence myString = new String();

นั่นหมายความว่าทุกครั้งที่วิธีการขอให้มันเป็นเรื่องที่ดีที่จะให้มันCharSequenceString

String myString = "hello";
getLength(myString); // OK

// ...

public int getLength(CharSequence text) {
    return text.length();
}

อย่างไรก็ตามสิ่งที่ตรงกันข้ามไม่เป็นความจริง หากวิธีการใช้Stringพารามิเตอร์คุณจะไม่สามารถผ่านมันเป็นสิ่งที่เป็นเพียงการรู้จักกันโดยทั่วไปจะเป็นCharSequenceเพราะมันเป็นจริงอาจจะมีSpannableStringหรือบางประเภทอื่น ๆ CharSequenceของ

CharSequence myString = "hello";
getLength(myString); // error

// ...

public int getLength(String text) {
    return text.length();
}

0

CharSequenceเป็นอินเตอร์เฟสและStringใช้งานได้ คุณสามารถสร้างอินสแตนซ์ a ได้Stringแต่ไม่สามารถทำได้CharSequenceเนื่องจากเป็นอินเทอร์เฟซ คุณสามารถค้นหาการใช้งานอื่น ๆ ได้ในCharSequenceเว็บไซต์ Java อย่างเป็นทางการ


-4

CharSequence เป็นลำดับถ่านที่อ่านได้ซึ่งใช้ String มันมี 4 วิธี

  1. charAt (ดัชนี int)
  2. ความยาว()
  3. subSequence (int start, int end)
  4. toString ()

โปรดอ้างอิงเอกสารCharSequence เอกสาร


6
CharSequenceStringไม่ใช้ แม้ว่าการสนทนาจะเป็นจริง
seh

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