0

I have an ExpandableListView, which has objects of class Group as groups. Each Group object has ArrayList, and the objects in this array are children. When a user clicks on child, it's supposed to do the following:

  • Check if the child's boolean selected set to true or false. True means add it to the array, false - remove it from the array.
  • If true: check if the ID of child's parent (Group) exists in selected_groups_ids array
    • If yes - add selected child object to ArrayList in the Group that is in the array selected_groups.
    • if no - add the ID to array selected_groups_ids, create new empty Group object with the ID of child's parent and add it to the selected_groups array, add the selected child to the ArrayList in that group.
  • If false: remove this child from ArrayList in the Group inside selected_groups
    • Check for ArrayList size
    • If it's 0 - remove the Group object from selected_groups array.

So here's what I tried to do:

This is the Group object code:

public class Group {

private int group_id;
private String group_name;
private ArrayList<Base_Item> group_items = new ArrayList<Base_Item>();

public Group(int category_id, String group_name, ArrayList<Base_Item> items) {
    super();
    this.group_id = category_id;
    this.group_name = group_name;
    this.group_items = items;
}


public Group(int group_id, String group_name) {
    super();
    this.group_id = group_id;
    this.group_name = group_name;
}

// Getters and setters...

public int remove_item_from_array(Base_Item item)
{
    for(Base_Item current_item : group_items)
    {
        if(current_item.getItem_id() == item.getItem_id())
        {
            group_items.remove(current_item);
        }
    }
    return group_items.size();
}

Then in my Activity with the Expandable list:

@Override
        public boolean onChildClick(ExpandableListView parent, View v,
                int groupPosition, int childPosition, long id) {
            Group selected_group = arr_all_groups.get(groupPosition);
            Base_Item bi = selected_group.getGroup_items().get(childPosition);
            Group temp_group = new Group(selected_group.getGroup_id(), selected_group.getGroup_name());

            if(!bi.isBase_item_selected())
            {
                bi.setBase_item_selected(true);
                if(!is_group_in_array(selected_group.getGroup_id()))
                {
                    arr_selected_groups_ids.add(selected_group.getGroup_id());
                    arr_selected_groups.add(temp_group);
                }
                insert_item_to_array(temp_group, bi);
                Log.i("Array size +++", ""+arr_selected_groups.size());

            } else {
                bi.setBase_item_selected(false);
                remove_item_from_array(temp_group, bi);
                Log.i("Array size ---", ""+arr_selected_groups.size());
            }
            adapter.notifyDataSetChanged();

            return false;
        }

private boolean is_group_in_array(int group_id)
{
    for(int i : arr_selected_groups_ids)
    {
        if (i == group_id)
            return true;
    }
    return false;
}

private void insert_item_to_array(Group g, Base_Item bi)
{
    for(Group current_group : arr_selected_groups)
    {
        if(current_group.getGroup_id() == g.getGroup_id())
        {
            ArrayList<Base_Item> arr = current_group.getGroup_items();
            arr.add(bi);
            break;
        }
    }
}

private void remove_item_from_array(Group g, Base_Item bi)
{
    for(Group current_group : arr_selected_groups)
    {
        if(current_group.getGroup_id() == g.getGroup_id())
        {
            int arr_size = current_group.remove_item_from_array(bi);
            if(arr_size==0)
            {
                arr_selected_groups_ids.remove(current_group.getGroup_id());
                arr_selected_groups.remove(current_group);
            }
        }
    }
}

It looks like the objects are being added - I tried several Log records and Toast messages, and each time I was adding a child I was getting the correct size of the array in the Group object. The problem begins when I want to remove the child object from the array or remove the Group from from selected_groups array if the ArrayList in this group is empty. The app crashes with this message:

07-22 11:01:03.734: E/AndroidRuntime(31281): FATAL EXCEPTION: main
07-22 11:01:03.734: E/AndroidRuntime(31281): java.util.ConcurrentModificationException
07-22 11:01:03.734: E/AndroidRuntime(31281):    at java.util.ArrayList$ArrayListIterator.next(ArrayList.java:569)
07-22 11:01:03.734: E/AndroidRuntime(31281):    at com.mycompany.myapp.models.Group.remove_item_from_array(Group.java:67)
07-22 11:01:03.734: E/AndroidRuntime(31281):    at com.mycompany.myapp.activities.Activity_Add_Edit_Trip.remove_item_from_array(Activity_Add_Edit_Trip.java:359)
07-22 11:01:03.734: E/AndroidRuntime(31281):    at com.mycompany.myapp.activities.Activity_Add_Edit_Trip.access$2(Activity_Add_Edit_Trip.java:338)
07-22 11:01:03.734: E/AndroidRuntime(31281):    at com.mycompany.myapp.activities.Activity_Add_Edit_Trip$2.onChildClick(Activity_Add_Edit_Trip.java:114)
07-22 11:01:03.734: E/AndroidRuntime(31281):    at android.widget.ExpandableListView.handleItemClick(ExpandableListView.java:582)
07-22 11:01:03.734: E/AndroidRuntime(31281):    at android.widget.ExpandableListView.performItemClick(ExpandableListView.java:521)
07-22 11:01:03.734: E/AndroidRuntime(31281):    at android.widget.AbsListView$PerformClick.run(AbsListView.java:2514)
07-22 11:01:03.734: E/AndroidRuntime(31281):    at android.widget.AbsListView$1.run(AbsListView.java:3168)
07-22 11:01:03.734: E/AndroidRuntime(31281):    at android.os.Handler.handleCallback(Handler.java:605)
07-22 11:01:03.734: E/AndroidRuntime(31281):    at android.os.Handler.dispatchMessage(Handler.java:92)
07-22 11:01:03.734: E/AndroidRuntime(31281):    at android.os.Looper.loop(Looper.java:137)
07-22 11:01:03.734: E/AndroidRuntime(31281):    at android.app.ActivityThread.main(ActivityThread.java:4424)
07-22 11:01:03.734: E/AndroidRuntime(31281):    at java.lang.reflect.Method.invokeNative(Native Method)
07-22 11:01:03.734: E/AndroidRuntime(31281):    at java.lang.reflect.Method.invoke(Method.java:511)
07-22 11:01:03.734: E/AndroidRuntime(31281):    at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:784)
07-22 11:01:03.734: E/AndroidRuntime(31281):    at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:551)
07-22 11:01:03.734: E/AndroidRuntime(31281):    at dalvik.system.NativeStart.main(Native Method)

