ทำไมเราจึงมีความยาวของอาร์เรย์เป็นแอตทริบิวต์ที่array.length
และสำหรับ String เรามีวิธีการที่str.length()
?
มีเหตุผลบางอย่าง?
ทำไมเราจึงมีความยาวของอาร์เรย์เป็นแอตทริบิวต์ที่array.length
และสำหรับ String เรามีวิธีการที่str.length()
?
มีเหตุผลบางอย่าง?
คำตอบ:
ก่อนอื่นให้ฉันเน้นสามวิธีที่แตกต่างกันเพื่อวัตถุประสงค์ที่คล้ายคลึงกัน
length
- อาร์เรย์ ( int[]
, double[]
, String[]
) - ที่จะรู้ว่าความยาวของอาร์เรย์
length()
- String related Object ( String
, StringBuilder
ฯลฯ ) - เพื่อทราบความยาวของ String
size()
- การเก็บวัตถุ ( ArrayList
, Set
ฯลฯ ) - เพื่อทราบขนาดของคอลเลกชัน
ตอนนี้ลืมเกี่ยวกับการlength()
พิจารณาเพียงและlength
size()
length
ไม่ใช่วิธีการดังนั้นจึงสมเหตุสมผลอย่างยิ่งที่จะไม่ทำงานกับวัตถุ ใช้ได้กับอาร์เรย์เท่านั้น
size()
ชื่อของมันอธิบายได้ดีกว่าและเนื่องจากเป็นวิธีการจึงจะใช้ในกรณีของวัตถุที่ทำงานกับคอลเลกชัน (คอลเลกชันเฟรมเวิร์ก) ตามที่ฉันกล่าวไว้ที่นั่น
ตอนนี้มาที่length()
:
String ไม่ใช่อาร์เรย์ดั้งเดิม (เราจึงใช้ไม่ได้.length
) และไม่ใช่คอลเล็กชัน (เราจึงใช้ไม่ได้.size()
) นั่นคือเหตุผลที่เราต้องการอันอื่นซึ่งก็คือlength()
(เก็บความแตกต่างและตอบสนองวัตถุประสงค์)
เป็นคำตอบว่าทำไม?
ฉันคิดว่ามันมีประโยชน์จำง่ายและใช้ง่ายและเป็นมิตร
ทำให้ง่ายขึ้นเล็กน้อยคุณสามารถคิดได้ว่าอาร์เรย์เป็นกรณีพิเศษและไม่ใช่คลาสธรรมดา (คล้ายกับแบบดั้งเดิม แต่ไม่ใช่) สตริงและคอลเลกชันทั้งหมดเป็นคลาสดังนั้นวิธีการรับขนาดความยาวหรือสิ่งที่คล้ายกัน
ฉันเดาว่าเหตุผลในการออกแบบคือประสิทธิภาพ ถ้าพวกเขาสร้างมันขึ้นมาในวันนี้พวกเขาอาจจะมีบางอย่างเช่นคลาสคอลเลกชันที่มีอาร์เรย์สำรองแทน
หากใครสนใจนี่คือตัวอย่างโค้ดเล็ก ๆ เพื่อแสดงความแตกต่างระหว่างสองโค้ดที่สร้างขึ้นโดยอันดับแรกคือแหล่งที่มา:
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 (หรือสิ่งที่คล้ายกัน) และไม่ใช่โครงสร้างภาษาที่ไม่ได้มีพฤติกรรมเหมือนคลาส แต่รวดเร็ว แน่นอนว่าพวกเขาสามารถปิดบังคุณสมบัติเป็นการเรียกใช้เมธอดและจัดการกับมันในคอมไพเลอร์ได้ แต่ฉันคิดว่ามันน่าจะสับสนกว่านี้หากมีวิธีการบางอย่างที่ไม่ใช่คลาสจริง
Vector
เมื่อภาษา Java ไม่มีการรองรับอาร์เรย์
.length
เป็นคุณสมบัติครั้งเดียวของ Java มันใช้ในการหาขนาดของมิติเดียวอาร์เรย์
.length()
เป็นวิธีการ ใช้เพื่อหาความยาวของไฟล์String
. หลีกเลี่ยงการทำซ้ำค่า
พิจารณา:
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 และอื่น ๆ ) ทำในสิ่งเดียวกันดังนั้นนี่อาจเป็นข้อบกพร่องเล็กน้อยที่กลายเป็นส่วนหนึ่งของภาษา
คุณจะได้รับข้อผิดพลาดหากคุณใช้ผิด
ใน Java Array จะเก็บความยาวแยกจากโครงสร้างที่เก็บข้อมูลจริง เมื่อคุณสร้าง Array คุณจะต้องระบุความยาวและนั่นจะกลายเป็นการกำหนดแอตทริบิวต์ของ Array ไม่ว่าคุณจะทำอะไรกับอาร์เรย์ที่มีความยาว N (เปลี่ยนค่าค่าว่างออก ฯลฯ ) มันจะเป็นอาร์เรย์ของความยาว N เสมอ
ความยาวของสตริงเป็นเรื่องบังเอิญ ไม่ใช่แอตทริบิวต์ของ String แต่เป็นผลพลอยได้ แม้ว่าในความเป็นจริง Java Strings จะไม่เปลี่ยนรูป แต่ถ้าเป็นไปได้ที่จะเปลี่ยนเนื้อหาคุณสามารถเปลี่ยนความยาวได้ การเคาะอักขระสุดท้าย (ถ้าเป็นไปได้) จะทำให้ความยาวลดลง
ฉันเข้าใจว่านี่เป็นความแตกต่างที่ดีและฉันอาจถูกโหวตให้ลงคะแนน แต่มันเป็นเรื่องจริง ถ้าฉันสร้าง Array ที่มีความยาว 4 ความยาวของสี่นั้นเป็นลักษณะที่กำหนดของ Array และเป็นจริงโดยไม่คำนึงถึงสิ่งที่อยู่ภายใน ถ้าฉันสร้างสตริงที่มี "สุนัข" สตริงนั้นจะมีความยาว 4 เนื่องจากมีอักขระสี่ตัว
ฉันเห็นว่านี่เป็นเหตุผลสำหรับการดำเนินการกับแอตทริบิวต์และอีกวิธีหนึ่งด้วยวิธีการ ความจริงมันอาจจะเป็นความไม่ลงรอยกันโดยไม่ได้ตั้งใจ แต่มันก็สมเหตุสมผลสำหรับฉันและนี่คือสิ่งที่ฉันคิดเกี่ยวกับเรื่องนี้เสมอ
ฉันได้รับการสอนว่าสำหรับอาร์เรย์ความยาวจะไม่ถูกดึงข้อมูลด้วยวิธีการเนื่องจากความกลัวต่อไปนี้: โปรแกรมเมอร์จะกำหนดความยาวให้กับตัวแปรโลคัลก่อนเข้าสู่ลูป (คิดว่าสำหรับลูปที่เงื่อนไขใช้ความยาวของอาร์เรย์) ควรจะทำเช่นนั้นเพื่อลดการเรียกใช้ฟังก์ชัน (และจะช่วยปรับปรุงประสิทธิภาพ) ปัญหาคือความยาวอาจเปลี่ยนแปลงในระหว่างการวนซ้ำและตัวแปรจะไม่
เมื่อใดก็ตามที่สร้างอาร์เรย์ขนาดจะถูกระบุ ดังนั้นความยาวจึงถือได้ว่าเป็นคุณสมบัติการก่อสร้าง สำหรับ String นั้นโดยพื้นฐานแล้วจะเป็นอาร์เรย์ของถ่าน ความยาวเป็นคุณสมบัติของอาร์เรย์ถ่าน ไม่จำเป็นต้องใส่ความยาวเป็นฟิลด์เพราะไม่ใช่ทุกอย่างที่ต้องการฟิลด์นี้ http://www.programcreek.com/2013/11/start-from-length-length-in-java/
ผมแค่อยากจะเพิ่มข้อสังเกตบางการที่ดีที่คำตอบโดย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 และไม่ควรใช้วิธีนี้