Java 中 array.length() 的内部代码是什么?
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/4910027/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
What is inside code for array.length() in Java?
提问by ranjanarr
What is stored in 10th location of array say
存储在数组的第 10 个位置的内容说
int[] array=new int[10];
Say we have values stored from array[0]
to array[9]
, if I were to print elements without using
假设我们有从array[0]
to存储的值array[9]
,如果我要打印元素而不使用
array.length()
or for (int a: array)
或者 for (int a: array)
How do I proceed?
我该如何进行?
My basic question is how will JVM determine end of array, is it when a null is encountered parsing array or when a garbage value is encountered? what is inbuilt code of array.length()
function?
我的基本问题是 JVM 如何确定数组的结尾,是在解析数组时遇到空值还是遇到垃圾值?什么是array.length()
函数的内置代码?
回答by RoflcoptrException
Arrays are objects with a length field. While looping, Java loads the length field and compares the iterator against it.
数组是具有长度字段的对象。循环时,Java 加载长度字段并将迭代器与它进行比较。
See 10.7 Array Membersin the JLS
请参阅JLS 中的10.7 数组成员
回答by Bert F
What is stored in 10th location of array say ... my basic question is how will JVM determine end of array, is it when a null is encountered parsing array or when a garbage value is encountered? what is inbuilt code of array.length() function?
什么存储在数组的第 10 个位置说......我的基本问题是 JVM 将如何确定数组的结尾,是在解析数组时遇到空值还是遇到垃圾值?array.length() 函数的内置代码是什么?
Welcome C/C++ programmer :-)
欢迎 C/C++ 程序员 :-)
Java uses a different paradigm than C/C++ for arrays. C/C++ uses the terminator/sentinel a.k.a. "garbage") value like NULL to indicate the end of the array. In Java, arrays are more like objects with a special "instance variable"-like variable length
that indicates how many slots there are in the array. This special "instance variable" is set at the array's creation and is read-only. Its accessible by saying array.length
.
Java 使用与 C/C++ 不同的范式来处理数组。C/C++ 使用终结符/哨兵,又名“垃圾”)值,如 NULL 来指示数组的结尾。在 Java 中,数组更像是具有特殊“实例变量”之类的变量的对象,该变量length
指示数组中有多少个插槽。这个特殊的“实例变量”是在创建数组时设置的,并且是只读的。它可以通过说 array 来访问.length
。
Java expects the code to know when to stop at the end of the array by making sure they don't specify an index greater than length - 1
. However, the JVM checks every access to the array for security reasons just in case. If the JVM finds an array index that is less than 0
or greater than length - 1
, then the JVM throws an IndexOutOfBoundsException
.
Java 希望代码通过确保它们不指定大于 的索引来知道何时在数组末尾停止length - 1
。但是,出于安全原因,JVM 会检查对数组的每次访问以防万一。如果 JVM 找到小于0
或大于的数组索引length - 1
,则 JVM 将抛出IndexOutOfBoundsException
.
What is stored in 10th location of array
什么存储在数组的第 10 个位置
Since we can always check the length, there is no need for a marker at the end of the array in Java. There isn't anything special after the last item in the array (it likely will be some other variable's memory).
由于我们始终可以检查长度,因此 Java 中不需要在数组末尾添加标记。在数组中的最后一项之后没有什么特别的(它可能是一些其他变量的内存)。
if I were to print elements without using
array.length()
如果我要在不使用的情况下打印元素
array.length()
for(int a: array) {
// code of loop body here
}
This code is magically transformed by the compiler to:
这段代码被编译器神奇地转换为:
for (int i = 0; i < array.length; i++) {
int a = array[i];
// code of loop body here
}
However, the i
index variable isn't accessible to the user's code. This code still uses array.length implicitly.
但是,i
用户代码无法访问索引变量。此代码仍隐式使用 array.length。
回答by templatetypedef
Internally, the JVM can track the length of an array however it sees fit. There's actually a bytecode instruction called arraylength
that the Java compiler emits whenever you try to get the length of an array, indicating that it's up to the JVM to determine the best way to track the length of an array.
在内部,JVM 可以跟踪数组的长度,但它认为合适。实际上arraylength
,每当您尝试获取数组的长度时,Java 编译器都会发出一个字节码指令,该指令表示由 JVM 来确定跟踪数组长度的最佳方法。
Most implementations probably store arrays as a block of memory whose first entry is the length of the array and whose remaining elements are the actual array values. This allows the implementation to query the length of the array, along with any value in the array, in O(1). If the implementation wanted to, though, it could store the elements followed by a sentinel value (as you've suggested), but I don't believe that any implementations do this because the cost of looking up the length would be linear in the size of the array.
大多数实现可能将数组存储为一个内存块,它的第一个条目是数组的长度,其余元素是实际的数组值。这允许实现以 O(1) 查询数组的长度以及数组中的任何值。但是,如果实现想要,它可以存储元素后跟一个标记值(如您所建议的那样),但我不相信任何实现会这样做,因为查找长度的成本在数组的大小。
As for how the foreach loop works, the compiler translates that code into something like this:
至于 foreach 循环的工作原理,编译器将该代码转换为如下内容:
for (int i = 0; i < arr.length; ++i) {
T arrayElem = arr[i];
/* ... do work here ... */
}
And finally, with regards as to what the 10th element of a 10-element array is, there's no guarantee that there's even an object at that location. The JVM could easily allocate space for the array in a way where there is no tenth element. Since you can't ever actually get this value in Java (it would throw an exception if you tried), there's no requirement that the JVM even have something meaningful there.
最后,关于 10 元素数组的第 10 个元素是什么,不能保证在那个位置甚至有一个对象。JVM 可以轻松地以没有第十个元素的方式为数组分配空间。由于您实际上无法在 Java 中获得此值(如果您尝试,它会抛出异常),因此不需要 JVM 甚至在那里有一些有意义的东西。
Hope this helps!
希望这可以帮助!
回答by Donnie
Define what a "garbage value" is. (Hint: since everything is binary, there is no such thing unless you use a sentinel value, and that's just bad practice).
定义什么是“垃圾值”。(提示:由于一切都是二进制的,除非您使用哨兵值,否则没有这样的事情,这只是不好的做法)。
The length of the array is stored inside the Array
instance as a member variable. It's nothing complex.
数组的长度Array
作为成员变量存储在实例中。没什么复杂的。
回答by Donnie
Okay, here I go :-)
好的,我来了:-)
Ways to deal with "arrays" in C
在 C 中处理“数组”的方法
In C there are numerous ways to deal with array. For the remainder I will talk about string*
(and use the variable strings
which has a type of string*
). This is because t[]
"effectively decomposes" into t*
and char*
is the type of a "C string". Thus string*
represents a pointer to "C string". This glosses over a number of pedantic issues in C w.r.t. "arrays" and "pointers". (Remember: just because a pointer can be accessed as p[i]
doesn't make the typean array in C parlance.)
在 C 中有很多方法来处理数组。对于其余部分,我将讨论string*
(并使用strings
类型为的变量string*
)。这是因为t[]
“可以有效分解”成t*
和char*
是一个“C字符串”的类型。因此string*
表示指向“C 字符串”的指针。这掩盖了 C 写“数组”和“指针”中的许多迂腐问题。(请记住:仅仅因为可以访问指针p[i]
并不能使该类型成为 C 语言中的数组。)
Now, strings
(of type string*
) has no way to know it's size -- it only represents a pointerto some string, or NULL perhaps. Now, let's look at some of the ways we can "know" the size:
现在,strings
(类型string*
)无法知道它的大小——它只代表一个指向某个字符串的指针,或者可能是 NULL。现在,让我们看看我们可以“知道”大小的一些方法:
Use a sentinel value.In this I am assuming the use NULL as the sentinel value (or it might be -1 for an "array" of integers, etc.). Remember that C has no such requirement that arrays have a sentinel value so this approach, like the following two, is just convention.
使用哨兵值。在此,我假设使用 NULL 作为标记值(或者对于整数“数组”等,它可能是 -1)。请记住,C 没有这样的要求,即数组具有标记值,因此这种方法,如以下两种,只是约定。
string* p;
for (p = strings; p != NULL; p++) {
doStuff(*p);
}
Track the array size externally.
在外部跟踪数组大小。
void display(int count, string* strings) {
for (int i = 0; i < count; i++) {
doStuff(strings[i]);
}
}
Bundle the "array" and the length together.
将“数组”和长度捆绑在一起。
struct mystrarray_t {
int size;
string* strings;
}
void display(struct mystrarray_t arr) {
for (int i = 0; i < arr.size i++) {
doStuff(arr.strings[i]);
}
}
Java uses this last approach.
Java 使用最后一种方法。
Every array objectin Java has a fixed sized which can be accessed as arr.length
. There is special byte-code magic to make this work (arrays are very magical in Java), but at the language levelthis is exposed as just a read-only integer field that never changes(remember, each array object has a fixed size). Compilers and the JVM/JIT can take advantage of this fact to optimize the loop.
Java 中的每个数组对象都有一个固定大小,可以作为arr.length
. 有一种特殊的字节码魔法来完成这项工作(数组在 Java 中非常神奇),但在语言级别,这只是一个永远不会改变的只读整数字段(请记住,每个数组对象都有固定的大小) . 编译器和 JVM/JIT 可以利用这一事实来优化循环。
Unlike C, Java guaranteesthat trying to access an index out of bounds willresult in an Exception (for performance reasons, even if it were not exposed, this would require the JVM kept track of the length of each array). In C this is just undefined behavior. For instance, if the sentinel value wasn't within the object(read "the desired accessibly memory") then example #1 would have lead to a buffer-overflow.
与 C 不同,Java保证尝试越界访问索引将导致异常(出于性能原因,即使未公开,这也需要 JVM 跟踪每个数组的长度)。在 C 中,这只是未定义的行为。例如,如果标记值不在对象内(读取“所需的可访问内存”),那么示例 #1 将导致缓冲区溢出。
However, there is nothing to prevent one from using sentinel values in Java. Unlike the C form with a sentinel value, this is also safe from IndexOutOfBoundExceptions (IOOB) because the length-guard is the ultimate limit. The sentinel is just a break-early.
但是,没有什么可以阻止人们在 Java 中使用标记值。与带有标记值的 C 形式不同,这对于 IndexOutOfBoundExceptions (IOOB) 也是安全的,因为长度保护是最终限制。哨兵只是一个破发的早。
// So we can add up to 2 extra names later
String names[] = { "Fred", "Barney", null, null };
// This uses a sentinel *and* is free of an over-run or IOB Exception
for (String n : names) {
if (n == null) {
break;
}
doStuff(n);
}
Or possibly allowing an IOOB Exception because we do something silly like ignore the fact that arrays know their length: (See comments wrt "performance").
或者可能允许 IOOB 异常,因为我们做了一些愚蠢的事情,比如忽略数组知道它们的长度这一事实:(参见“性能”评论)。
// -- THERE IS NO EFFECTIVE PERFORMANCE GAIN --
// Can ONLY add 1 more name since sentinel now required to
// cleanly detect termination condition.
// Unlike C the behavior is still well-defined, just ill-behaving.
String names[] = { "Fred", "Barney", null, null };
for (int i = 0;; i++) {
String n = strings[i];
if (n == null) {
break;
}
doStuff(n);
}
On the other hand, I would discourage the use of such primitive code-- better to just use a suitable data-type such as a List in almostall cases.
另一方面,我不鼓励使用这种原始代码——在几乎所有情况下,最好只使用合适的数据类型,例如 List 。
Happy coding.
快乐编码。
回答by Stephen C
In a comment on another, the OP writes:
在对另一个的评论中,OP 写道:
I agree array.length is the conventional method, I was looking for any other option if available.
我同意 array.length 是常规方法,如果可用,我正在寻找任何其他选项。
There is no other reasonable implementation option open to the JVM implementer ... on any mainstream hardware architecture.
JVM 实现者没有其他合理的实现选项......在任何主流硬件架构上。
In particular, the sentinel approach ONLY detects the case where an application fetchesan array element oneindex beyond the end.
特别是,哨兵方法仅检测应用程序获取数组元素超出末尾一个索引的情况。
- If it fetches 2 or more indexes beyond, then it misses the sentinel and proceeds to access memory whose contents are unknown.
- If it stores, then the sentinel is not consulted.
- If it needs to directly access the array size as part of the application algorithm, searching for a sentinel is a very inefficient way of doing it. (Not to mention unreliable; e.g. if
null
is a valid array element.) - Sentinels don't work for (most) primitive arrays because there is no value that can be used as a sentinel. (The idea of a primitive array holding a
null
is nonsensical from the JLS perspective, sincenull
is not type compatible with any Java primitive type.) - The garbage collector needs an array length in all cases.
- 如果它获取超过 2 个或更多索引,则它会错过标记并继续访问内容未知的内存。
- 如果它存储,则不咨询哨兵。
- 如果它需要直接访问数组大小作为应用程序算法的一部分,那么搜索哨兵是一种非常低效的方法。(更不用说不可靠了;例如 if
null
是一个有效的数组元素。) - 哨兵不适用于(大多数)原始数组,因为没有可用作哨兵的值。(
null
从 JLS 的角度来看,保存 a 的原始数组的想法是荒谬的,因为null
它与任何 Java 原始类型都不兼容。) - 垃圾收集器在所有情况下都需要一个数组长度。
In short, the length has to be stored in the array to deal with the other cases. Storing a sentinel as well means you are wasting space storing redundant information, and CPU cycles creating the sentinel and copying it (in the GC).
简而言之,长度必须存储在数组中以处理其他情况。存储哨兵也意味着你在浪费存储冗余信息的空间,以及创建哨兵和复制它的 CPU 周期(在 GC 中)。
回答by Pa?lo Ebermann
Allarray access outside the interval [0, 9] gives an ArrayIndexOutOfBoundsException
, not only position 10. So, conceptually you could say that your whole memory (reaching with indexes from Integer.MIN_VALUE
to Integer.MAX_VALUE
) is filled with sentinel values, apart from the space of the array itself, and when reading or writing to a position filled with a sentinel, you get your exception. (And each array has its own whole memoryto spend).
Of course, in reality no one has a whole memory for each array to spend, so the VM implements the array accesses a bit smarter. You can imagine something like this:
区间 [0, 9] 之外的所有数组访问都给出一个ArrayIndexOutOfBoundsException
,而不仅仅是位置 10。因此,从概念上讲,您可以说整个内存(从Integer.MIN_VALUE
to到达的索引Integer.MAX_VALUE
)都充满了哨兵值,除了数组本身的空间,当读取或写入一个充满哨兵的位置时,你会得到你的例外。(并且每个数组都有自己的整个内存要花费)。当然,实际上没有人有一个完整的内存供每个数组使用,因此 VM 实现数组访问更聪明一点。你可以想象这样的事情:
class Array<X> {
private final int length;
private final Class<X> componentType;
/**
* invoked on new X[len] .
*/
public Array<X>(int len, Class<X> type) {
if(len < 0) {
throw new NegativeArraySizeException("too small: " + len);
}
this.componentType = type;
this.len = len;
// TODO: allocate the memory
// initialize elements:
for (int i = 0; i < len; i++) {
setElement(i, null);
}
}
/**
* invoked on a.length
*/
public int length() {
return length;
}
/**
* invoked on a[i]
*/
public X getElement(int index) {
if(index < 0 || length <= index)
throw new ArrayIndexOutOfBoundsException("out of bounds: " + index);
// TODO: do the real memory access
return ...;
}
/**
* invoked on a[i] = x
*/
public X setElement(int index, X value) {
if(index < 0 || length <= index) {
throw new ArrayIndexOutOfBoundsException("out of bounds: " + index);
}
if(!componentType.isInstance(value)) {
throw new ArrayStoreException("value " + value + " is of type " +
value.getClass().getName() + ", but should be of type "
+ componentType.getName() + "!");
}
// TODO: do the real memory access
return value;
}
}
Of course, for primitive values the component type check is a bit simpler, since already the compiler (and then the VM bytecode verifier) checks that there are the right types, sometimes doing a type conversion, too. (And the initialization would be with the default value of the type, not null.)
当然,对于原始值,组件类型检查要简单一些,因为编译器(然后是 VM 字节码验证器)已经检查是否存在正确的类型,有时也会进行类型转换。(并且初始化将使用类型的默认值,而不是 null。)
回答by Péter T?r?k
how will you print elements without using array.length or foreach loop
你将如何在不使用 array.length 或 foreach 循环的情况下打印元素
You could of course loop through the array without bounds checking and then catch (and swallow) the ArrayIndexOutOfBoundsException
in the end:
您当然可以在没有边界检查的情况下遍历数组,然后在最后捕获(并吞下)ArrayIndexOutOfBoundsException
:
try {
int i = 0;
while (true) {
System.out.println(arr[i++]);
}
catch (ArrayIndexOutOfBoundsException e) {
// so we are past the last array element...
}
This technically works, but it is bad practice. You should not use exceptions for flow control.
这在技术上有效,但这是不好的做法。您不应该使用异常进行流控制。
回答by Michael Berry
In terms of how you'd print all the elements in the array without using either a for each loop or the length
field, well in all honesty you just wouldn't. You could potentially just have a for loop like the following:
就如何在不使用 for each 循环或length
字段的情况下打印数组中的所有元素而言,老实说,您不会这样做。您可能只有一个 for 循环,如下所示:
try {
for(int i=0 ; ; i++) {
System.out.println(arr[i]);
}
}
catch(IndexOutOfBoundsException ex) {}
But that's an awful way to do things!
但这是一种糟糕的做事方式!