概述
DiffUtil是support-v7:24.2.0新增的工具类,它主要是用来计算两个数据集之间的差异,计算出旧数据集->新数据集的最小变化量,并将其返回。
算法
DiffUtil内部采用ugene W. Myers’s difference 算法。该算法对空间做了优化,并使用O(N)空间来计算两个列表添加和删除的最小操作数,算法的时间复杂度为O(N + D ^ 2)。由于该算法不支持移动的Item,因而Google大牛在此基础上改进支持计算移动的Item。造成的后果就是,DiffUtil需要对结果进行第二遍运算,以便于计算移动的Item,从而更加耗费性能。此时,时间的复杂度为O(N ^ 2), 其中N是添加和删除操作的总数。对于根据约束条件排序的数据集,可以禁用移动Item的检测以提高性能。
用途
DiffUtil主要是与RecyclerView配合使用。其中,由DiffUtil找出每个Item的变化,由RecyclerView.Adapter更新UI。这样的好处就是,在数据集变化时,RecyclerView.Adapter不用无脑的调用notifyDataSetChanged()方法。
核心类
DiffUtil.Callback
DiffUtil.Callback是一个抽象类,在计算两个列表之间的差异时,由DiffUtil回调此类。在该类中,定义了5个抽象方法:
- int getOldListSize(): 获取旧数据集的长度
- int getNewListSize(): 获取新数据集的长度
- boolean areItemsTheSame(int oldItemPosition, int newItemPosition):用来判断 两个对象是否是相同的Item
- boolean areContentsTheSame(int oldItemPosition, int newItemPosition):用来检查 两个item是否含有相同的数据
- Object getChangePayload(int oldItemPosition, int newItemPosition):后续再说
DiffUtil.DiffResult
DiffUtil.DiffResult用于保存DiffUtil计算出的数据集之间的差异信息,其可以将差异信息分配给RecyclerView.Adapter,以便更新UI。
核心方法
- calculateDiff(DiffUtil.Callback cb)
- calculateDiff(DiffUtil.Callback cb, boolean detectMoves)
这两个方法都是用来计算旧数据集->新数据集的最小变化量,并起将其返回。其中,第一个方法是第二个方法的特例,默认开启移动Item的检测:
public static DiffResult calculateDiff(Callback cb) {
return calculateDiff(cb, true);
}
如果禁用移动Item的检测,可以调用第二个方法,并将detectMoves参数设置为false。
简单使用
前文,已经提到DiffUtil主要是与RecyclerView配合使用,以便高效的更新数据集。
创建Bean
data class DiffBean(var name: String, var desc: String) { override fun equals(o: Any?): Boolean { if (this === o) return true if (o == null || javaClass != o.javaClass) return false val diff = o as DiffBean? return diff!!.name == name } override fun hashCode(): Int { var result = name?.hashCode() ?: 0 return result } }
创建DiffUtil.Callback
class Diff