9

I am trying to remove an object from an ArrayList, but I keep getting an IndexOutOfBounds Error. Now there is plenty information available why this happens when iterating over the ArrayList while removing, however I'm not doing that. Example:

 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);

prints [A, B, C] and then fails with:

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)

Why is this happening?

EDIT to clarify this question is not a duplicate of this question:

The specific problem that is occuring here has nothing to do with the usual problems when removing elements while iterating. It is rather caused by the overloaded ArrayList remove method and the automatic type conversion from char to int by java.

This aspect is not covered in the answer of the other question.

Stefan
  • 2,006
  • 21
  • 33
  • You want to use `a.remove(0);` – Kevin Mee Jan 06 '16 at 21:44
  • 1
    Try: `a.remove((Character)'A')` – Titus Jan 06 '16 at 21:44
  • I specifically want to remove an object, not an index, as documented here: https://docs.oracle.com/javase/7/docs/api/java/util/ArrayList.html#remove(java.lang.Object) – Stefan Jan 06 '16 at 21:45
  • Yes, but `'A'` is a primitive type and because of that `remove(int index)` is used instead of `remove(Object o)`. – Titus Jan 06 '16 at 21:46
  • @Jarrod Roberson The question you referred to as a **duplicate** does not answer my question here. I am fully aware of the problems that can occur while iterating and removing elements, however the answer to my question is that the remove method is overloaded and the type promotion from char to int causes it to fail. – Stefan Feb 15 '16 at 17:02
  • Possible duplicate of [How to avoid ArrayIndexOutOfBoundsException or IndexOutOfBoundsException?](https://stackoverflow.com/questions/32568261/how-to-avoid-arrayindexoutofboundsexception-or-indexoutofboundsexception) –  Feb 15 '19 at 15:20

5 Answers5

25

There are 2 overloaded remove methods -- one that takes an int as an index, and one that takes an Object, to remove the object reference itself.

Section 15.12.2 of the JLS covers how Java chooses one method overload over another.

The phases are:

  1. The first phase (§15.12.2.2) performs overload resolution without permitting boxing or unboxing conversion, or the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the second phase.

This guarantees that any calls that were valid in the Java programming language before Java SE 5.0 are not considered ambiguous as the result of the introduction of variable arity methods, implicit boxing and/or unboxing. However, the declaration of a variable arity method (§8.4.1) can change the method chosen for a given method method invocation expression, because a variable arity method is treated as a fixed arity method in the first phase. For example, declaring m(Object...) in a class which already declares m(Object) causes m(Object) to no longer be chosen for some invocation expressions (such as m(null)), as m(Object[]) is more specific.

  1. The second phase (§15.12.2.3) performs overload resolution while allowing boxing and unboxing, but still precludes the use of variable arity method invocation. If no applicable method is found during this phase then processing continues to the third phase.

This ensures that a method is never chosen through variable arity method invocation if it is applicable through fixed arity method invocation.

  1. The third phase (§15.12.2.4) allows overloading to be combined with variable arity methods, boxing, and unboxing.

(bold emphasis mine)

Both methods are applicable here, because a char can be promoted to an int, but it can also be boxed to a Character, matching a's type parameter. But Java will choose promotion alone before any method that requires boxing, so 'A' is promoted to int, hence the value 65.

You can cast it explicitly to Character if you want to remove by object reference.

a.remove((Character) 'A');
Community
  • 1
  • 1
rgettman
  • 167,281
  • 27
  • 248
  • 326
4

The remove method expects an index and it removes an item at the specified index. You are passing a char, thus it is converted to an integer 65.

mic4ael
  • 6,326
  • 3
  • 28
  • 40
4

The ArrayList class has two overloads of the method remove. One with an integer parameter, that removes the item at that index, and one with an Object parameter.

You are passing a char. This is neither an int not an Object. So the compiler has to decide which remove it uses: Does it promote the char to an int, or does it box it into a Character which will then be promoted to Object.

Overload resolution in Java always starts without consideration of boxing and unboxing. Therefore it gives priority to the remove(int) overload. So it takes the value of the character A, which is 65, and promotes it to int, which means it will try to remove item #65, when the list only has three items.

To solve this, you need to explicitly tell it to use a Character object, as in: remove(Character.valueOf('A')).

RealSkeptic
  • 32,074
  • 7
  • 48
  • 75
2

Should pass index to the remove method of Arraylist. trying replacing this line a.remove('A'); with

a.remove(0);

jayachsi
  • 75
  • 1
  • 10
1
a.remove()

expects to get an integer for the index of the item to be removed. 'A' returns the ASCII value of A which is 64,thus out of the bounds of the array

  • 2
    A is 65, actually, as in the exception message. – Andy Turner Jan 06 '16 at 21:47
  • 2
    It's not really the ASCII value, it's the UTF-16 code point, which just happens to be the same as the Latin1 value, which just happens to be the same as the ASCII value. It's a bit of a pet peeve of mine, people saying ASCII value for code point. – David Conrad Jan 06 '16 at 22:01
  • @DavidConrad I totally agree with you, but I think you meant to say UTF-16 code _unit_. A code _point_ is absolute and not an encoding. – Klitos Kyriacou Jan 06 '16 at 22:05
  • @Klitos Thank you. I should indeed have said code *unit*. – David Conrad Jan 06 '16 at 22:06