优雅永不过时-每日签到功能的优雅实现

11 篇文章 0 订阅
7 篇文章 0 订阅

需求简介

需要在页面中上展示该用户本月的签到情况,已经签到过的日期就要做一个标记。

如下图
在这里插入图片描述

这里主要解决的是数据存储和读取的问题。

数据结构

简单解决方案

简单思考的话很好解决,可以建议使用下面的表结构来实现(去除非必要字段)

字段名字段类型说明
user_idint用户
monthint所属月份
dayint签到日

这样的话简单明了,但是有一个问题,一个用户一个月就是30条数据,50万月度活跃用户(设计目标)一年就能产生 500000*30*12=180,000,000 ,也就是1.8亿条数据……

哈哈,看上去能行,实际上,呃……

进阶解决方案

上面每一次签到都记录一次数据,看上去精细(可以保存每一次操作的创建时间之类的),但是实际保存了签到的日就行了,这个设计肯定会有非常多的冗余数据的。

首先一个月最多31天,可以理解为这里只要一个长度为31的boolean的数组,序号代表日期,boolean值代表是否已经签到。

其次,boolean类型的本质不就是 01嘛!

ok了,问题解决了!

那就是一个长度为31的byte数组了!

31位的byte数组,那还不如32呢,为啥?32位直接可以转换成一个int值啊!

妙!

说干就干,数据结构可以简单变一下就行了

字段名字段类型说明
user_idint用户
monthint所属月份
dayint签到日集合

只要每次展示的时候解析day这个代表当月签到日集合的Int数值,就可以知道当月的已签到日,当月总计签到日。

按照50万月度活跃用户预估,一年能产生 500000*1*12=6000,000 ,比之前肯定是减小一个数量级了。

稳,优雅永不过时!

关键代码实现


public class DateUtils {
    
    /**
     * 指定日期签到
     *
     * @param sign 已签到的集合
     * @param day  需要添加的签到日期 1,2,3...
     * @return
     */
    public static Integer sign(Integer sign, Integer day) {
        return sign | 1 << day;
    }

    /**
     * 检查指定日期是否已签到
     *
     * @param sign
     * @param day
     * @return
     */
    public static boolean checkSign(Integer sign, Integer day) {
        return 0 != (sign & 1 << day);
    }


    /**
     * 统计本月已签到次数
     *
     * @param sign
     * @return
     */
    public static Integer countSign(Integer sign) {
        int count = 0;
        while (sign != 0) {
            count += (sign & 1);
            sign = sign >>> 1;
        }
        return count;
    }

    /**
     * 获取以前到的日期列表
     *
     * @param sign
     * @return
     */
    public static List<String> getSignDays(int sign) {
        List<String> result = new ArrayList<String>();
        int day = 0;
        while (sign != 0) {
            day++;
            sign = sign >>> 1;
            if (1 == (sign & 1)) {
                result.add(String.valueOf(day));
            }

        }
        return result;
    }

    public static void main(String[] args) {
        Integer defaultSign = 0;
        
        Integer sign = sign(defaultSign, 2);
        sign = sign(defaultSign, 2);
        Assert.isTrue(countSign(sign) == 1);
        Assert.isTrue(checkSign(sign, 2));

        sign = sign(sign, 13);

        Assert.isTrue(!checkSign(sign, 12));
        Assert.isTrue(checkSign(sign, 2));
        Assert.isTrue(checkSign(sign, 13));
        Assert.isTrue(!checkSign(sign, 12));
        Assert.isTrue(countSign(sign) == 2);

        for (int i = 0; i <= 31; i++) {
            sign = sign(sign, i);
        }
        System.out.println(getSignDays(sign).toString());
    }

}

  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值