0

The new List comes from an SQLite database.

The oldList, is storing the first and second rows of a JOINED table via the @Embedded interface that android provides.

The query that populates it, is supposed to fetch only those 2 rows, because they comply to a specific WHERE clause.

While doing transactions in the right table, like inserting a 3rd row whose foreign key is not yet "pointing"(?) towards the left table, and while no changes have been made to the first 2 rows (joined rows), the Observer gets called, and gives me my same 2 rows, but the java code tells me they are different.

It is understandable that the Observer is giving me a response as if the data changed, because the joined table HAS indeed changed as a whole, and the the SQL query needs to check if the third row inserted complies with the condition imposed (which it does not), but what is not clear is why the java code fetches the same 2 rows, and tells me that they are different from the same 2 rows stored moments ago.

At first I thought it had something to do with the fact that the list was being .get() from a HashMap<K,V> , it could also be that.

        @Override
        protected void onFound(List<X> xes, int liveDataId) {


            List<X> xOldList = listHashMap.get(liveDataId);


            if (xOldList == null) {
                Log.d(TAG, "onFound: list not found on HasMap adding response");

                listHashMap.put(liveDataId, xes);

                if (listHashMap.size() == sources.size()) {
                    Log.d(TAG, "onFound: HashMap complete, converting HasMap");
                    ends(listHashMap);
                }

            } else {
                if (!xOldList.equals(xes)) {
                    Log.d(TAG, "onFound: oldList not the same as new one!");
                    if (xes.size() > 0) {
                        Log.d(TAG, "onFound: new list size greater than 0");
                        synchronizedSubmitList(xes, xOldList, liveDataId);
                    } else {
                        Log.d(TAG, "onFound: newList size is zero");
                        listHashMap.put(liveDataId, xes);
                        ends(listHashMap);
                    }
                }
            }
        }

The logs

enter image description here

The @Embedded Pojo

public class PieceQuantityAndPiece implements EntityInterface { private static final String TAG = "PieceQuantityAndPiece";

@Embedded
public PieceQuantity pieceQuantity;

@Relation(
        parentColumn = "child_piece_id",
        entityColumn = "piece_id"
)
public Piece piece;


public PieceQuantityAndPiece() {
}

}

The Query imposed for left table

@Transaction
@Query("SELECT * FROM piece_quantity_table WHERE folder_id = :folderId")
LiveData<List<PieceQuantityAndPiece>> getPieceQuantityAndPiecesAtFolder(int folderId);

The transactions being performed on the right table that trigger the onChanged():

@Insert(onConflict = OnConflictStrategy.REPLACE)
void insert(Piece piece);

