SortedList

2024-02-28 12:08
文章标签 sortedlist

本文主要是介绍SortedList,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

SortedList是什么?

源码头注释如下:

A Sorted list implementation that can keep items in order and also notify for changes in the list。

翻译: 
一个有序列表(数据集)的实现,可以保持ItemData都是有序的,并(自动)通知列表(RecyclerView)(数据集)中的更改。

人话: 
首先它是一种数据结构,是一个有序的List,list改变后(增删改查),也可以一直保持数据集List有序,并且会自动调用**adapter中定向更新**notifyXXXX方法,更新RecyclerView。 
对了,它还会自动去重

关键点: 
搭配RecyclerView使用,去重,有序,自动定向刷新

刚看到这里,我觉得这特么自动定向刷新这一点特性,怎么有点像DiffUtil,后来我查阅资料才发现,这家伙出来的比DiffUtil要早,是在Support Library 22 引入的。所以说应该是DiffUtil像它。

 

用法:

我们来看看如果使用SortedList该怎么写:

Adapter:

要写RecyclerView,就少不了Adapter。 
一个常规的Adapter内部一般持有一个List<T>的数据集, 
使用SortedList的话,需要将存储数据源的变量类型改变成SortedList, 
* 唯一差异:将以前的ArrayList->替换为SortedList.

其他的话,倒没有变化,因为SortedList虽然没有继承自List,但是暴漏出API还和List一样的。

public class SortedAdapter extends RecyclerView.Adapter<SortedAdapter.VH> {/*** 数据源替换为SortedList,* 以前可能会用ArrayList。*/private SortedList<TestSortBean> mDatas;...public SortedAdapter(Context mContext, SortedList<TestSortBean> mDatas) {this.mContext = mContext;this.mDatas = mDatas;mInflater = LayoutInflater.from(mContext);}public void setDatas(SortedList<TestSortBean> mDatas) {this.mDatas = mDatas;}@Overridepublic SortedAdapter.VH onCreateViewHolder(ViewGroup parent, int viewType) {return new SortedAdapter.VH(mInflater.inflate(R.layout.item_diff, parent, false));}@Overridepublic void onBindViewHolder(final SortedAdapter.VH holder, final int position) {TestSortBean bean = mDatas.get(position);holder.tv1.setText(bean.getName());holder.tv2.setText(bean.getId() + "");holder.iv.setImageResource(bean.getIcon());}...
}

实体类

无任何修改,就是一个普通的实体类。与上文DiffUtil里的一样。

Callback:

看过DiffUtil详解的同学对这个Callback的编写和理解就易如反掌了,编写规则和套路和DiffUtil.Callback一样。 
而且还少写一个方法public Object getChangePayload(int oldItemPosition, int newItemPosition),这里顺带复习一下上文内容,这个方法返回 一个 代表着新老item的改变内容的 payload对象 
这里说远一点,关于这个少写的方法,正是定向刷新中部分绑定(Partial bind)的核心方法。 
DiffUtil是利用这个getChangePayload()方法的返回值,作为第三个参数,回调ListUpdateCallback接口里的void onChanged(int position, int count, Object payload);方法,最终回调adapter.notifyItemRangeChanged(position, count, payload);方法,再往下就走到Adapter的三参数onBindViewHolder(VH holder, int position, List<Object> payloads)方法,也就是我们部分绑定所操作的地方了,不太明白的可以去看DiffUtil详解. 
除此之外,耶不用传新旧数据集进来了,里面的每个方法都是直接传入ItemData进行比较。 
那么我们的SortedList的Callback如下编写:

