是啥导致 java.lang.ArrayIndexOutOfBoundsException 以及如何防止它?
Posted
技术标签:
【中文标题】是啥导致 java.lang.ArrayIndexOutOfBoundsException 以及如何防止它?【英文标题】:What causes a java.lang.ArrayIndexOutOfBoundsException and how do I prevent it?是什么导致 java.lang.ArrayIndexOutOfBoundsException 以及如何防止它? 【发布时间】:2011-04-05 15:54:03 【问题描述】:ArrayIndexOutOfBoundsException
是什么意思,我该如何摆脱它?
下面是一个触发异常的代码示例:
String[] names = "tom", "bob", "harry" ;
for (int i = 0; i <= names.length; i++)
System.out.println(names[i]);
【问题讨论】:
关于最后一个问题,代码会有所帮助。你是用已知索引访问数组,还是必须开始调试才能弄清楚错误发生时索引是如何计算的? 用i < name.length
替换i <= name.length
- 或者更好,编写一个增强的for循环。 (for (String aName : name) ...
)
这意味着,你想得到不存在的数组元素,'i
en.wikipedia.org/wiki/Off-by-one_error
当您尝试操作的索引超过数组的长度时,数组超出范围。为了正确起见,您的索引应始终比总数小一。数组元素的数量,因为数组索引从 0 而不是 1 开始。
【参考方案1】:
你的第一个停靠港应该是documentation,它解释得很清楚:
抛出以指示已使用非法索引访问了数组。索引为负数或大于等于数组的大小。
例如:
int[] array = new int[5];
int boom = array[10]; // Throws the exception
至于如何避免...嗯,不要那样做。小心你的数组索引。
人们有时会遇到的一个问题是认为数组是 1 索引的,例如
int[] array = new int[5];
// ... populate the array here ...
for (int index = 1; index <= array.length; index++)
System.out.println(array[index]);
这将错过第一个元素(索引 0)并在索引为 5 时抛出异常。这里的有效索引是 0-4 包括在内。这里正确的、惯用的for
语句是:
for (int index = 0; index < array.length; index++)
(当然,这是假设您需要索引。如果您可以改用增强的 for 循环,请这样做。)
【讨论】:
我要补充一点,对于在 Java 中可能具有任意形状的多维数组,嵌套循环应该检查相关的子数组长度:for (int nestedIndex = 0; nestedIndex < array[outerIndex].length; nestedIndex++) ... array[outerIndex][nestedIndex] ...
。
可以在答案中提到应该查阅堆栈跟踪以找到发生错误的确切行。 IMO,这是第一个停靠港。
@rghome:这不是“ArrayIndexOutOfBoundsException 是什么意思”的答案。我认为在尝试了解代码中的原因之前,您需要了解它的含义。如果我告诉您在给定的代码行中出现 GrobulatorError,如果您不知道 GrobulatorError 是什么,您将如何确定导致它的原因?
是的 - 但是。我们得到了 lot 的这些,因此需要找到 close-as-duplicate 的链接。通常,OP 甚至还没有确定它发生的代码行。还有一个关于如何分析堆栈跟踪的问题,但我认为最好有一个查找 IOOB 的程序,这样有人可以关闭问题,确信 OP 可以通过链接自己解决它.
@rghome:规范的问答听起来不错,但我认为这不一定是正确的。或者即使它是正确的,我仍然会说首先要了解错误的含义,然后再查看代码以了解它发生的原因。【参考方案2】:
if (index < 0 || index >= array.length)
// Don't use this index. This is out of bounds (borders, limits, whatever).
else
// Yes, you can safely use this index. The index is present in the array.
Object element = array[index];
另见:
The Java Tutorials - Language Basics - Arrays更新:根据您的代码 sn-p,
for (int i = 0; i<=name.length; i++)
索引包括数组的长度。这是越界了。您需要将<=
替换为<
。
for (int i = 0; i < name.length; i++)
【讨论】:
【参考方案3】:来自这篇优秀的文章:ArrayIndexOutOfBoundsException in for loop
简单地说:
在最后一次迭代中
for (int i = 0; i <= name.length; i++)
i
将等于 name.length
,这是一个非法索引,因为数组索引是从零开始的。
你的代码应该是这样的
for (int i = 0; i < name.length; i++)
^
【讨论】:
【参考方案4】:这意味着您正在尝试访问一个无效的数组索引,因为它不在边界之间。
例如,这将初始化一个上限为 4 的原始整数数组。
int intArray[] = new int[5];
程序员从零开始计数。因此,例如,这会抛出一个ArrayIndexOutOfBoundsException
,因为上限是 4 而不是 5。
intArray[5];
【讨论】:
Bounds 是范围内的限制。 IE。; 0-5【参考方案5】:什么导致ArrayIndexOutOfBoundsException
?
如果您将变量视为可以放置值的“盒子”,那么数组就是一系列彼此相邻放置的盒子,其中盒子的数量是一个有限且明确的整数。
像这样创建一个数组:
final int[] myArray = new int[5]
创建一行 5 个框,每个框都有一个 int
。每个盒子都有一个索引,即在一系列盒子中的位置。该索引从 0 开始,到 N-1 结束,其中 N 是数组的大小(框数)。
要从这一系列框中检索其中一个值,您可以通过它的索引来引用它,如下所示:
myArray[3]
这将为您提供系列中第 4 个框的值(因为第一个框的索引为 0)。
ArrayIndexOutOfBoundsException
是由于尝试检索一个不存在的“盒子”、传递的索引高于最后一个“盒子”的索引或为负数而引起的。
在我的运行示例中,这些代码 sn-ps 会产生这样的异常:
myArray[5] //tries to retrieve the 6th "box" when there is only 5
myArray[-1] //just makes no sense
myArray[1337] //waay to high
如何避免ArrayIndexOutOfBoundsException
为了防止ArrayIndexOutOfBoundsException
,有几个关键点需要考虑:
循环
在遍历数组时,请始终确保您检索的索引严格小于数组的长度(框数)。例如:
for (int i = 0; i < myArray.length; i++)
注意<
,切勿在其中混入=
..
你可能想做这样的事情:
for (int i = 1; i <= myArray.length; i++)
final int someint = myArray[i - 1]
别这样。坚持上面那个(如果你需要使用索引的话),它会为你省去很多痛苦。
在可能的情况下,使用 foreach:
for (int value : myArray)
这样您就不必考虑索引了。
循环时,无论你做什么,都不要改变循环迭代器的值(这里:i
)。这应该改变值的唯一地方是保持循环继续。否则更改它只会冒异常的风险,并且在大多数情况下是不必要的。
检索/更新
在检索数组的任意元素时,始终检查它是否是数组长度的有效索引:
public Integer getArrayElement(final int index)
if (index < 0 || index >= myArray.length)
return null; //although I would much prefer an actual exception being thrown when this happens.
return myArray[index];
【讨论】:
【参考方案6】:为避免数组索引越界异常,应在可以使用的位置和时间使用enhanced-for
语句。
主要动机(和用例)是在您进行迭代并且不需要任何复杂的迭代步骤时。您将不能够使用增强型-for
在数组中向后移动或仅迭代每个其他元素。
保证您在执行此操作时不会用完要迭代的元素,并且您的 [更正] 示例很容易转换。
以下代码:
String[] name = "tom", "dick", "harry";
for(int i = 0; i< name.length; i++)
System.out.print(name[i] + "\n");
...等价于:
String[] name = "tom", "dick", "harry";
for(String firstName : name)
System.out.println(firstName + "\n");
【讨论】:
【参考方案7】:在您的代码中,您访问了从索引 0 到字符串数组长度的元素。 name.length
给出字符串对象数组中字符串对象的数量,即 3,但您最多只能访问索引 2 name[2]
,
因为可以从索引 0 到 name.length - 1
访问数组,您可以在其中获得 name.length
的对象数。
即使在使用for
循环时,您也是从索引零开始的,并且应该以name.length - 1
结束。在数组 a[n] 中,您可以从 a[0] 访问 a[n-1]。
例如:
String[] a="str1", "str2", "str3" ..., "strn";
for(int i=0; i<a.length(); i++)
System.out.println(a[i]);
在你的情况下:
String[] name = "tom", "dick", "harry";
for(int i = 0; i<=name.length; i++)
System.out.print(name[i] +'\n');
【讨论】:
【参考方案8】:对于给定的数组,数组的长度为 3(即 name.length = 3)。但由于它存储从索引 0 开始的元素,因此它的最大索引为 2。
因此,您应该编写 'iname.length' 以避免出现 'ArrayIndexOutOfBoundsException'。
【讨论】:
【参考方案9】:这个简单的问题就这么多,但我只想强调 Java 中的一个新特性,它可以避免数组索引的所有混淆,即使对于初学者也是如此。 Java-8 已经为您抽象了迭代的任务。
int[] array = new int[5];
//If you need just the items
Arrays.stream(array).forEach(item -> println(item); );
//If you need the index as well
IntStream.range(0, array.length).forEach(index -> println(array[index]); )
有什么好处?好吧,一件事是像英语这样的可读性。其次,您不必担心ArrayIndexOutOfBoundsException
【讨论】:
【参考方案10】:由于i<=name.length
部分,您将获得ArrayIndexOutOfBoundsException
。 name.length
返回字符串name
的长度,即3。因此当你尝试访问name[3]
时,它是非法的并抛出异常。
解决的代码:
String[] name = "tom", "dick", "harry";
for(int i = 0; i < name.length; i++) //use < insteadof <=
System.out.print(name[i] +'\n');
在Java language specification中定义:
public final
字段length
,其中包含组件的数量 的数组。length
可能是正数或零。
【讨论】:
【参考方案11】:这就是这种类型的异常在 Eclipse 中抛出时的样子。红色数字表示您尝试访问的索引。所以代码看起来像这样:
myArray[5]
当您尝试访问该数组中不存在的索引时会引发错误。如果一个数组的长度为3,
int[] intArray = new int[3];
那么唯一有效的索引是:
intArray[0]
intArray[1]
intArray[2]
如果一个数组的长度为1,
int[] intArray = new int[1];
那么唯一有效的索引是:
intArray[0]
任何等于数组长度或大于它的整数:超出范围。
任何小于0的整数:越界;
P.S.:如果您希望对数组有更好的理解并进行一些实践练习,这里有一个视频:tutorial on arrays in Java
【讨论】:
【参考方案12】:对于多维数组,确保访问正确维度的length
属性可能会很棘手。以如下代码为例:
int [][][] a = new int [2][3][4];
for(int i = 0; i < a.length; i++)
for(int j = 0; j < a[i].length; j++)
for(int k = 0; k < a[j].length; k++)
System.out.print(a[i][j][k]);
System.out.println();
System.out.println();
每个维度都有不同的长度,所以微妙的错误是中间和内部循环使用相同维度的length
属性(因为a[i].length
与a[j].length
相同)。
相反,内部循环应使用a[i][j].length
(或a[0][0].length
,为简单起见)。
【讨论】:
添加了来自 Why does my for loop not work when i change the condition(multidimensional arrays)? 的代码示例,因为此问题被用作任何处理 ArrayIndexOutOfBoundsException 的问题的欺骗目标。【参考方案13】:我见过的看似神秘的 ArrayIndexOutOfBoundsExceptions 最常见的情况,即显然不是由您自己的数组处理代码引起的,是同时使用 SimpleDateFormat。特别是在 servlet 或控制器中:
public class MyController
SimpleDateFormat dateFormat = new SimpleDateFormat("MM/dd/yyyy");
public void handleRequest(ServletRequest req, ServletResponse res)
Date date = dateFormat.parse(req.getParameter("date"));
如果两个线程一起进入 SimplateDateFormat.parse() 方法,您可能会看到 ArrayIndexOutOfBoundsException。注意class javadoc for SimpleDateFormat 的同步部分。
确保您的代码中没有任何地方以并发方式(如在 servlet 或控制器中)访问线程不安全类(如 SimpleDateFormat)。检查您的 servlet 和控制器的所有实例变量以找出可能的嫌疑人。
【讨论】:
【参考方案14】:对于任何长度为 n 的数组,该数组的元素将具有从 0 到 n-1 的索引。
如果您的程序试图访问数组索引大于 n-1 的任何元素(或内存),那么 Java 将抛出 ArrayIndexOutOfBoundsException
所以这里有两个我们可以在程序中使用的解决方案
保持计数:
for(int count = 0; count < array.length; count++)
System.out.println(array[count]);
或其他一些循环语句,如
int count = 0;
while(count < array.length)
System.out.println(array[count]);
count++;
更好的方法是使用 for each 循环,在这种方法中,程序员无需担心数组中的元素数量。
for(String str : array)
System.out.println(str);
【讨论】:
【参考方案15】:ArrayIndexOutOfBoundsException 每当出现此异常时,这意味着您正在尝试使用超出其界限的数组索引,或者用外行术语来说,您请求的数量超出了您的初始化范围。
为防止这种情况,请始终确保您没有请求数组中不存在的索引,即如果数组长度为 10,那么您的索引必须介于 0 到 9 之间
【讨论】:
【参考方案16】:ArrayIndexOutOfBounds 表示您正在尝试索引未分配的数组中的位置。
在这种情况下:
String[] name = "tom", "dick", "harry" ;
for (int i = 0; i <= name.length; i++)
System.out.println(name[i]);
name.length 为 3,因为该数组已使用 3 个 String 对象定义。
访问数组的内容时,位置从0开始。由于有3个项目,这意味着name[0]="tom"、name[1]="dick"和name[2]="harry
循环时,由于 i 可能小于或等于 name.length,因此您尝试访问不可用的 name[3]。
要解决这个问题...
在你的 for 循环中,你可以做 i
for(int i = 0; i<name.length; i++)
为每个循环使用一个
String[] name = "tom", "dick", "harry" ;
for(String n : name)
System.out.println(n);
使用 list.forEach(Consumer action)(需要 Java8)
String[] name = "tom", "dick", "harry" ;
Arrays.asList(name).forEach(System.out::println);
将数组转换为流 - 如果您想对数组执行额外的“操作”,这是一个不错的选择,例如过滤、转换文本、转换为地图等(需要 Java8)
String[] name = "tom", "dick", "harry" ;
--- Arrays.asList(name).stream().forEach(System.out::println);
--- Stream.of(name).forEach(System.out::println);
【讨论】:
【参考方案17】:ArrayIndexOutOfBoundsException
表示您正在尝试访问不存在或超出该数组边界的数组索引。数组索引从 0 开始,到 length - 1 结束。
你的情况
for(int i = 0; i<=name.length; i++)
System.out.print(name[i] +'\n'); // i goes from 0 to length, Not correct
ArrayIndexOutOfBoundsException
在您尝试访问时发生
不存在的 name.length 索引元素(数组索引以长度 -1 结束)。只需将
for(int i = 0; i < name.length; i++)
System.out.print(name[i] +'\n'); // i goes from 0 to length - 1, Correct
【讨论】:
【参考方案18】:根据您的代码:
String[] name = "tom", "dick", "harry";
for(int i = 0; i<=name.length; i++)
System.out.print(name[i] +'\n');
如果你检查 System.out.print(name.length);
你会得到 3 个;
这意味着你的名字长度是 3
您的循环从 0 运行到 3 应该运行“0到2”或“1到3”
回答
String[] name = "tom", "dick", "harry";
for(int i = 0; i<name.length; i++)
System.out.print(name[i] +'\n');
【讨论】:
【参考方案19】:数组中的每一项都称为一个元素,每个元素都可以通过其数字索引来访问。如上图所示,编号从0开始。例如,第 9 个元素将在索引 8 处访问。
IndexOutOfBoundsException 被抛出以指示某种索引(例如数组、字符串或向量)超出范围。
任意数组X,可以从[0到(X.length - 1)]访问
【讨论】:
【参考方案20】:我在这里看到了所有解释如何使用数组以及如何避免索引越界异常的答案。我个人不惜一切代价避免使用数组。我使用 Collections 类,它避免了必须完全处理数组索引的所有愚蠢。循环结构与支持更易于编写、理解和维护的代码的集合完美配合。
【讨论】:
【参考方案21】:如果您使用数组的长度来控制 for 循环的迭代,请始终记住数组中第一项的索引是 0。所以数组中最后一个元素的索引比数组的长度小一。
【讨论】:
【参考方案22】:ArrayIndexOutOfBoundsException
名称本身解释说,如果您尝试访问超出数组大小范围的索引处的值,则会发生此类异常。
在你的情况下,你可以从你的 for 循环中删除等号。
for(int i = 0; i<name.length; i++)
更好的选择是迭代一个数组:
for(String i : name )
System.out.println(i);
【讨论】:
【参考方案23】:此错误发生在运行循环超限时间。让我们考虑这样的简单示例,
class demo
public static void main(String a[])
int[] numberArray=4,8,2,3,89,5;
int i;
for(i=0;i<numberArray.length;i++)
System.out.print(numberArray[i+1]+" ");
首先,我将一个数组初始化为“numberArray”。然后,使用 for 循环打印一些数组元素。当循环运行 'i' 时间时,打印 (numberArray[i+1] 元素..(当 i 值为 1 时,打印 numberArray[i+1] 元素。)..假设,当 i=(numberArray.长度-2),数组的最后一个元素被打印出来。当'i'值变为(numberArray.length-1)时,没有打印值。在这一点上,'ArrayIndexOutOfBoundsException'发生。我希望你能得到想法。谢谢!
【讨论】:
【参考方案24】:您可以在函数样式中使用 Optional 来避免 NullPointerException
和 ArrayIndexOutOfBoundsException
:
String[] array = new String[]"aaa", null, "ccc";
for (int i = 0; i < 4; i++)
String result = Optional.ofNullable(array.length > i ? array[i] : null)
.map(x -> x.toUpperCase()) //some operation here
.orElse("NO_DATA");
System.out.println(result);
输出:
AAA
NO_DATA
CCC
NO_DATA
【讨论】:
【参考方案25】:在大多数编程语言中,索引都是从 0 开始的。所以你必须改写 i
【讨论】:
这个问题与“大多数”编程语言无关。它是关于 Java 的。 (无论如何,10 年前投票/接受的最高答案就是这样。)【参考方案26】:您无法迭代或存储超过数组长度的数据。在这种情况下,您可以这样做:
for (int i = 0; i <= name.length - 1; i++)
// ....
或者这个:
for (int i = 0; i < name.length; i++)
// ...
【讨论】:
以上是关于是啥导致 java.lang.ArrayIndexOutOfBoundsException 以及如何防止它?的主要内容,如果未能解决你的问题,请参考以下文章