为了账号安全,请及时绑定邮箱和手机立即绑定

一个bitmap的实现代码

标签:
Android

是在阅读RecyclerView的源码时, 在里面发现了该部分代码, 觉得很有趣, 故记录一下. 
该部分代码用于实现了bitmap, 在RecyclerView的包中用于管理子view. 
代码位于RecyclerView包的 ChildHelper.java 文件, 是一个子类, 类名为 Bucket.

介绍

该类使用一个 LONG链表 作为bitmap的存储器, 该结构的主要特点包括:

  1. 理论上可以存放无限多个bit.

  2. 支持动态扩展.

  3. 支持添加/删除/插入/统计等操作.

代码实现

成员变量

该类只有四个变量, BITS_PER_WORD代笔每个Bucket对象可以存放的bit数量, 这里使用了Long类型, 故可以存放64位. mData是具体存放bit的数据, next指向下一个Bucket对象.

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;

设置bit: set(index)

set(int index) 函数用于将index位置的bit设置为1. 
该函数首先检查要插入的位置, 是不是大于当前的Bucket的范围,

  1. 如果不大于, 那么通过 "或" 来设置该bit位.

  2. 如果大于, 先调用ensureNext()函数确定是否需要生成一个新 的Bucket放到链表尾部, 然后调用下个Bucket的set()函数, 但是传入的参数 为 index 减去当前Bucket的长度(64). 如果下个bucket还是不满足, 会一直 顺着链表调用下去, 直到找到一个合适的位置.

void set(int index) {
      if (index >= BITS_PER_WORD) {
           ensureNext();
           next.set(index - BITS_PER_WORD);
      } else {
           mData |= 1L << index;
      }}

ensureNext()的代码如下:

private void ensureNext() {
     if (next == null) {
          next = new Bucket();
     }}

清除操作: clear(index)

其原理跟设置一样, 通过 "与" 操作将bit位清除.

void clear(int index) {
    if (index >= BITS_PER_WORD) {
        if (next != null) {
            next.clear(index - BITS_PER_WORD);
        }
    } else {
        mData &= ~(1L << index);
    }}

判断: get(index)

递归查找链表, 判断指定位置是否设置了bit位.

boolean get(int index) {
    if (index >= BITS_PER_WORD) {
        ensureNext();
        return next.get(index - BITS_PER_WORD);
    } else {
        return (mData & (1L << index)) != 0;
    }}

插入: insert(index, b)

在指定的位置插入一个bit的设置, 该位置后面的内容后移. 这个操作是通过下述步骤实现:

  1. 先获取该位置之前位置的mask: long mask = (1L << index) - 1;

  2. 通过"与"操作获取该位置之前的内容.

  3. 通过"与"操作与"移位"操作将该位置之后的内容后移一位.

  4. 使用第二个参数设置该位置的bit值.

注: 如果插入之前, Bucket对象的最高位的bit被设置(为1), 那么在后移过程中, 该位置会被移除该Bucket, 所以需要记录下来, 将其重新插入到下一个Bucket中. 这个过程会一直持续下去, 直到碰到一个最高位没被设置的Bucket.

void insert(int index, boolean value) {
    if (index >= BITS_PER_WORD) {
        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;
        final long after = ((mData & ~mask)) << 1;
        mData = before | after;
        if (value) {
            set(index);
        } else {
            clear(index);
        }
        if (lastBit || next != null) {
            ensureNext();
            next.insert(0, lastBit);
        }
    }}

移除: remove(index)

该函数用于将该位置的bit位移除, 并将其后面的bit前移一位. 该函数的步骤:

  1. 通过"与"操作将index位置的bit设为0.

  2. 缓存index之前的数据.

  3. 调用Long.rotateRight(), 将index之后的数据前移一位. 因为rotate之前已经将前面(低位)的数据置位0, 所以rotate之后 最高位一直是0.

  4. 将第2步和第3步的数据合并成新数据.

  5. 判断下一个Bucket的第一位是否为1. 如果是则将该bucket的最高位置1.

  6. 调用下一个bucket的remove(0). 遍历链表, 重复这个操作.

boolean remove(int index) {
    if (index >= BITS_PER_WORD) {
        ensureNext();
        return next.remove(index - BITS_PER_WORD);
    } else {
        long mask = (1L << index);
        final boolean value = (mData & mask) != 0;
        mData &= ~mask;
        mask = mask - 1;
        final long before = mData & mask;
        // cannot use >> because it adds one.
        final long after = Long.rotateRight(mData & ~mask, 1);
        mData = before | after;
        if (next != null) {
            if (next.get(0)) {
                set(BITS_PER_WORD - 1);
            }
            next.remove(0);
        }
        return value;
    }}

统计: countOnesBefore(index)

统计index之前的bit数量. 基于 Long 的 bitCount() 函数实现.

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

原文链接:http://www.apkbus.com/blog-705730-60479.html

点击查看更多内容
TA 点赞

若觉得本文不错,就分享一下吧!

评论

作者其他优质文章

正在加载中
  • 推荐
  • 评论
  • 收藏
  • 共同学习,写下你的评论
感谢您的支持,我会继续努力的~
扫码打赏,你说多少就多少
赞赏金额会直接到老师账户
支付方式
打开微信扫一扫,即可进行扫码打赏哦
今天注册有机会得

100积分直接送

付费专栏免费学

大额优惠券免费领

立即参与 放弃机会
意见反馈 帮助中心 APP下载
官方微信

举报

0/150
提交
取消