Tại sao chúng ta có độ dài của một mảng là một thuộc tính array.length
, và đối với Chuỗi chúng ta có một phương thức str.length()
,?
Có một số lý do?
Tại sao chúng ta có độ dài của một mảng là một thuộc tính array.length
, và đối với Chuỗi chúng ta có một phương thức str.length()
,?
Có một số lý do?
Câu trả lời:
Trước tiên, hãy để tôi nêu bật ba cách khác nhau cho mục đích tương tự.
length
- mảng ( int[]
, double[]
, String[]
) - để biết chiều dài của mảng
length()
- Đối tượng liên quan đến chuỗi ( String
, StringBuilder
v.v.) - để biết độ dài của chuỗi
size()
- Đối tượng Bộ sưu tập ( ArrayList
, Set
v.v.) - để biết kích thước của Bộ sưu tập
Bây giờ quên length()
xem xét chỉ length
và size()
.
length
không phải là một phương thức, vì vậy nó hoàn toàn có ý nghĩa rằng nó sẽ không hoạt động trên các đối tượng. Nó chỉ hoạt động trên các mảng.
size()
tên của nó mô tả nó tốt hơn và vì nó là một phương thức, nó sẽ được sử dụng trong trường hợp những đối tượng làm việc với bộ sưu tập (bộ sưu tập khung) như tôi đã nói ở trên.
Bây giờ đến với length()
:
Chuỗi không phải là một mảng nguyên thủy (vì vậy chúng ta không thể sử dụng .length
) và cũng không phải là một Bộ sưu tập (vì vậy chúng ta không thể sử dụng .size()
) đó là lý do tại sao chúng ta cũng cần một length()
mảng khác (giữ sự khác biệt và phục vụ mục đích).
Như câu trả lời cho Tại sao?
Tôi thấy nó hữu ích, dễ nhớ và dễ sử dụng và thân thiện.
Đơn giản hóa một chút, bạn có thể nghĩ rằng mảng là một trường hợp đặc biệt và không phải là các lớp bình thường (hơi giống với các nguyên thủy, nhưng không phải). Chuỗi và tất cả các bộ sưu tập là các lớp, do đó các phương thức để lấy kích thước, chiều dài hoặc những thứ tương tự.
Tôi đoán lý do tại thời điểm thiết kế là hiệu suất. Nếu họ tạo ra nó ngày hôm nay, họ có thể đã nghĩ ra một thứ gì đó giống như các lớp tập hợp được hỗ trợ bởi mảng.
Nếu có ai quan tâm, đây là một đoạn mã nhỏ để minh họa sự khác biệt giữa hai mã trong mã được tạo, trước tiên là nguồn:
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());
}
}
Việc cắt một phần không quá quan trọng của mã byte, chạy javap -c
trên lớp dẫn đến kết quả như sau cho hai dòng cuối cùng:
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
Trong trường hợp đầu tiên (20-25), mã chỉ hỏi JVM về kích thước của mảng (trong JNI, đây sẽ là một lệnh gọi tới GetArrayLength ()) trong khi trong trường hợp Chuỗi (28-35), nó cần thực hiện một gọi phương thức để lấy độ dài.
Vào giữa những năm 1990, nếu không có JIT và nội dung tốt, nó sẽ hoàn toàn giết chết hiệu suất nếu chỉ có java.util.Vector (hoặc một cái gì đó tương tự) chứ không phải một cấu trúc ngôn ngữ không thực sự hoạt động như một lớp nhưng nhanh. Tất nhiên họ có thể đã che thuộc tính dưới dạng một cuộc gọi phương thức và xử lý nó trong trình biên dịch nhưng tôi nghĩ sẽ còn khó hiểu hơn nếu có một phương thức trên một thứ không phải là một lớp thực.
Vector
khi ngôn ngữ Java không có hỗ trợ mảng?
Xem xét:
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
Rất có thể các chuỗi và mảng được thiết kế vào những thời điểm khác nhau và do đó cuối cùng sử dụng các quy ước khác nhau. Một lý do là vì các Chuỗi sử dụng các mảng bên trong, một phương thức length()
, được sử dụng để tránh trùng lặp thông tin giống nhau. Một phương pháp khác là việc sử dụng một phương thức length()
giúp nhấn mạnh tính bất biến của các chuỗi, mặc dù kích thước của một mảng cũng không thể thay đổi.
Cuối cùng thì đây chỉ là một sự mâu thuẫn đã phát triển mà chắc chắn sẽ được khắc phục nếu ngôn ngữ được thiết kế lại từ đầu. Theo như tôi biết thì không có ngôn ngữ nào khác (C #, Python, Scala, v.v.) làm điều tương tự, vì vậy đây có thể chỉ là một lỗ hổng nhỏ cuối cùng trở thành một phần của ngôn ngữ.
Bạn vẫn sẽ gặp lỗi nếu bạn sử dụng sai.
Trong Java, một Mảng lưu trữ độ dài của nó một cách riêng biệt với cấu trúc thực sự chứa dữ liệu. Khi bạn tạo một Mảng, bạn chỉ định độ dài của nó và điều đó trở thành một thuộc tính xác định của Mảng. Bất kể bạn làm gì với Mảng có độ dài N (thay đổi giá trị, bỏ trống, v.v.), nó sẽ luôn là Mảng có độ dài N.
Độ dài của một chuỗi là ngẫu nhiên; nó không phải là một thuộc tính của Chuỗi, mà là một sản phẩm phụ. Mặc dù các chuỗi Java trên thực tế là bất biến, nhưng nếu có thể thay đổi nội dung của chúng, bạn có thể thay đổi độ dài của chúng. Bỏ ký tự cuối cùng (nếu có thể) sẽ làm giảm độ dài.
Tôi hiểu rằng đây là một sự khác biệt tốt và tôi có thể bị bỏ phiếu cho nó, nhưng đó là sự thật. Nếu tôi tạo một Mảng có độ dài 4, độ dài 4 đó là một đặc điểm xác định của Mảng và đúng bất kể những gì được giữ bên trong. Nếu tôi tạo một Chuỗi có chứa "chó", thì Chuỗi đó có độ dài 4 vì nó tình cờ chứa bốn ký tự.
Tôi xem đây là sự biện minh cho việc làm một cái với một thuộc tính và cái kia với một phương thức. Thật ra, đó có thể chỉ là một sự mâu thuẫn vô tình, nhưng nó luôn có ý nghĩa đối với tôi, và đây luôn là cách tôi nghĩ về nó.
Tôi đã được dạy rằng đối với mảng, độ dài không được truy xuất thông qua một phương thức do lo ngại sau: người lập trình sẽ chỉ định độ dài cho một biến cục bộ trước khi vào một vòng lặp (hãy nghĩ đến vòng lặp for trong đó điều kiện sử dụng độ dài của mảng.) lẽ ra sẽ làm như vậy để cắt bớt các lệnh gọi hàm (và do đó cải thiện hiệu suất.) Vấn đề là độ dài có thể thay đổi trong vòng lặp, và biến thì không.
Bất cứ khi nào một mảng được tạo, kích thước của nó được chỉ định. Vì vậy có thể coi chiều dài là một thuộc tính xây dựng. Đối với String, về cơ bản nó là một mảng char. Độ dài là một thuộc tính của mảng char. Không cần đặt độ dài như một trường, vì không phải mọi thứ đều cần trường này. http://www.programcreek.com/2013/11/start-from-length-length-in-java/
Tôi chỉ muốn thêm một số nhận xét vào câu trả lời tuyệt vời của Fredrik .
Các ngôn ngữ kỹ thuật Java trong Mục 4.3.1 bang
Một đối tượng là một cá thể lớp hoặc một mảng .
Vì vậy, mảng thực sự có một vai trò rất đặc biệt trong Java. Tôi tự hỏi tại sao.
Người ta có thể tranh luận rằng mảng triển khai hiện tại là / quan trọng để có hiệu suất tốt hơn. Nhưng nó là một cấu trúc bên trong, không nên để lộ ra ngoài.
Tất nhiên họ có thể đã che thuộc tính dưới dạng một cuộc gọi phương thức và xử lý nó trong trình biên dịch nhưng tôi nghĩ sẽ còn khó hiểu hơn nếu có một phương thức trên một thứ không phải là một lớp thực.
Tôi đồng ý với Fredrik, rằng một tối ưu hóa trình biên dịch thông minh sẽ là lựa chọn tốt hơn. Điều này cũng sẽ giải quyết vấn đề, rằng ngay cả khi bạn sử dụng một thuộc tính cho mảng, bạn vẫn chưa giải quyết được vấn đề cho chuỗi và các kiểu tập hợp (không thay đổi) khác, bởi vì, ví dụ, string
dựa trên một char
mảng như bạn có thể thấy trên định nghĩa lớp của String
:
public final class String implements java.io.Serializable, Comparable<String>, CharSequence {
private final char value[]; // ...
Và tôi không đồng ý với điều đó sẽ còn khó hiểu hơn, bởi vì mảng kế thừa tất cả các phương thức từjava.lang.Object
.
Là một kỹ sư, tôi thực sự không thích câu trả lời "Bởi vì nó luôn luôn như vậy." và ước sẽ có một câu trả lời tốt hơn. Nhưng trong trường hợp này thì có vẻ như vậy.
tl; dr
Theo tôi, đó là một lỗi thiết kế của Java và lẽ ra không nên thực hiện theo cách này.