【问题标题】:Java ArrayList remove object - IndexOutOfBoundsExceptionJava ArrayList 删除对象 - IndexOutOfBoundsException
【发布时间】:2016-04-11 03:32:54
【问题描述】:

我正在尝试从 ArrayList 中删除一个对象,但我不断收到 IndexOutOfBounds 错误。现在有很多可用的信息,为什么在 迭代 ArrayList 同时删除 时会发生这种情况,但我没有这样做。 示例:

 ArrayList<Character> a = new ArrayList<Character>();
 a.add('A');
 a.add('B');
 a.add('C');
 System.out.println(a);
 a.remove('A');
 System.out.println(a);

打印[A, B, C],然后失败:

java.lang.IndexOutOfBoundsException: Index: 65, Size: 3
    at java.util.ArrayList.rangeCheck(ArrayList.java:635)
    at java.util.ArrayList.remove(ArrayList.java:474)
    at b.<init>(b.java:23)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance0(Native Method)
    at sun.reflect.NativeConstructorAccessorImpl.newInstance(NativeConstructorAccessorImpl.java:57)
    at sun.reflect.DelegatingConstructorAccessorImpl.newInstance(DelegatingConstructorAccessorImpl.java:45)
    at java.lang.reflect.Constructor.newInstance(Constructor.java:526)
    at bluej.runtime.ExecServer$3.run(ExecServer.java:746)

为什么会这样?

编辑澄清这个问题不是this question的重复:

这里发生的具体问题与迭代时删除元素时的常见问题无关。而是ArrayListremove方法重载和java自动将char类型转换为int造成的。

其他问题的答案未涵盖此方面。

【问题讨论】:

  • 你要使用a.remove(0);
  • 试试:a.remove((Character)'A')
  • 我特别想删除一个对象,而不是一个索引,如此处所述:docs.oracle.com/javase/7/docs/api/java/util/…
  • 是的,但是'A' 是原始类型,因此使用remove(int index) 代替remove(Object o)
  • @Jarrod Roberson 您提到的问题 重复 在这里没有回答我的问题。我完全了解迭代和删除元素时可能发生的问题,但是我的问题的答案是 remove 方法被重载并且从 char 到 int 的类型提升导致它失败。

标签: java arraylist


【解决方案1】:

ArrayList 类有两个方法 remove 的重载。一个带有整数参数,用于删除该索引处的项目,另一个带有Object 参数。

您传递的是char。这既不是int,也不是Object。所以编译器必须决定它使用哪个remove:是将char提升为int,还是将其封装成Character,然后将其提升为Object

Java 中的重载解析总是在不考虑装箱和拆箱的情况下开始。因此它优先考虑remove(int) 过载。所以它取字符A的值,即65,并将其提升为int,这意味着当列表只有三个项目时,它将尝试删除项目#65。

要解决这个问题,您需要明确告诉它使用Character 对象,如:remove(Character.valueOf('A'))

【讨论】:

    【解决方案2】:

    应该将索引传递给Arraylist 的remove 方法。尝试替换这条线 a.remove('A');与

    a.remove(0);

    【讨论】:

    • 这将删除'B'
    【解决方案3】:

    有 2 个重载的 remove 方法 -- one that takes an int as an indexone that takes an Object,用于删除对象引用本身。

    Section 15.12.2 of the JLS 介绍了 Java 如何选择一种方法重载而不是另一种方法。

    阶段是:

    1. 第一阶段(第 15.12.2.2 节)执行重载决议不允许装箱或拆箱转换,或使用可变参数方法调用。如果在此阶段未找到适用的方法,则处理继续到第二阶段。

    这保证了在 Java SE 5.0 之前在 Java 编程语言中有效的任何调用都不会因为引入可变参数方法、隐式装箱和/或拆箱而被视为模棱两可。但是,变量 arity 方法的声明(第 8.4.1 节)可以更改为给定方法方法调用表达式选择的方法,因为变量 arity 方法在第一阶段被视为固定 arity 方法。例如,在已经声明 m(Object) 的类中声明 m(Object...) 会导致不再为某些调用表达式(例如 m(null))选择 m(Object),如 m(Object[] ) 更具体。

    1. 第二阶段(第 15.12.2.3 节)执行重载决议同时允许装箱和拆箱,但仍排除使用可变参数方法调用。如果在此阶段没有找到适用的方法,则处理继续到第三阶段。

    这确保了一个方法永远不会通过变量 arity 方法调用来选择,如果它适用于通过固定 arity 方法调用。

    1. 第三阶段(第 15.12.2.4 节)允许将重载与可变参数方法、装箱和拆箱相结合。

    (我的粗体强调)

    这两种方法在这里都适用,因为char 可以提升为int,但也可以装箱为Character,匹配a 的类型参数。但是Java会在任何需要装箱的方法之前单独选择提升,因此'A'被提升为int,因此值为65。

    如果您想通过对象引用删除,可以将其显式转换为 Character

    a.remove((Character) 'A');
    

    【讨论】:

      【解决方案4】:
      a.remove()
      

      期望为要删除的项目的索引获取一个整数。 'A' 返回 A 的 ASCII 值,即 64,因此超出了数组的范围

      【讨论】:

      • A 实际上是 65,如异常消息中所示。
      • 并不是真正的 ASCII 值,而是 UTF-16 码位,恰好与 Latin1 值相同,恰好与 ASCII 值相同。这有点让我恼火,人们说代码点的 ASCII 值。
      • @DavidConrad 我完全同意你的看法,但我认为你的意思是说 UTF-16 代码 unit。代码 point 是绝对的,而不是编码。
      • @Klitos 谢谢。我确实应该说代码 unit.
      【解决方案5】:

      remove 方法需要一个索引,它会删除指定索引处的项目。您正在传递一个 char,因此它被转换为整数 65。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2015-08-05
        • 2017-08-02
        • 2016-03-22
        • 2014-08-28
        • 2019-02-13
        • 1970-01-01
        • 2010-11-21
        相关资源
        最近更新 更多