简介
RecyclerView中的ChildHelper.Bucket是一个工具类,实现了类似List<Boolean>
的数据结构,从而达到减少内存占用的目的。
Bucket是一个链表结构,有两个字段:mData用于存储当前信息,next指向下一个数据
public static class Bucket {
final static int BITS_PER_WORD = Long.SIZE;
final static long LAST_BIT = 1L << (Long.SIZE - 1);
long mData = 0;//当前数据
Bucket next;//指向下一个数据
}
mData是long类型,存在64个bit位,每一个bit位可以是0或者1,代表true或false。这样一个mData可以存储64个(long类型的位数)true或false信息。相对于List<Boolean>
,而且是最多64位。当然,如果需要集合的大小超过了64,则相关的信息存储到了next中,这就是使用链表的作用了吧。
toString方法
链式调用next,打印mData的二进制字符串,不同的buckt用xx分隔(后面有使用示例)
@Override
public String toString() {
return next == null ? Long.toBinaryString(mData)
: next.toString() + "xx" + Long.toBinaryString(mData);
}
调用端代码:
Log.i(TAG,bucket.toString());//0
bucket.set(65);
Log.i(TAG,bucket.toString());//10xx0
set方法
设置对应index位的bit为1(index从0开始)
public void set(int index) {
if (index >= BITS_PER_WORD) {//如果index大于等于64,则相关信息设置到next中
ensureNext();
next.set(index - BITS_PER_WORD);
} else {
mData |= 1L << index;
}
}
调用端代码:
ChildHelper.Bucket bucket = new ChildHelper.Bucket();
bucket.set(3);
Log.i(TAG,bucket.toString());//1000
从结果可以看到:idex为3的位置的bit已经被设置成1了
get方法
判断index对应的bit是否为1,如果为1,返回true,否则返回false。
public boolean get(int index) {
if (index >= BITS_PER_WORD) {//如果index大于等于64,则从next中获取
ensureNext();
return next.get(index - BITS_PER_WORD);
} else {
return (mData & (1L << index)) != 0;
}
}
调用端代码:
bucket.set(4);
Log.i(TAG,bucket.toString());//10000
boolean has4 = bucket.get(4);
Log.i(TAG,"has4=" + has4);//has4=true
可以看到index为4的bit为1,就返回了true
clear方法
设置index对应的bit为0
public void clear(int index) {
if (index >= BITS_PER_WORD) {//如果index大于等于64,则设置next中相应数据
if (next != null) {
next.clear(index - BITS_PER_WORD);
}
} else {
mData &= ~(1L << index);
}
}
调用端代码:
bucket.set(3);
Log.i(TAG,bucket.toString());//1000
bucket.clear(3);
Log.i(TAG,bucket.toString());//0
可以看到index为3对应的bit已经被设置成0
countOnesBefore方法
计算比index小的所有位数上bit为1的总个数。
例如 0001 1010,如果index为5,则结果为3;如果index为4,则结果是2
public int countOnesBefore(int index) {
if (next == null) {
if (index >= BITS_PER_WORD) {
return Long.bitCount(mData);
}
return Long.bitCount(mData & ((1L << index) - 1));
}
if (index < BITS_PER_WORD) {
return Long.bitCount(mData & ((1L << index) - 1));
} else {
return next.countOnesBefore(index - BITS_PER_WORD) + Long.bitCount(mData);
}
}
调用端代码:
bucket.set(4);
Log.i(TAG,bucket.toString());//10000
boolean has4 = bucket.get(4);
Log.i(TAG,"has4=" + has4);//has4=true
bucket.set(3);
bucket.set(2);
Log.i(TAG,bucket.toString());//11100
int i = bucket.countOnesBefore(5);
Log.i(TAG,"i=" + i);//i=3(前5位中bit为1的总数)
int j = bucket.countOnesBefore(4);
Log.i(TAG,"j=" + j);//j=2(前4位中bit为1的总数)
可以看到,如果当前bucket为11100,则countOnesBefore(5)为3,countOnesBefore(4)为2。
reset方法
重置,所有数据置为0
public void reset() {
mData = 0;
if (next != null) {
next.reset();
}
}
调用端代码:
bucket.set(65);
Log.i(TAG,bucket.toString());//10xx0
bucket.reset();
Log.i(TAG,bucket.toString());//0xx0
insert方法
在index位置插入指定bit:value为true插入1,value为false插入0
public void insert(int index, boolean value) {
if (index >= BITS_PER_WORD) {//如果index大于等于64,则在next中插入
ensureNext();
next.insert(index - BITS_PER_WORD, value);
} else {
final boolean lastBit = (mData & LAST_BIT) != 0;
long mask = (1L << index) - 1;
final long before = mData & mask;//小于index的数据
final long after = ((mData & ~mask)) << 1;//大于index的数据,已经左移一位,腾出当前位置
mData = before | after;
if (value) {
set(index);//value为ture插入1
} else {
clear(index);//value为false插入0
}
if (lastBit || next != null) {
ensureNext();
next.insert(0, lastBit);//将当前bucket最高位的bit插入到next的index为0的位置上(实现左移操作)
}
}
}
调用端代码:
bucket.reset();
Log.i(TAG,bucket.toString());//0xx0
bucket.set(6);
bucket.set(5);
Log.i(TAG,bucket.toString());//0xx1100000
bucket.insert(6,false);
Log.i(TAG,bucket.toString());//0xx10100000(6和5之间插入一位,插入的值为0)
bucket.insert(6,true);
Log.i(TAG,bucket.toString());//0xx101100000(6和5之间插入一位,插入的值为0)
可以看到在index为6的位置上插入了指定bit,低于6的数据保持不变,大于等于6的数据左移了一位
remove方法
移除index对应位置的bit。低于index的所有数据保持不变,高于index的数据右移一位。
public boolean remove(int index) {
if (index >= BITS_PER_WORD) {//如果index大于等于64,则在next中移除
ensureNext();
return next.remove(index - BITS_PER_WORD);
} else {
long mask = (1L << index);
final boolean value = (mData & mask) != 0;
mData &= ~mask;//index未bit置为0
mask = mask - 1;
final long before = mData & mask;//小于index的数据
// cannot use >> because it adds one.
final long after = Long.rotateRight(mData & ~mask, 1);//大于index的数据(已右移一位)
mData = before | after;
if (next != null) {//如果next存在
if (next.get(0)) {//如果next的index为0的bit为1
set(BITS_PER_WORD - 1);//设置当前最高位的bit为1(相当于右移一位)
}
next.remove(0);//next需要进行:删除index为0的操作
}
return value;
}
}
调用端代码:
Log.i(TAG,bucket.toString());//0xx101100000(6和5之间插入一位,插入的值为0)
bucket.remove(6);
Log.i(TAG,bucket.toString());//0xx10100000(移除位置6的bit)
可以看到index为6的bit删除了,低于6的数据保持不变,高于6的数据全部右移了一位
总结
Bucket通过链表的数据结构和long类型可以存储64位bit为1或0的特性,实现了类似List<Boolean>
的数据结构,从而达到减少内存占用的目的。