Georgi Koemdzhiev Georgi Koemdzhiev - 5 months ago 18
Android Question

Return list of elements not contained in another [JAVA/ANDROID]

I want to retain all of the different alements from two ArrayLists and save them in seperate array. I have local and a remote ArrayList (the Local is on my device and I am creating it by reading from my local database). The remote as you can guess I am getting from my server.
I want ot get all of the differences between the local and the remote in order to delete by id all of the elements from my local list (assuming that the remote lsit will contain the most recent data).

My remote list is this one:

[
{
"userID": 8,
"address": "6 Lamond Place, Aberdeen, AB25 3UT",
"price": 1,
"lastUpdated": "1466437965391",
"id": 175
},
{
"userID": 8,
"address": "26 Meadgrey, Edinburgh, EH4",
"price": 4,
"lastUpdated": "1466438561094",
"id": 176
}
]


My local list is contains this data:

[
Property{id=174, userID=8, address='4', price='3', lastUpdated='1466437959249'},
Property{id=175, userID=8, address='6 Lamond Place, Aberdeen, AB25 3UT', price='1', lastUpdated='1466437965391'},
Property{id=176, userID=8, address='26 Meadgrey, Edinburgh, EH4', price='4', lastUpdated='1466438561094'}
]


THis is what I am doing now:

public void retainAllLocalFromRemote(List<Property> remoteProperties) {
ArrayList<Property> localProperties = getAllPropertyItems();

Log.d(TAG, "Local db size: " + localProperties.size());
Log.d(TAG, "Remote db size: " + remoteProperties.size());

ArrayList<Property> differences = new ArrayList<>();

for(int i = 0; i < localProperties.size(); i ++){

if(remoteProperties.contains(localProperties.get(i))){
Log.d(TAG, "remote contains local property with id: " + localProperties.get(i).getId());
}else {
differences.add(localProperties.get(i));
}

}

Log.d(TAG, "differences list size: " + differences.size());
Log.d(TAG, "differences list : " + differences.toString());
}


This is my current log output:

Getting all properties from the database...
06-21 09:54:50.965 10585-10585/xdesign.georgi.espc_retrofit D/EspcItemDataSource: Local db size: 3
06-21 09:54:50.965 10585-10585/xdesign.georgi.espc_retrofit D/EspcItemDataSource: Remote db size: 2
06-21 09:54:50.966 10585-10585/xdesign.georgi.espc_retrofit D/EspcItemDataSource: differences list size: 3
06-21 10:00:48.192 10585-10585/xdesign.georgi.espc_retrofit D/EspcItemDataSource: differences list :[
Property{id=174, userID=8, address='4', price='3', lastUpdated='1466437959249'},
Property{id=175, userID=8, address='6 Lamond Place, Aberdeen, AB25 3UT', price='1', lastUpdated='1466437965391'},
Property{id=176, userID=8, address='26 Meadgrey, Edinburgh, EH4', price='4', lastUpdated='1466438561094'}]


Clearly, there is something wrong because the differences list should be of size. What I am doing wrong?

Answer

What I am doing wrong?

List.contains() uses Object.equals() to compare items and see if they are in a list or not. If you don't implement it I believe it uses toString value or something as fallback to compare items. This will mess up your comparison.

Currently all the items in the loop get false for contains() because of this and they are all added to differences. You need to provide a custom implementation of equals() and hashCode() in class Property so you can decide when an object is equal to another and when not.

It seems straightforward here to do

@Override
public boolean equals(Object o) {
    if (this == o) return true;
    if (!(o instanceof Property)) return false;
    Property prop = (Property) o;
    return Objects.equals(id, prop.id);
}

@Override
public int hashCode() {
    return Objects.hash(id);
}

To compare by id. If you want to compare by an other value, you can change it as you see fit.