'Updating a setter of an ArrayList using index results in modifying values of all elements?

Whenever an item in a RecyclerView is clicked I just return the item. sets the value of it as true using setSelected(true). But it changes the isSelected boolean of the entire ArrayList as true.

@Override
public void onParentItemReturn(ServiceItem serviceItem, int parentPosition, int position, boolean isMajor, boolean isSelected) {
    showToast(Services.this, "callback");
    serviceItem.getServices().get(0).setSelected(true);
    finalServiceItems.set(parentPosition, serviceItem);
    servicesAdapter.notifyItemChanged(parentPosition);
}

Service Class

private boolean isSelected;

public void setSelected(boolean selected) {
    isSelected = selected;
}

I tried debugging the code, once the setSelected line is executed the entire ArrayList gets modified to true.

Also tried something like this,

ServiceItem item = new ServiceItem();
    item.setServices(serviceItem.getServices());
    item.setAddon(serviceItem.getAddon());
    item.getServices().get(0).setServiceName("hgutrue");
    finalServiceItems.set(2, item);

this resulted in changing name of all items.

 public class ServiceItem implements Serializable {


@SerializedName("services")
private ArrayList<GService> services;

@SerializedName("addon")
private ArrayList<GService> addon;


public class GService implements Serializable {

@SerializedName("id")
public int id;

@SerializedName("serviceName")
public String serviceName;

@SerializedName("isSelected")
private boolean isSelected;


Solution 1:[1]

This is caused by the same instance of the service class being added to all elements of the list. When this happens, any element modified will result in all references to change (since they are the same). This example illustrates what's happening.

public class BooleanListDemo {
    public static void main(String[] args) {
        List<ServiceClass> bools = new ArrayList<>(5);
        ServiceClass svc = new ServiceClass();
        
        for(int i = 0; i < 5; i++) {
            bools.add(i, svc);
        }
        bools.forEach(System.out::println);

        bools.get(0).isSelected(true);
        bools.forEach(System.out::println);

    }
    
    private static class ServiceClass {
        private boolean isSelected;
        
        public void isSelected(boolean selected) {
            this.isSelected = selected;
        }
        
        @Override
        public String toString() {
            return String.valueOf(isSelected);
        }
    }
}

This outputs:

false
false
false
false
false
true
true
true
true
true

In your case, the list was instantiated and populated upstream. You need to check the code that generated the list and confirm this is happening as I described. Then, to avoid this, new (different) instances must be added to the list.

for(int i = 0; i < 5; i++) {
    bools.add(i, new ServiceClass());
}

Now, you can safely change any index without worrying about all instances changing.


NOTE: In Java, objects are basically pointers. The main error developers make is thinking that because each index location in an array or a list is unique, they assume that adding the same object to each location (magically) makes each object unique. What is indeed happening is that each distinct index location contains the same value, which happens to be a pointer to a location in memory. And, since each index contains the same address in memory, changing the value of one index changes the value of all indexes. By adding a new instance of ServiceClass to each location prevents this problem; even though internally every new instance is the same. Invoking the new operator sets distinct pointers to each location. So, changing one index isolates the change to that one location and no more.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1