public class SortedListCallback extends SortedListAdapterCallback<TestSortBean> {public SortedListCallback(RecyclerView.Adapter adapter) {super(adapter);}/*** 把它当成equals 方法就好*/@Overridepublic int compare(TestSortBean o1, TestSortBean o2) {return o1.getId() - o2.getId();}/*** 和DiffUtil方法一致,用来判断 两个对象是否是相同的Item。*/@Overridepublic boolean areItemsTheSame(TestSortBean item1, TestSortBean item2) {return item1.getId() == item2.getId();}/*** 和DiffUtil方法一致,返回false,代表Item内容改变。会回调mCallback.onChanged()方法;*/@Overridepublic boolean areContentsTheSame(TestSortBean oldItem, TestSortBean newItem) {//默认相同 有一个不同就是不同if (oldItem.getId() != newItem.getId()) {return false;}if (oldItem.getName().equals(newItem.getName())) {return false;}if (oldItem.getIcon() != newItem.getIcon()) {return false;}return true;}
}

Activity:

Activity的编写也没啥大变化,区别如下: 
* 以前构建Adapter时,一般会将data也一起传入,现在可传可不传。 
* SortedList初始化的时候,要将Adapter传进来。所以先构建Adapter,再构建SortedList

public class SortedListActivity extends AppCompatActivity {/*** 数据源替换为SortedList,* 以前可能会用ArrayList。*/private SortedList<TestSortBean> mDatas;private RecyclerView mRv;private SortedAdapter mAdapter;@Overrideprotected void onCreate(Bundle savedInstanceState) {super.onCreate(savedInstanceState);setContentView(R.layout.activity_sorted_list);mRv = (RecyclerView) findViewById(R.id.rv);mRv.setLayoutManager(new LinearLayoutManager(this));//★以前构建Adapter时,一般会将data也一起传入,现在有变化mAdapter = new SortedAdapter(this, null);mRv.setAdapter(mAdapter);initData();mAdapter.setDatas(mDatas);}private void initData() {//★SortedList初始化的时候,要将Adapter传进来。所以先构建Adapter,再构建SortedListmDatas = new SortedList<>(TestSortBean.class, new SortedListCallback(mAdapter));mDatas.add(new TestSortBean(10, "Android", R.drawable.pic1));//★注意这里有一个重复的字段 会自动去重的。mDatas.add(new TestSortBean(10, "Android重复", R.drawable.pic1));mDatas.add(new TestSortBean(2, "Java", R.drawable.pic2));mDatas.add(new TestSortBean(30, "背锅", R.drawable.pic3));mDatas.add(new TestSortBean(4, "手撕产品", R.drawable.pic4));mDatas.add(new TestSortBean(50, "手撕测试", R.drawable.pic5));}

代码写到这里,界面就可以正常显示了。 
可以看到虽然我们add进去的数据 是有重复的,顺序也是乱序的。 
但是列表界面依然按照id的升序显示。 
到这就完了吗,还没有说到自动定向刷新呢。

0步自动定向刷新

和DiffUtil两步完成定向刷新比,SortedList这一点真的是很强。0步完成自动定向刷新

新增一条:

在上述代码的基础上,如果此时查询数据库,发现有一条新的IM聊天信息,那么直接add()进来即可: 
add 内部会自动调用 mCallback.onInserted(index, 1) ->notifyItemRangeInserted(index,1)
也就是说我们add一次 它就notify一次,没有batch操作,有点low

mDatas.add(new TestSortBean(26, "温油对待产品", R.drawable.pic6));//模拟新增
mDatas.add(new TestSortBean(12, "小马可以来点赞了", R.drawable.pic6));//模拟新增
mDatas.add(new TestSortBean(2, "Python", R.drawable.pic6));//add进去 重复的会自动修改

新增一坨:

如果是一坨消息,可以用addAll(),查看源码,它内部会自动做Batch操作,beginBatchedUpdates();endBatchedUpdates();。所以如果想batch,就必须用addAll()操作,感觉这算一个限制。

//addAll 也分两种
//第一种 以可变参数addAll
//mDatas.addAll(new TestSortBean(26, "帅", R.drawable.pic6),new TestSortBean(27, "帅", R.drawable.pic6));
//第二种 集合形式List<TestSortBean> temp = new ArrayList<>();
temp.add(new TestSortBean(26, "帅", R.drawable.pic6));
temp.add(new TestSortBean(28, "帅", R.drawable.pic6));
mDatas.addAll(temp);

刷新

而如果是刷新的场景,可能就不太适用了,刷新时,服务器给我们的一般都是一个List,直接addAll 要先clear, 会闪屏:

