Android:带有重复项的SortedList

12

我对RecyclerViewSortedList有些问题不太理解。

假设我有一个非常简单的类,只有一个非常简单的类来保存数据:

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
                + "]";
    }
}

我理解的是排序后的列表不会包含任何重复项。

但是当我有一个带回调函数的 SortedList,像这样:

....

@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;
}

当我添加具有相同ID但不同字符的多个项目时,最终会出现重复项。

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

我期望列表更新项,但现在即使areItemsTheSame返回true,我也有多个项。


列表可以有重复项。尝试使用哈希表。 - Anitha Manikandan
据我理解,不是“如果项目已经存在于列表中且其排序标准未更改,则将其替换为现有项目。”(https://developer.android.com/reference/android/support/v7/util/SortedList.html#add(T))。我不需要HashMap,因为我需要与RecyclerView有效地耦合。 - Paul Woitaschek
为什么这个问题被打上了Java标签? - Boris
@Paul Woitaschek 你解决了这个问题吗? - Ari
3
是的,@Ari 我正在手动检查它。请看这里 - Paul Woitaschek
6个回答

9

SortedList不会根据id保留任何映射(因为API中没有id)。所以当排序标准改变时(在您的情况下从a到b),SortedList无法找到现有元素。

您可以自己保留id映射,然后将添加方法更改如下:

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);
}

你还需要实现一个remove方法,从idMap中移除该项。

正如您所指出的,当排序条件改变时,SortedList无法找到现有元素,那么indexOf如何找到正确的索引呢?在我的情况下,它总是返回-1 - Shaw
在更改数据之前,您需要将其称为“before”。 - yigit

5
正如Minhtdh在他的答案中提到的那样,问题出现在您的compare()函数中。 add()查找现有对象的索引时使用了您实现的compare()函数。因此,当您的compare()函数返回非0值时,它就会将该对象添加到列表中。
在比较之前,您需要检查这些项是否相同。但是,如果您的内容可以相同,则需要进行第二次比较。
以下是我在您的情况下如何实现compare()函数的方式:
@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;
}

0

我在创建一个聊天应用程序时遇到了类似的问题,需要通过它们的ID更新消息并按日期对它们进行排序。支持库的SortedList并没有做到这一点,或者至少我没有时间深入研究其源代码和测试。因此,我创建了一个小组件MultiSortedList:

import android.support.v7.widget.RecyclerView

/**
 * Created by abduaziz on 6/14/18.
 *
 *   MultiSortedList is a wrapper component to ArrayList that keeps its elements in a sorted order
 *   using UpdateCallbackInterface. It is intended to be used inside recycler view adapters.
 *
 * */

class MultiSortedList<T>(var updateCallback: UpdateCallback<T>, var adapter: RecyclerView.Adapter<*>? = null) {

    companion object {
        val TAG = "SORTEDLIST"
    }

    // internal list to hold elements by sortBy() -> visible to user
    private val list: ArrayList<T> = arrayListOf()

    // internal list to hold elements by updateBy() -> not visible
    private val uList: ArrayList<T> = arrayListOf()

    // add adapter from ui
    fun addAdapter(adapter: RecyclerView.Adapter<*>?) {
        this.adapter = adapter
    }

    /*
    * 1. Search for existing element that satisfies updateBy()
    * 2. Remove the existing element if found
    * 3. Add the new item with sortBy()
    * 4. Notify if adapter is not null
    * */
    fun add(newItem: T) {
        remove(newItem)

        // save to internal list by updateBy()
        var toBeStoredPosition = uList.binarySearch { updateCallback.updateBy(it, newItem) }
        if (toBeStoredPosition < 0) toBeStoredPosition = -(toBeStoredPosition + 1)
        uList.add(toBeStoredPosition, newItem)

        // save to UI list and notify changes
        var sortPosition = list.binarySearch { updateCallback.sortBy(it, newItem) }
        if (sortPosition < 0) sortPosition = -(sortPosition + 1)
        list.add(sortPosition, newItem)
        adapter?.notifyItemInserted(sortPosition)
    }

    /*
    * Remove and notify the adapter
    * */
    fun remove(removeItem: T) {
        val storedElementPosition = uList.binarySearch { updateCallback.updateBy(it, removeItem) }
        if (storedElementPosition >= 0 && storedElementPosition < uList.size) {

            // remove from internal list
            val itemTobeRemoved = uList[storedElementPosition]
            uList.removeAt(storedElementPosition)

            // remove from ui
            val removePosition = list.binarySearch { updateCallback.sortBy(it, itemTobeRemoved) }
            if (removePosition >= 0 && removePosition < list.size) {
                list.removeAt(removePosition)
                adapter?.notifyItemRemoved(removePosition)
            }
        }
    }

    // can be accessed -> list.get(position) or list[position]
    operator fun get(pos: Int): T {
        return list[pos]
    }

    // for adapter use
    fun size(): Int {
        return list.size
    }

    inline fun forEachIndexed(action: (Int, T) -> Unit) {
        for (index in 0 until size()) {
            action(index, get(index))
        }
    }

    /*
    * UpdateCallback is the main interface that is used to compare the elements.
    *   - sortBy() is used to locate new elements passed to SortedList
    *   - updateBy() is used to update/remove elements
    *
    * Typical example would be Message model class which we want to:
    *   - Sort messages according to their dates
    *   - Update/Remove messages according to their randomIDs or IDs.
    * */
    interface UpdateCallback<T> {
        fun sortBy(i1: T, i2: T): Int
        fun updateBy(oldItem: T, newItem: T): Int
    }
}

使用方法在这里解释: https://medium.com/@abduazizkayumov/sortedlist-with-recyclerview-part-2-64c3e9b1b124


0
我认为你应该在compare方法中使用Integer.compare(o1.id, o2.id);,这是SortList决定这两个项目是否相同的地方。

1
从文档中可以清楚地看到,这是用于排序的。我认为如果两个项目是否相同是在areItemsTheSame()函数中决定的。 - Paul Woitaschek
我认为compareareItemsTheSame这两种方法类似于hashCodeequal。如果你想让两个对象具有相同的身份,首先它们必须具有相同的hashCode,然后equal返回true。所以在这种情况下,compare == 0必须首先出现,然后才可以确定areItemsTheSametrue - justHooman

0

你可以通过以下方式检查排序列表中是否已存在该对象。

if (sortedList.indexOf(item) == -1) 
{
    sortedList.add(item);  //Item still does not exist because index is -1
} 
else 
{
    sortedList.updateItemAt(sortedList.indexOf(item), item);
}

-1
在Java中,一个不包含重复元素的集合被称为Set。常见的实现类有HashSetTreeSet。你错了,认为SortedList可以做到这一点。

但是这不是暗示了吗?(https://developer.android.com/reference/android/support/v7/util/SortedList.html#add(T)) - Paul Woitaschek
是的,对我来说看起来很混乱。通常 List 可以包含重复项,但 Set 不行。你能试着使用 SortedSet 而不是 SortedList 吗? - Boris
1
这是一个 Android SortedList,而不是 utils.java。没有适用于 RecyclerView 绑定的 SortedSet。 - Paul Woitaschek

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接