ความยาวและความยาว () ใน Java


88

ทำไมเราจึงมีความยาวของอาร์เรย์เป็นแอตทริบิวต์ที่array.lengthและสำหรับ String เรามีวิธีการที่str.length()?

มีเหตุผลบางอย่าง?


3
ได้มีการพูดคุยกันก่อนหน้านี้ที่ CodeRanch ดูการอภิปรายที่นี่
missingfaktor

คำตอบ:


98

ก่อนอื่นให้ฉันเน้นสามวิธีที่แตกต่างกันเพื่อวัตถุประสงค์ที่คล้ายคลึงกัน

length- อาร์เรย์ ( int[], double[], String[]) - ที่จะรู้ว่าความยาวของอาร์เรย์

length()- String related Object ( String, StringBuilderฯลฯ ) - เพื่อทราบความยาวของ String

size()- การเก็บวัตถุ ( ArrayList, Setฯลฯ ) - เพื่อทราบขนาดของคอลเลกชัน

ตอนนี้ลืมเกี่ยวกับการlength()พิจารณาเพียงและlengthsize()

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

ตอนนี้มาที่length():
String ไม่ใช่อาร์เรย์ดั้งเดิม (เราจึงใช้ไม่ได้.length) และไม่ใช่คอลเล็กชัน (เราจึงใช้ไม่ได้.size()) นั่นคือเหตุผลที่เราต้องการอันอื่นซึ่งก็คือlength()(เก็บความแตกต่างและตอบสนองวัตถุประสงค์)

เป็นคำตอบว่าทำไม?
ฉันคิดว่ามันมีประโยชน์จำง่ายและใช้ง่ายและเป็นมิตร


6
ทางออกที่ยอดเยี่ยม!
Jeff Hu

27

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

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

หากใครสนใจนี่คือตัวอย่างโค้ดเล็ก ๆ เพื่อแสดงความแตกต่างระหว่างสองโค้ดที่สร้างขึ้นโดยอันดับแรกคือแหล่งที่มา:

public class LengthTest {
  public static void main(String[] args) {
    int[] array = {12,1,4};
    String string = "Hoo";
    System.out.println(array.length);
    System.out.println(string.length());
  }
}

การตัดส่วนที่ไม่สำคัญของรหัสไบต์การรันjavap -cในคลาสจะได้ผลลัพธ์ต่อไปนี้สำหรับสองบรรทัดสุดท้าย:

20: getstatic   #3; //Field java/lang/System.out:Ljava/io/PrintStream;
23: aload_1
24: arraylength
25: invokevirtual   #4; //Method java/io/PrintStream.println:(I)V
28: getstatic   #3; //Field java/lang/System.out:Ljava/io/PrintStream;
31: aload_2
32: invokevirtual   #5; //Method java/lang/String.length:()I
35: invokevirtual   #4; //Method java/io/PrintStream.println:(I)V

ในกรณีแรก (20-25) โค้ดจะถาม JVM สำหรับขนาดของอาร์เรย์ (ใน JNI นี่จะเป็นการเรียก GetArrayLength ()) ในขณะที่ในกรณี String (28-35) จำเป็นต้องทำ วิธีการโทรเพื่อรับความยาว

ในช่วงกลางปี ​​1990 หากไม่มี JIT และสิ่งของที่ดีมันจะฆ่าประสิทธิภาพโดยสิ้นเชิงเพียงแค่มี java.util.Vector (หรือสิ่งที่คล้ายกัน) และไม่ใช่โครงสร้างภาษาที่ไม่ได้มีพฤติกรรมเหมือนคลาส แต่รวดเร็ว แน่นอนว่าพวกเขาสามารถปิดบังคุณสมบัติเป็นการเรียกใช้เมธอดและจัดการกับมันในคอมไพเลอร์ได้ แต่ฉันคิดว่ามันน่าจะสับสนกว่านี้หากมีวิธีการบางอย่างที่ไม่ใช่คลาสจริง


