实战篇(一)BitMap实现签到功能

提示:文章写完后,目录可以自动生成,如何生成可参考右边的帮助文档

文章目录


项目背景

现要求实现一个签到功能,要求如下:

  • 能够判断用户是否已经进行签到
  • 能够判断用户连续签到的天数

1、理论讲解

Bitmap是一个存储二进制数字的数组,数组中每个元素的下标被称为offset。

Bitmap 不是Redis中的实际数据类型,而是在String类型上定义的一组面向位的操作,将其视为位向量。由于字符串是二进制安全的块,且最大长度为512MB,它们适合用于设置最多 232个不同的位。

命令 介绍
SETBIT key offset value 设置指定 offset 位置的值
GETBIT key offset 获取指定 offset 位置的值
BITCOUNT key start end 获取 start 和 end 之间值为 1 的元素个数
BITOP operation destkey key1 key2 ... 对一个或多个 Bitmap 进行运算,可用运算符有 AND, OR, XOR 以及 NOT

2、实现方式

用户签到功能如下所示:

  • key的设计:"sign:" + userId + 日期(格式为yyyyMM)
  • 写入Redis中(比如当月第5天,就将Bitmap对应的第4个位置设置为1)
java 复制代码
    @Override
    public void sign() {
        // 1.获取当前登录用户
        Long userId = AuthContextUtil.getUserInfo().getId();
        System.out.println(userId);
        // 2. 获取日期
        LocalDateTime now = LocalDateTime.now();
        // 3. 拼接key
        String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key = "sign:" + userId + keySuffix;
        // 4. 获取今天是本月的第几天
        int dayOfMonth = now.getDayOfMonth();
        // 5. 写入Redis SETBIT key offset 1
        stringRedisTemplate.opsForValue().setBit(key, dayOfMonth - 1, true);
    }

统计今天截止的连续签到次数:

  • key的设计:"sign:" + userId + 日期(格式为yyyyMM)相同业务下格式保持一致
  • 获取本月为止的所有签到记录,返回的是一个十进制的数字
  • 该数字的二进制形式的最低位代表的是今天签到的情况,最高位代表着是当月第一天的签到情况。
  • 获取当月至今为止的签到情况,只需要从低位到高位以此与1做位运算即可,直到为0,即为断签。
java 复制代码
public int signCount() {
        // 1.获取当前登录用户
        Long userId = AuthContextUtil.getUserInfo().getId();
        // 2. 获取日期
        LocalDateTime now = LocalDateTime.now();
        // 3. 拼接key
        String keySuffix = now.format(DateTimeFormatter.ofPattern(":yyyyMM"));
        String key = "sign:" + userId + keySuffix;
        // 4. 获取今天是本月的第几天
        int dayOfMonth = now.getDayOfMonth();
        // 5. 获取本月截止今天为止的所有的签到记录,返回的是一个十进制的数字
        List<Long> result = stringRedisTemplate.opsForValue().bitField(
                key, BitFieldSubCommands.create().get(BitFieldSubCommands.BitFieldType.unsigned(dayOfMonth)).valueAt(0)
        );
        if(result == null || result.isEmpty()){
            // 没有任何签到结果
            return 0;
        }

        Long num = result.get(0);
        if(num == null || num == 0){
            return 0;
        }
        int count = 0;
        // 6. 循环遍历
        // 如果为0,说明未签到,结束
        // 如果不为0,说明已签到,计数器+1
        while ((num & 1) != 0) {
            // 6.1. 让这个数字与1做与运算,得到数字的最后一个bit位,判断这个bit位是否为0
            count++;
            num >>>= 1;
            // 把数字右移一位,抛弃最后一个bit位,继续下一个bit位
        }
    return count;
    }

总结

BitMap因其结构的特殊性,所以十分适合做用户签到的功能。

相关推荐
小江的记录本41 分钟前
【JVM虚拟机】堆内存分代模型:年轻代(Eden+Survivor)、老年代、元空间Metaspace(附《思维导图》+《面试高频考点清单》)
java·前端·jvm·后端·python·spring·面试
在繁华处44 分钟前
Java从零到熟练(三):流程控制
java·开发语言·python
唐青枫1 小时前
Java Optional 实战指南:优雅处理空值与链式转换
java
一起学开源1 小时前
一文读懂 ReAct 范式:让 AI Agent 真正学会“思考+行动“
java·javascript·react.js·ecmascript·react·alibaba·智能体开发
逍遥德2 小时前
MQTT教程详解-04.SpringBoot集成MQTT(告别手动控制)
java·spring boot·物联网·中间件·iot·iotdb
语戚2 小时前
力扣 3161. 块放置查询:线段树解法(Java 实现)
java·算法·leetcode·面试·线段树·力扣·
我命由我123453 小时前
Android 开发问题:MlKitException: An internal error occurred during initialization.
android·java·java-ee·android jetpack·android-studio·androidx·android runtime
888CC++3 小时前
java 并发编程
java·开发语言·python
无风听海4 小时前
JSON Web Token(JWT)完全指南
java·前端·json
JAVA社区4 小时前
Java高级全套教程(十一)—— Kubernetes 超详细企业级实战详解
java·运维·微服务·容器·面试·kubernetes