it points to this line in my Group class (line 67):

for(Base_Item current_item : group_items)

and to this line in my Activity (line 359):

int arr_size = current_group.remove_item_from_array(bi);

line 338:

private void remove_item_from_array(Group g, Base_Item bi)

line 114:

remove_item_from_array(temp_group, bi);

Why is it happening? What am I doing wrong here?

Igal
  • 5,833
  • 20
  • 74
  • 132

2 Answers2

1

You cannot remove an element with the collection.remove() function while using the foreach-loop, only the iterator.remove() function in this fashion, which isn't available to you in this form.

Therefore, the problem is that

for(Group current_group : arr_selected_groups) //you are using this kind of loop
{
    ....
}

For this code to work, use the following, slightly dumber version of iteration:

for(int i = 0; i < arr_selected_groups.size(); i++)
{
    Group current_group = arr_selected_groups.get(i);
    if(current_group.getGroup_id() == g.getGroup_id())
    {
        int arr_size = current_group.remove_item_from_array(bi);
        if(arr_size==0)
        {
            arr_selected_groups_ids.remove(current_group.getGroup_id());
            arr_selected_groups.remove(current_group);
            i--; //once you remove the current element, decrease the index by one

            //you can also use the line arr_selected_groups.remove(i--);
        }
    }
}

You could also use the

Iterator<Group> iterator = arr_selected_groups.iterator();
while(iterator.hasNext())
{
    Group current_group = iterator.next();
    if(current_group.getGroup_id() == g.getGroup_id())
    {
        int arr_size = current_group.remove_item_from_array(bi);
        if(arr_size==0)
        {
            arr_selected_groups_ids.remove(current_group.getGroup_id());
            iterator.remove(); //this method 
        }
    }
}

But I honestly don't prefer it to the first one I mentioned, I think the iterator makes it less clear in case of a list. In the case of a Set, it would be different of course.

EpicPandaForce
  • 79,669
  • 27
  • 256
  • 428
  • 1
    I tried to use the same "dumb" loop in my Group class to remove the Base_Item from the Array, but eventually got this error message: java.lang.IndexOutOfBoundsException: Invalid index 3, size is 2` at this line: `arr_selected_groups_ids.remove(current_group.getGroup_id());` So I changed it to: `arr_selected_groups_ids.remove(arr_selected_groups_ids.indexOf(current_group.getGroup_id()));` and then looks like it worked perfectly! Thank you so very much!!! I was pulling my hair out with this problem... – Igal Jul 22 '14 at 09:59
  • I'm glad I could help :) On a second glance though, the item removal from `group_items` in `remove_item_from_array()` has the same error just waiting around to emerge from the depths. Just a note. – EpicPandaForce Jul 22 '14 at 10:26
  • 1
    I actually changed it immediately too, according to your explanation. So it had for loop instead of foreach too. Ran a few tests and it worked! Hopefully It will not fail! :) Having other issues now, with other code, but that's a different story... :) – Igal Jul 22 '14 at 13:51
1

you need to use iterator if you want to modify your collection in loop.

Iterator it=list.iterator();
while (it.hasNext()){
    System.out.println(it.next());
    it.remove(); // valid here
}
Waqar Ahmed
  • 5,005
  • 2
  • 23
  • 45