1
Java ถือว่าอาร์เรย์เป็นวัตถุและสตริงเป็นวัตถุที่ไม่เปลี่ยนรูป !! : |
Nitish Upreti

@Myth: ฉันไม่เห็นจุดของคุณ ... คุณสมบัติความยาวของอาร์เรย์ไม่ใช่ฟิลด์สาธารณะหรืออะไรเลยมันเป็นโครงสร้างของ jvm (ความยาวคลื่นคือการดำเนินการใน bytecode) เห็นได้ชัดมากเมื่อคุณทำ JNI หรือถ้าคุณเพิ่งถอดรหัสว่ามาจากไหน
Fredrik

ฉันไม่คิดว่าเหตุผลของการออกแบบเดิมคือประสิทธิภาพเท่านั้น คุณจะใช้งานอย่างไรVectorเมื่อภาษา Java ไม่มีการรองรับอาร์เรย์
Holger

8

.lengthเป็นคุณสมบัติครั้งเดียวของ Java มันใช้ในการหาขนาดของมิติเดียวอาร์เรย์

.length()เป็นวิธีการ ใช้เพื่อหาความยาวของไฟล์String . หลีกเลี่ยงการทำซ้ำค่า


8

พิจารณา:

int[] myArray = new int[10];
String myString = "hello world!";
List<int> myList = new ArrayList<int>();

myArray.length    // Gives the length of the array
myString.length() // Gives the length of the string
myList.size()     // Gives the length of the list

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

