Removing an item from a ListView behaving weirdly - Java

Hey guys, so I am writing an Android app for an assignment and my delete functionality is behaving really oddly. I’m not sure why it’s not removing all of the items I have selected in my ListView. It deletes everything, but the middle item. Sometimes, it might even just delete 1 item off the list. I’m not really sure what’s wrong because if the for loop in Java is just like any other for loop in any language, I’m not sure why it’s acting really weird. I have also logged the data to my LogCat as you can see below. The size of the item list returns the correct amount since I can verify that to be true.

    private void deleteChecked() {
        // Log.d(TAG, "Item size: " + items.size());
        for(int counter = 0; counter < items.size(); counter++) {
            // For some reason, what I'm doing isn't working.
            // It deletes an item, but it doesn't seem to delete the correct one at times.

            // We're getting a little bit closer.
            // But it won't delete more than 1 item at a time even though it's in a for loop.
            Log.d(TAG, "Item ID: " + counter);
            if(items.get(counter).isChecked == true) {
                items.remove(counter);
                Log.d(TAG, "Removed ID: " + counter);
            }
        }
    }

Here are some screenshots of what happens when I try to remove checked items from the list.

As you can see; Pizza, Cheese, and Beer are selected, but only Pizza and Beer are removed from the list. I’m not entirely sure why it didn’t also remove Cheese as Cheese’s value is already true since it’s been checked.

The picture below shows that I have selected all 4 items, but only 2 items were removed.

I’m stumped at why Java’s for loop is acting really weird compared to other language. Anyone got any idea why this is happening?

Nvm, I had to just iterate the for loop backwards which is really strange. Then it started removing the correct items in the list even if I selected 5 out of 8 items in the list. Java is really weird, I’m used to PHP.

1 Like

Debugging programming Procedure #1: Trace your variables!

if you remove an item from the list, then items.size() changes when you go back to reevaluate your stop condition… and your array’s indexing changes, so counter is pointing at a different value than the one you think it is.

items[0] = “a”
items[1] = “b”
items[2] = “c”
counter = 0;
counter < items.size() => 0 < 3, True
items.remove(counter); => items.remove(0);
Loop Return
Counter++ => counter = 1
counter < items.size() => 1 < 2 : True.
items.remove(counter); => items.remove(1); //NOTE: This now points at “c”, NOT “b”!
Loop Return
Counter++ => counter = 2
counter < items.size() => 2 < 1 : FALSE.
Loop Abort
Result:
items = [“b”]

You’re modifying a live list of items, not a static one.

Yes, I can see the scenario of playing out, but it will not remove the first item as well if all items are selected. Look at my screenshots. The behavior of Java’s for loop is very different from what I’ve experienced. If your debugging was true, then every item except the last item would be removed. However, if you have more than 3 items in the list, the behavior is different than if you had 3. When I was debugging it, for every 1 item that I selected after 3 items, it’ll add onto the items that weren’t removed. For instance, in a list of 5, if I selected 4, it would just remove 2. In a list of 5, it would just remove 3. And so on.

Here, 3 items seems too complicated to explain. I’ll add a few more items to demonstrate what is really happening. I forgot to also mention, debugging in Java is usually done by using Log.d and catching the results and or data objects and sending them to LogCat to view what is really happening. And as you can see, I’ve got 3 (well in the actual file, I have about 6) Log.d logs so it’s not like I’m not debugging.

So in the screenshot above, we have 6 items. I have the first 5 selected and I want to delete them off the list. That means Soda should be the only one that’s left if we’re going to try and go the route you’re talking about. But look what happens when we click on the delete feature and it goes through the for loop.

Pizza and Beer are now the ones that aren’t deleted even though Pizza is the 2nd item on the list. It should have been deleted if we’re going to base it off your loop theory. But it doesn’t behave the way you’re trying to say. Let’s do the opposite way and pick everything on the list except Butter.

Now Cheese and Tea aren’t deleted from that list even though their index numbers are in the middle of the list.

The code is just the same in these screenshots as the code I posted. But if I iterate the for loop backwards, it automatically removes the correct items that are selected.

private void deleteChecked() {
        // Log.d(TAG, "Item size: " + items.size());
        for(int counter = items.size() - 1;  counter >= 0; counter--) {
            // For some reason, what I'm doing isn't working.
            // It deletes an item, but it doesn't seem to delete the correct one at times.

            // We're getting a little bit closer.
            // But it won't delete more than 1 item at a time even though it's in a for loop.
            Log.d(TAG, "Item ID: " + counter);
            if(items.get(counter).isChecked == true) {
                items.remove(counter);
                Log.d(TAG, "Removed ID: " + counter);
            }
        }
    }

We’ll select everything except soda again. And we get this.

Let’s try that the other way around and select everything except Butter.

And we get this.


So my initial theory has been confirmed. Java’s for loop has really strange behaviors when the list is ordered sequentially, but behaves normally when the for loop is reversed.

You have misunderstood. You are moving the pointer, AND reindexing the array.

Counter = 0. Items.Size() = 6.
Items(counter) is Butter. Butter is checked = True, Delete Butter. Start new Loop
Counter = 1. Items.Size() = 5.
Items(counter) is now Cheese (Pizza is now Items(0). But we’re looking at counter = 1…). Cheese is checked = True. Delete cheese. Start new loop
Counter = 2. Items.Size() = 4.
Items(counter) is now Tea. Tea is checked = True. Delete Tea. Start new loop
Counter = 3. Items.Size() = 3. Stop condition met. Terminate Loop
End result: Pizza, Beer, Soda. Exactly the behavior you see.

Looking back on this, I realize I only diagnosed the problem, didn’t actually give a solution for those that may follow. My bad.

The ‘simplest’ (read: closest to existing code) solution to this problem is to modify how you handle counter. Instead of incrementing it using the for loop, make it a conditional:
If you delete an item, leave counter alone; removing the existing item at index ‘counter’, will put the next item at index ‘counter’, meaning you want to leave counter where it is.
If you didn’t delete an item, increment counter. You’ve finished processing this item, move on to the next.

The ‘better’ solution is to actually use a forEach.

That would make a lot more sense.

I prefer this approach too, however since this is an assignment, the professor didn’t let us use a forEach loop. He still gave me full credit since the application still worked under his guidelines.

1 Like