Getting an IndexOutOfBounds Exception in my Android App

Issue

This code tracks what missiles players buy and sets the most recent 3 at the top of the list of missile options for players to easily buy them again. After players purchase missiles, the game crashes, but only if they purchase certain ones or purchase a certain amount.

Process: com.apps.fast.counterforcetest, PID: 16063
java.lang.IndexOutOfBoundsException: Index: 5, Size: 3
    at java.util.LinkedList.checkElementIndex(LinkedList.java:555)
    at java.util.LinkedList.remove(LinkedList.java:525)
    at com.apps.fast.launch.components.ClientDefs.SetMissilePreferred(ClientDefs.java:199)
    at com.apps.fast.launch.launchviews.PurchaseLaunchableView$1$1.onClick(PurchaseLaunchableView.java:198)

Here is the code the error points to in CLientDefs.java:199

public static void SetMissilePreferred(int lMissile) {
    // Set a purchased missile to go to the top of the preferred list.
    if (MissilePreferences == null)
        MissilePreferences = new LinkedList<>();

    if (MissilePreferences.contains(lMissile))
        MissilePreferences.remove(lMissile); //<---- Line 199

    MissilePreferences.push(lMissile);

    if (MissilePreferences.size() > PREFERENCE_LIST_SIZE)
        MissilePreferences.removeLast();
}

MissilePreferences is a List of Integers.

Solution

There’s a bit of a mixup going on here:

if (MissilePreferences.contains(lMissile))
    MissilePreferences.remove(lMissile);

lMissile is an int, right? Representing an ID? So when you call contains, you’re checking if that ID exists in MissilePreferences. You want to know if the List contains that int.

Here’s the method signature for contains:

public boolean contains(Object o)

It doesn’t take a primitive like an int, it takes an Object – so your int is being autoboxed into an Integer – which makes sense because a List can’t contain primitives either, only objects. MissilePreferences is a list of Integers.


So that’s all fine, it converts your int to an Integer and tells you if it exists in the list of Integers, perfect! But then you call remove with that original int. And there are two versions of that method that take a parameter:

public boolean remove(Object o)

Removes the first occurrence of the specified element from this list, if it is present.

public E remove(int index)

Removes the element at the specified position in this list.

Those do two very different things. The Object version removes the first instance of that object from the list. The int version treats the int as an index and removes whatever is in that position.

When you use contains, you’re treating your int as an element in the list you want to find, and it’s automatically converted to an Integer. And you want to do the same with remove – but there’s a different version of that method that takes the int you’re providing directly. It won’t autobox that and call the other method, obviously! You’d have to do it yourself:

Integer missileId = new Integer(lMissile);
// explicitly passing an Integer object to both - only crucial for #remove
if (MissilePreferences.contains(missileId))
    MissilePreferences.remove(missileId);

That’s what you have to watch out for – outside of arrays and some special classes you might run into elsewhere, collection objects don’t contain primitives, so you often want to explicitly work with the Object wrapper versions they contain. Especially important for ints because of situations like this!

That said, you could just do this:

MissilePreferences.remove(new Integer(lMissile));

No need to use contains – you can just call remove and it’ll remove the first matching object if it has one. It returns true if it removed anything, in case you need to check that

Answered By – cactustictacs

This Answer collected from stackoverflow, is licensed under cc by-sa 2.5 , cc by-sa 3.0 and cc by-sa 4.0

Leave a Reply

(*) Required, Your email will not be published