什么存储在数组的第10个位置 说
int[] array=new int[10];
如果我要在不使用
的情况下打印元素,请说我们将值从array[0]
存储到array[9]
array.length()
或for (int a: array)
我该如何处理?
我的基本问题是JVM如何确定数组的结尾,是否在遇到解析数组的null或遇到垃圾值时?什么是array.length()
函数的内置代码?
答案 0 :(得分:4)
数组是具有长度字段的对象。循环时,Java加载长度字段并将迭代器与它进行比较。
请参阅JLS中的10.7 Array Members
答案 1 :(得分:4)
什么存储在数组的第10个位置说 ... 我的基本问题是JVM如何确定数组的结束,是否在遇到解析数组的null或遇到垃圾值时?什么是array.length()函数的内置代码?
欢迎使用C / C ++程序员: - )
Java对数组使用与C / C ++不同的范例。 C / C ++使用终结符/ sentinel a.k.a.“garbage”)值来表示数组的结尾。在Java中,数组更像是具有特殊“实例变量”的变量length
的对象,它指示数组中有多少个插槽。这个特殊的“实例变量”在数组的创建时设置,是只读的。通过说数组.length
可以访问它。
Java希望代码通过确保它们不指定大于length - 1
的索引来知道何时在数组末尾停止。但是,出于安全考虑,JVM会检查对阵列的每次访问,以防万一。如果JVM发现数组索引小于0
或大于length - 1
,则JVM会抛出IndexOutOfBoundsException
。
什么存储在数组的第10个位置
由于我们总是可以检查长度,因此Java中数组末尾不需要标记。在数组中的最后一项之后没有任何特殊内容(它可能是其他变量的内存)。
如果我不使用
打印元素array.length()
for(int a: array) {
// code of loop body here
}
编译器将此代码神奇地转换为:
for (int i = 0; i < array.length; i++) {
int a = array[i];
// code of loop body here
}
但是,用户代码无法访问i
索引变量。此代码仍然隐式使用array.length。
答案 2 :(得分:2)
在内部,JVM可以跟踪阵列的长度,但它认为合适。实际上,每当您尝试获取数组的长度时,Java编译器就会发出一个名为arraylength
的字节码指令,指示由JVM决定跟踪数组长度的最佳方法。
大多数实现可能将数组存储为内存块,其第一个条目是数组的长度,其余元素是实际的数组值。这允许实现在O(1)中查询数组的长度以及数组中的任何值。但是,如果实现想要,它可以存储元素后跟一个sentinel值(正如你所建议的那样),但我不相信任何实现都会这样做,因为查找长度的成本是线性的数组的大小。
至于foreach循环如何工作,编译器将该代码转换为如下代码:
for (int i = 0; i < arr.length; ++i) {
T arrayElem = arr[i];
/* ... do work here ... */
}
最后,关于10元素数组的第10个元素是什么,不能保证在该位置甚至有一个对象。 JVM可以以没有第十个元素的方式轻松地为数组分配空间。因为你实际上不可能在Java中实际获得这个值(如果你尝试过它会引发异常),并不要求JVM甚至在那里有一些有意义的东西。
希望这有帮助!
答案 3 :(得分:1)
定义“垃圾值”是什么。 (提示:因为一切都是二进制的,除非你使用哨兵值,否则没有这样的东西,这只是不好的做法)。
数组的长度作为成员变量存储在Array
实例中。这并不复杂。
答案 4 :(得分:1)
好的,我走了: - )
在C中有很多方法可以处理数组。对于其余部分,我将讨论string*
(并使用类型为strings
的变量string*
)。这是因为t[]
“有效地分解”为t*
而char*
是“C字符串”的类型。因此string*
表示指向“C字符串”的指针。这掩盖了C w.r.t中的许多迂腐问题。 “数组”和“指针”。 (请记住:只是因为p[i]
可以访问指针而不能使用C语言将类型作为数组。)
现在,strings
(类型string*
)无法知道它的大小 - 它只代表某个字符串的指针,或者也许是NULL。现在,让我们看看我们可以“知道”大小的一些方法:
使用sentinel值。在此我假设使用NULL作为sentinel值(或者对于整数的“数组”,它可能是-1,等等)。请记住,C没有要求数组具有sentinel值,因此这种方法,如下面的两个,只是约定。
string* p;
for (p = strings; p != NULL; p++) {
doStuff(*p);
}
从外部跟踪数组大小。
void display(int count, string* strings) {
for (int i = 0; i < count; i++) {
doStuff(strings[i]);
}
}
将“数组”和长度捆绑在一起。
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中的每个数组对象都有一个固定大小,可以arr.length
访问。有一些特殊的字节码魔术可以使这项工作(数组在Java中非常神奇),但在语言级别,这只是一个只读的整数字段,永远不会改变< / em>(记住,每个数组对象都有固定的大小)。编译器和JVM / JIT可以利用这一事实来优化循环。
与C不同,Java 保证尝试访问超出范围的索引将导致异常(出于性能原因,即使没有公开,也会要求JVM跟踪每个数组的长度)。在C中,这只是未定义的行为。例如,如果sentinel值不是在对象中(读取“所需的可访问内存”),则示例#1将导致缓冲区溢出。
然而,没有什么可以阻止人们在Java中使用sentinel值。与具有sentinel值的C形式不同,这对IndexOutOfBoundExceptions(100OB)也是安全的,因为长度保护是最终限制。哨兵只是一个休息时间。
// 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);
}
或者可能允许IOOB异常,因为我们做了一些愚蠢的事情,就像忽略了数组知道它们的长度的事实:(参见评论wrt“performance”)。
// -- 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);
}
另一方面,我会阻止使用这种原始代码 - 最好只使用合适的数据类型,例如中的List几乎所有情况
快乐的编码。
答案 5 :(得分:1)
在对另一个人的评论中,OP写道:
我同意array.length是传统的方法,我正在寻找任何其他选项(如果有的话)。
在任何主流硬件架构上,没有其他合理的实现选项可供JVM实施者使用。
特别是,Sentinel方法仅检测应用程序提取数组元素一个索引的情况。
null
是有效的数组元素。)null
的原始数组的想法是没有意义的,因为null
与任何Java基元类型都不兼容。)简而言之,长度必须存储在数组中以处理其他情况。存储哨兵也意味着你浪费存储冗余信息的空间,并且CPU周期创建哨兵并复制它(在GC中)。
答案 6 :(得分:0)
如何在不使用array.length或foreach循环的情况下打印元素
你当然可以在没有边界检查的情况下遍历数组,然后在最后捕获(并吞下)ArrayIndexOutOfBoundsException
:
try {
int i = 0;
while (true) {
System.out.println(arr[i++]);
}
catch (ArrayIndexOutOfBoundsException e) {
// so we are past the last array element...
}
这在技术上有效,但这是不好的做法。您不应该使用异常进行流量控制。
答案 7 :(得分:0)
就如何在不使用每个循环或length
字段的情况下打印数组中的所有元素而言,诚实地说你不会。您可能只需要像以下一样使用for循环:
try {
for(int i=0 ; ; i++) {
System.out.println(arr[i]);
}
}
catch(IndexOutOfBoundsException ex) {}
但这是一种糟糕的做事方式!
答案 8 :(得分:0)
在[0,9]之外的所有数组访问都会得到ArrayIndexOutOfBoundsException
,而不仅仅是位置10.所以,从概念上讲,你可以说你的整个记忆(从{到达索引)除了数组本身的空间外,{1}}到Integer.MIN_VALUE
)都填充了 sentinel values ,当读取或写入填充了标记的位置时,会出现异常。 (并且每个阵列都有自己的整个内存来支出)。
当然,实际上没有人为每个阵列都有一个完整的内存,因此VM实现了更智能的阵列访问。你可以想象这样的事情:
Integer.MAX_VALUE
当然,对于原始值,组件类型检查稍微简单一些,因为编译器(然后是VM字节码验证器)已经检查了正确的类型,有时也进行类型转换。 (并且初始化将使用类型的默认值,而不是null。)