       List<TestSortBean> newDatas = new ArrayList<>();for (int i = 0; i < mDatas.size(); i++) {try {newDatas.add(mDatas.get(i).clone());//clone一遍旧数据 ,模拟刷新操作} catch (CloneNotSupportedException e) {e.printStackTrace();}}newDatas.add(new TestSortBean(29, "帅", R.drawable.pic6));//模拟新增数据newDatas.get(0).setName("Android+");newDatas.get(0).setIcon(R.drawable.pic7);//模拟修改数据TestSortBean testBean = newDatas.get(1);//模拟数据位移newDatas.remove(testBean);newDatas.add(testBean);mDatas.clear();mDatas.addAll(newDatas);

异步操作

查看源码,SortedList是在每次add()addAll()clear()…..等对数据集进行增删改查的函数里,都会进行一遍排序和去重。这排序和去重显然是个耗时操作。那么我想说能不能用异步处理呢?丢在子线程中。 
于是我如下写:

        //每次add都会计算一次 想放在子线程中new Thread(new Runnable() {@Overridepublic void run() {mDatas.add(new TestSortBean(26, "帅", R.drawable.pic6));//模拟新增数据mDatas.add(new TestSortBean(27, "帅", R.drawable.pic6));//模拟新增数据}}).start();}

然而这是肯定不行的,上文提过,每次add 会自动 mAdapter.notifyItemRangeInserted(position, count); 
在线程中操作UI,会android.view.ViewRootImpl$CalledFromWrongThreadException: Only the original thread that created a view hierarchy can touch its views. 
这一点就不如DiffUtil啦。

和DiffUtil的异同

它们两真的很像,而且不管是DiffUtil计算出Diff后,还是SortedList修改过数据后,内部持有的回调接口都是同一个:android.support.v7.util.ListUpdateCallback:

/*** An interface that can receive Update operations that are applied to a list.* <p>* This class can be used together with DiffUtil to detect changes between two lists.*/
public interface ListUpdateCallback {void onInserted(int position, int count);void onRemoved(int position, int count);void onMoved(int fromPosition, int toPosition);void onChanged(int position, int count, Object payload);
}

我就不解析源码了,非常简单,大致流程就是: 
DiffUtil计算出Diff或者SortedList察觉出数据集有改变后,在合适的时机,回调ListUpdateCallback接口的这四个方法,DiffUtilSortedList提供的默认Callback实现中,都会通知Adapter完成定向刷新。 
这就是自动定向刷新的原理。

总结一下它们的异同吧: 
* DiffUtil比较两个数据源(一般是List)的差异(Diff),Callback中比对时 传递的参数是 position 
* SortedList 能完成数据集的排序 和去重, Callback中比对时,传递的是ItemData (JavaBean)。 
* DiffUtil 能完成自动定向刷新 + 部分绑定 
* SortedList 只能完成自动定向刷新 
* DiffUtil 更通用,SortedList还与数据结构耦合 
* DiffUtils: 检测不出重复的,会被认为是新增的。(因为比对的核心是postion。 所以无法去重) 但是IM这种消息顺序移动会被检测到。 
* 它们都是一种自动定向刷新的手段

感受总结:

使用SortedList的话,Adapter的保存数据集的变量类型要改变。 
对代码有侵入性,没有热插拔的快感。 
在项目中有各种BaseAdapter的前提下,可能要扩展一种BaseSortedListAdater更方便使用。

只不过它的目的不是在定向刷新,而是维护数据集的 有序 & 去重 。 
顺带有一个定向刷新的功能。

而DiffUtil主打的就是 比较集合的差异,更是帮我们自动完成定向刷新

所以SortedList 不适用于 服务器给所有数据过来的,下拉刷新情况。此时不如使用普通的List。

它的亮点和核心,还是在于 有序 & 去重 。

且它也不支持 部分绑定(Partial bind)

但它在特定场景下,例如 数据集每次更新时是增量更新,且需要维持一个排序规则的时候,就像城市列表界面,还是给我们带来了一定的便利之处的。

 

这篇关于SortedList的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/755461

相关文章

C# SortedList 用法

文章目录 基本用法主要属性和方法注意事项 `SortedList` 的一些高级用法和注意事项。自定义排序规则线程安全性性能考量与其他集合的对比 SortedList 是 C# 中的一个集合类,它是一个键/值对集合,其中的键自动按顺序排序。这个类位于 System.Collections.Generic 命名空间中(对于非泛型版本则是 System.Collections),

SortedList在神马笔记中的应用

SortedList在神马笔记中的应用 一、界面的层次结构二、实现效果三、代码层次结构四、职责定义五、下载地址 一、界面的层次结构 神马笔记有5个界面用于展示笔记/文件夹内容,分别为 文件夹(相同文件夹内的笔记及子文件夹)搜索(同一关键子的笔记及文件夹)最近项目(最近访问的笔记,不包括文件夹)最近删除(最近删除的笔记及文件夹)标签(同一标签的笔记及文件夹) 笔记及文件夹在神

键/值对的集合SortedList

表示键/值对的集合,这些键和值按键排序并可按照键和索引访问。 SortedList最合适对一列健/值对 进行排序,在排序时,是对键进行排序,SortedList 是 Hashtable 和 Array 的混合。当使用 Item 索引器属性按照元素的键访问元素时,其行为类似于 Hashtable。当使用 GetByIndex 或 SetByIndex 按照元素的索引访问元素时,其行为类似于 A