views:

173

answers:

6

I'm trying to remove all but the first child component from a Java Container. The following code logs "There are 3" and throws an ArrayIndexOutOfBoundsException: Array index out of range: 2

int componentCount = cardPanel.getComponentCount();
logger.info("There are " + componentCount);
for (int i = 1; i < componentCount; i++) {
    cardPanel.remove(i);
}

However, this modified version works perfectly:

Component[] components = cardPanel.getComponents();
logger.info("There are " + components.length);
for (int i = 1; i < components.length; i++) {
    cardPanel.remove(components[i]);
}

It appears that the Container.getComponentCount() and Container.remove(int i) can't agree on the number of components in the container. Has anyone else run into this problem?

+4  A: 

When you're doing cardPanel.remove(i), the number of components is reducing.

So you have [0, 1, 2], and remove item at index 1.
Now you have [0, 2] and remove item at index 2, which throws the ArrayIndexOutOfBoundsException.

The modified version works because it is removing the actual object from the container, rather than working from the index.

Try this instead

int componentCount = cardPanel.getComponentCount();
logger.info("There are " + componentCount);
for (int i = 1; i < componentCount; i++) {
    cardPanel.remove(1);
}
Benny Hallett
+1 Spot on ..... !
Stephen C
A: 

Indexes begin from 0, three items means the first item is at index 0, second is at index 1 and third and thus last is at index 2.

Esko
A: 

Container.remove(int i) removes the item at index i

Your code removes items at 1,2,3

Ravish
+3  A: 

Benny's answer is correct, but depending on the container, it may be inefficient. Something like an ArrayList doesn't really like removing things from the front multiple times, because it copies all the rest of the data down each time.

Here's an alternative:

for (int i = cardPanel.getComponentCount() - 1; i >= 1; i--)
{
    cardPanel.remove(i);
}

Basically this starts from the end and works towards the start.

It's only going to be significant for large collections, but it's worth knowing the technique.

Jon Skeet
A: 

What are you trying to do? If you want to remove all items from a container then removeAll is the right choice.

If you need to process the items while removing then the typical idiom is go from end to front, as Jon Skeet explains.

Unless it is necessary that you go from 0 to end, in which case you keep removing the 0th element:

for (int i = 0; i < componentCount; i++) {
    cardPanel.remove(0);
}
Hemal Pandya
A: 

If you want to remove items based on some condition on their index, it is probably best to use an explicit iterator to remove them while keeping your own count of the index.

For example, to remove all objects at an odd index:

    int index = 0;
    final Iterator<String> iterator = list.iterator();
    while (iterator.hasNext())
    {
        iterator.next();
        if (index % 2 != 0)
        {
            iterator.remove();
        }
        index++;
    }

While this is a bit longer, it avoids tricky code and also works when there are equal objects in the list.

starblue
Unfortunately this isn't possible in the context of removing child Components from a Container as there's no way to retrieve the child Components as a mutable List.
Adamski