Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Android: SortedList with duplicates

I have some problems understanding RecyclerViews SortedList.

Lets say I have a very simple class only having a very simple class holding data:

public class Pojo {
    public final int id;
    public final char aChar;

    public Pojo(int id, char aChar) {
        this.id = id;
        this.aChar = aChar;
    }

    @Override
    public String toString() {
        return "Pojo[" + "id=" + id
                + ",aChar=" + aChar
                + "]";
    }
}

My understanding is that the sorted list won't contain any duplicates.

But when I have a SortedList with callbacks like this:

....

@Override
public boolean areContentsTheSame(Pojo oldItem, Pojo newItem) {
    return oldItem.aChar == newItem.aChar;
}

@Override
public int compare(Pojo o1, Pojo o2) {
    return Character.compare(o1.aChar, o2.aChar);
}

@Override
public boolean areItemsTheSame(Pojo item1, Pojo item2) {
    return item1.id == item2.id;
}

I end up with duplicates when I add multiple items with the same id but different chars.

sortedList.add(new Pojo(1, 'a'));
sortedList.add(new Pojo(1, 'b'));

I would expect the list to update the item. Instead now I have multiple items even though areItemsTheSame returned true.

like image 426
Paul Woitaschek Avatar asked Aug 06 '15 10:08

Paul Woitaschek


People also ask

Can sorted set have duplicates?

The SortedSet<T> class does not accept duplicate elements. If item is already in the set, this method returns false and does not throw an exception.

Is there a collection which keeps the data sorted and accepts duplicate values?

Java: Sorted collection which allows duplicates, is memory efficient and provides fast insert + update.


2 Answers

As Minhtdh already mentioned in his answer, the problem lies within your compare().

See, add() looks up the existing object's index by using the compare() you implement. Therefore when your compare() returns something other than 0 it adds the object to the list.

You would need to check if the items are the same before comparing it's contents. However, if your content can be the same you would need a secondary comparison.

This is how I would implement the compare() in your case:

@Override
public int compare(Pojo o1, Pojo o2) {
    int result;
    if (areItemsTheSame(o1, o2) {
        result = 0;
    } else {
        result = Character.compare(o1.aChar, o2.aChar);
        if (result == 0) {
            // TODO implement a secondary comparison 
        }
    }

    return result;
}
like image 107
robocab Avatar answered Oct 16 '22 10:10

robocab


SortedList does not keep any mapping by ids (because there are no ids in the API). So when the sorting criteria changes (a to b in your case), SortedList cannot find the existing element.

You can keep the id mapping yourself, then have your add method as follows:

void add(Item t) {
  Item existing = idMap.get(t.id);
  if (existing == null) {        
     sortedList.add(t);
  } else {
     sortedList.updateItemAt(sortedList.indexOf(existing), t);
  }
  idMap.put(t.id, t);
}

You'll also need to implement a remove method to remove the item from the idMap.

like image 28
yigit Avatar answered Oct 16 '22 10:10

yigit