ท้ายที่สุดแล้วนี่เป็นเพียงความไม่ลงรอยกันที่เกิดขึ้นซึ่งจะได้รับการแก้ไขอย่างแน่นอนหากภาษาได้รับการออกแบบใหม่ตั้งแต่ต้น เท่าที่ฉันรู้ไม่มีภาษาอื่น (C #, Python, Scala และอื่น ๆ ) ทำในสิ่งเดียวกันดังนั้นนี่อาจเป็นข้อบกพร่องเล็กน้อยที่กลายเป็นส่วนหนึ่งของภาษา

คุณจะได้รับข้อผิดพลาดหากคุณใช้ผิด


นี่เป็นคำตอบที่สมเหตุสมผลและเป็นกลางมากกว่า
Zubair Alam

3

ใน Java Array จะเก็บความยาวแยกจากโครงสร้างที่เก็บข้อมูลจริง เมื่อคุณสร้าง Array คุณจะต้องระบุความยาวและนั่นจะกลายเป็นการกำหนดแอตทริบิวต์ของ Array ไม่ว่าคุณจะทำอะไรกับอาร์เรย์ที่มีความยาว N (เปลี่ยนค่าค่าว่างออก ฯลฯ ) มันจะเป็นอาร์เรย์ของความยาว N เสมอ

ความยาวของสตริงเป็นเรื่องบังเอิญ ไม่ใช่แอตทริบิวต์ของ String แต่เป็นผลพลอยได้ แม้ว่าในความเป็นจริง Java Strings จะไม่เปลี่ยนรูป แต่ถ้าเป็นไปได้ที่จะเปลี่ยนเนื้อหาคุณสามารถเปลี่ยนความยาวได้ การเคาะอักขระสุดท้าย (ถ้าเป็นไปได้) จะทำให้ความยาวลดลง

ฉันเข้าใจว่านี่เป็นความแตกต่างที่ดีและฉันอาจถูกโหวตให้ลงคะแนน แต่มันเป็นเรื่องจริง ถ้าฉันสร้าง Array ที่มีความยาว 4 ความยาวของสี่นั้นเป็นลักษณะที่กำหนดของ Array และเป็นจริงโดยไม่คำนึงถึงสิ่งที่อยู่ภายใน ถ้าฉันสร้างสตริงที่มี "สุนัข" สตริงนั้นจะมีความยาว 4 เนื่องจากมีอักขระสี่ตัว

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


2

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


10
ความยาวของอาร์เรย์ไม่สามารถเปลี่ยนแปลงได้ในระหว่างการวนซ้ำ
Fredrik

2
คุณไม่สามารถเปลี่ยนความยาวของอาร์เรย์เฉพาะนี้ได้ แต่คุณสามารถกำหนดอันใหม่ให้กับตัวแปรเดียวกันได้
Bozho

2
@Bozho: จริง แต่ก็เป็นอาร์เรย์อื่นและถ้าคุณตั้งใจและไม่อัปเดตสิ่งที่คุณตรวจสอบลูปว่าคุณมีข้อบกพร่อง Java ไม่เคยมีความตั้งใจที่จะหยุดคุณจากการเขียนบั๊ก คุณสามารถทำลูปแบบไม่มีที่สิ้นสุดการเรียกซ้ำจนกว่าคุณจะตายและข้อผิดพลาดทางตรรกะทุกประเภทโดยไม่ต้องใช้ Java พยายามหยุดคุณจากมัน เป็นเหตุผลที่ดีที่จะไม่เก็บความยาวไว้ในตัวแปร แต่ไม่แน่ใจว่าทำไมจึงออกแบบมาเช่นนั้น
Fredrik

1

เมื่อใดก็ตามที่สร้างอาร์เรย์ขนาดจะถูกระบุ ดังนั้นความยาวจึงถือได้ว่าเป็นคุณสมบัติการก่อสร้าง สำหรับ String นั้นโดยพื้นฐานแล้วจะเป็นอาร์เรย์ของถ่าน ความยาวเป็นคุณสมบัติของอาร์เรย์ถ่าน ไม่จำเป็นต้องใส่ความยาวเป็นฟิลด์เพราะไม่ใช่ทุกอย่างที่ต้องการฟิลด์นี้ http://www.programcreek.com/2013/11/start-from-length-length-in-java/


0

ผมแค่อยากจะเพิ่มข้อสังเกตบางการที่ดีที่คำตอบโดยFredrik

Java Language ข้อกำหนดในมาตรา 4.3.1รัฐ

วัตถุเป็นเช่นชั้นหรืออาร์เรย์

ดังนั้นอาร์เรย์จึงมีบทบาทพิเศษใน Java ฉันสงสัยว่าทำไม

อาจโต้แย้งได้ว่าอาร์เรย์การใช้งานปัจจุบันมีความสำคัญต่อประสิทธิภาพที่ดีขึ้น แต่มันเป็นโครงสร้างภายในซึ่งไม่ควรเปิดเผย

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

ฉันเห็นด้วยกับ Fredrik ว่าการเพิ่มประสิทธิภาพคอมไพเลอร์อัจฉริยะน่าจะเป็นทางเลือกที่ดีกว่า สิ่งนี้จะช่วยแก้ปัญหาได้เช่นกันแม้ว่าคุณจะใช้คุณสมบัติสำหรับอาร์เรย์ แต่คุณยังไม่ได้แก้ปัญหาสำหรับสตริงและประเภทคอลเลกชันอื่น ๆ (ไม่เปลี่ยนรูป) เนื่องจากเช่นstringขึ้นอยู่กับcharอาร์เรย์ดังที่คุณเห็นในนิยามคลาส ของString:

public final class String implements java.io.Serializable, Comparable<String>, CharSequence {           
    private final char value[]; // ...

และผมก็ไม่เห็นด้วยกับที่มันจะทำให้เกิดความสับสนมากยิ่งขึ้นเพราะอาร์เรย์ไม่สืบทอดวิธีการทั้งหมดจากjava.lang.Object

ในฐานะวิศวกรฉันไม่ชอบคำตอบ "เพราะมันเป็นแบบนี้มาตลอด" และหวังว่าจะมีคำตอบที่ดีกว่านี้ แต่ในกรณีนี้ดูเหมือนจะเป็น

tl; dr

ในความคิดของฉันมันเป็นข้อบกพร่องในการออกแบบของ Java และไม่ควรใช้วิธีนี้

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