@Query("UPDATE piece_table SET _total = :total, _date_of_last_edit = :lastEdited WHERE piece_id = :pieceId")
void updatePieceTotalAt(int total, long lastEdited, int pieceId);
Delark
  • 1,141
  • 2
  • 9
  • 15
  • 1
    [Don't post images of code](https://idownvotedbecau.se/imageofcode). Post the text – Michael Jun 29 '20 at 15:34
  • Why don't you use a debugger to step into `equals` and see which element causes it to fail? It could be a different number of elements, it could be that the elements are the same but in a different order. – Michael Jun 29 '20 at 15:37
  • Thanks @Michael, I edited to post it in the text, yes, that was lazy on my behalf. I'll use a debugger to see whats the issue. – Delark Jun 29 '20 at 15:45

1 Answers1

0

I managed to compare the equality of both Lists by iterating their separate elements, and then, by using 2 functional interfaces that I had hanging around in the class that returned a boolean value if their Id's were the same on one, and if a field of interest (contents) were also the same on the other one; I compared both items independently.

The silver lining here, in relation to how optimal it is to be iterating this much, is that at most the difference in sizes between old and new List will always be of 1 item at a time, but If given the case that I would made the option of deleting more items, everything still happens on the background, and also there are, I believe, enough clauses to be met that make sure to do whats absolutely necessary.

The order of the items is not really a problem because they are always gonna be fetched in the same order, and if they are in disorder, the list would still go straight to a list differentiator class that manages them cleanly. Also, there is a 100% chance that if the contents are in a different order, it's because something truly changed anyways.

As for the debugging, they logs seemed to be of more help, maybe because I'm not experienced enough with debugging.

The logs showed that the items where truly the same in every field, but their hash codes where different, that's clear, LiveData does not have memory just set() and fin.

@MainThread
protected void setValue(T value) {
    assertMainThread("setValue");
    mVersion++;
    mData = value;
    dispatchingValue(null);
}

Even tho if that T value is not declared new is it still "pointing" to its original source?? I doubt it cause the source is SQLite.

Still even if by type casting every element to their respective subtypes independently, they where still different.

But going deep in the List.equal(List) method I found that my objects will never be equal because this:

public boolean equals(Object obj) {
    return (this == obj);
}

So thats an == ... and they will always have different hash codes...

This is where I began comparing their fields one by one (they were the same), and their primitives where finally comparable.

Having a supertype interface for all SQLite entities, I can compare their ids, and their most important field (in this case last_date_edited), and using the 2 functional interfaces mentioned before I did a clause condition:

private boolean verifyEquality(
        List<X> oldList, List<X> newList,
        @NonNull ListDifferentiator4.AreIdsTheSame<X> areIdsTheSame,
        @NonNull ListDifferentiator4.AreContentsTheSame<X> areContentsTheSame
) {
    for (int i = 0; i < oldList.size(); i++) {

        X oldX = oldList.get(i);
        X newX = newList.get(i);

        if (!areIdsTheSame.areIdsTheSame(newX, oldX) || !areContentsTheSame.areContentsTheSame(newX, oldX)) {
            Log.d(TAG, "verifyEquality: items are different");
            return false;
        }
    }
    Log.d(TAG, "verifyEquality: Lists are the same");
    return true;
}

The onFound Method()...

        @Override
        protected void onFound(List<X> xes, int liveDataId) {


            List<X> xOldList = listHashMap.get(liveDataId);


            if (xOldList == null) {
                Log.d(TAG, "onFound: list not found on HasMap adding response");
                listHashMap.put(liveDataId, xes);

                if (listHashMap.size() == sources.size()) {

                    Log.d(TAG, "onFound: HashMap complete, converting HasMap");
                    convertAndSet(listHashMap);
                }

            } else {
                Log.d(TAG, "onFound: livedataId is: " + liveDataId);

                if (xOldList.size() == xes.size()) {
                    Log.d(TAG, "onFound: Lists are the same size!");

                    if (xOldList.size() != 0) {
                        Log.d(TAG, "onFound: sizes are not 0");
                        boolean areEqual = verifyEquality(
                                xOldList,
                                xes,
                                areIdsTheSame,
                                areContentsTheSame
                        );
                        Log.println(Log.ASSERT, TAG, "onFound: are equal boolean 1st is: " + areEqual);
                        Log.d(TAG, "onFound: list size is: " + xes.size());

                        if (!areEqual) {
                            Log.println(Log.ASSERT, TAG, "onFound: oldList not the same as new one!");
                            listHashMap.put(liveDataId, xes);
                            convertAndSet(listHashMap);

                        } else {
                            Log.println(Log.ASSERT, TAG, "onFound: LISTS WHERE THE SAME!");
                        }
                    }


                } else {

                    Log.println(Log.ASSERT, TAG, "onFound: Lists are not the same size");

                    if (xes.size() > 0) {

                        Log.d(TAG, "onFound: new list size greater than 0");
                        synchronizedSubmitList(xes, xOldList, liveDataId);

                    } else {

                        Log.d(TAG, "onFound: newList size is zero");
                        listHashMap.put(liveDataId, xes);
                        convertAndSet(listHashMap);
                    }

                }
            }
        }

The entire class being initialized with the list differentiator interfaces...

    liveDataListSource = new SourceMergingLiveData<>(
            (newX, oldX) -> newX.getPrimaryKey() == oldX.getPrimaryKey(),
            (newX, oldX) -> newX.getFieldElement().equals(oldX.getFieldElement())
    );

The logs ...

enter image description here

As a final thought, this proved to be an instance where 2 completely equal objects had different Hashcodes each and every time, which is the opposite of what this answer states.

https://stackoverflow.com/a/5443140/11214643

Delark
  • 1,141
  • 2
  • 9
  • 15