实现 连续15签到记录_使用位图算法来优化签到历史数据的空间占用

前言

实际开发中有这样的场景,用户每日签到,可获取相对应的积分赠送,如果连续签到,则可获得额外的积分赠送。

本文主要讲解使用位图算法来优化签到历史记录的空间占用。当然如果业务中仅仅是获取连续签到的最大天数,使用一个计数器即可记录。

需求:

1.记录一年的签到历史

2.获取某月的签到历史

3.获取过去几天连续签到的最大天数

位图算法实现思路

一天的签到状态只有两种,签到和未签到。如果使用一个字节来表示,就需要最多366个字节。如果只用一位来表示仅需要46(366/8 = 45.75)个字节。

80e5f53264eb226337089ddbfa6e2204.png

位图算法最关键的地方在于定位。 也就是说数组中的第n bit表示的是哪一天。给出第n天,如何查找到第n天的bit位置。

这里使用除法和求余来定位。

比如上图

第1天,index = 1/8 = 0, offset = 1 % 8 = 1 ,也就是第0个数组的第1个位置(从0开始算起)。

第11天,index = 11/8 = 1, offset = 11 % 8 = 3 ,也就是第1个数组的第3个位置(从0开始算起)。

 byte[] decodeResult = signHistoryToByte(signHistory); //index 该天所在的数组字节位置 int index = dayOfYear / 8; //该天在字节中的偏移量 int offset = dayOfYear % 8; //设置该天所在的bit为1     byte data = decodeResult[index]; data = (byte)(data|(1 << (7-offset))); decodeResult[index] = data ;    //获取该天所在的bit的值 int flag = data[index] & (1 << (7-offset)); 

编码问题

应用中使用的字节数组,而存到数据库的是字符串。

由于ASCII表中有很多不可打印的ASCII值,并且每一个签到记录的字节都是-128~127,如果使用String 来进行转码,会造成乱码出现,

4ba9129f5767ffee89958e822f9d7bb9.png

乱码

public static void main(String args[]){ byte[] data = new byte[1]; for(int i = 0; i< 127; i++){ data[0] = (byte)i; String str = new String(data); System.out.println(data[0] + "---" + str); } data[0] = -13; String str = new String(data); System.out.println(data[0] + "---" + str + "----"); }/0--- 1---2---3---4---5---6---7---8--9--- 10---11---12---

为了解决编码乱码问题,

本文使用BASE64编码来实现。参看

Base64 的那些事儿

LocalDate

Date类并不能为我们提供获取某一天是该年的第几天功能,JDK8为我们提供了LocalDate类,该类可以替换Date类,相比Date提供了更多丰富的功能。更多使用方法参考源码。

 //获取2018/6/11 位于该年第几天 LocalDate localDate = LocalDate.of(2018,6,11); localDate.getDayOfYear(); //获取今天 位于当年第几天 LocalDate localDate1 = LocalDate.now(); localDate.getDayOfYear();

数据表

原始数组长度仅需要46个字节,经过BASE64编码后的字符串长度为64,所以这里的sign_history长度最大为64.

DROP TABLE IF EXISTS `sign`;CREATE TABLE `sign`( `id` BIGINT AUTO_INCREMENT COMMENT "ID
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
实现连续签到加分的Java代码可以参考以下示例: ``` // 定义签到记录类 public class SignInRecord { private Date signInDate; private int signInScore; public SignInRecord(Date signInDate, int signInScore) { this.signInDate = signInDate; this.signInScore = signInScore; } public Date getSignInDate() { return signInDate; } public int getSignInScore() { return signInScore; } } // 定义签到管理类 public class SignInManager { private List<SignInRecord> signInRecords; private int maxContinuousDays; // 最大连续签到天数 private int continuousDays; // 当前连续签到天数 private int signInScore; // 签到得分 public SignInManager() { signInRecords = new ArrayList<>(); maxContinuousDays = 0; continuousDays = 0; signInScore = 0; } public void signIn() { Date today = new Date(); int score = 1; if (!signInRecords.isEmpty()) { // 判断是否连续签到 Date lastSignInDate = signInRecords.get(signInRecords.size() - 1).getSignInDate(); if (isContinuousSignIn(lastSignInDate, today)) { continuousDays++; score = continuousDays; if (continuousDays > maxContinuousDays) { maxContinuousDays = continuousDays; } } else { continuousDays = 1; } } signInRecords.add(new SignInRecord(today, score)); signInScore += score; } public int getMaxContinuousDays() { return maxContinuousDays; } public int getContinuousDays() { return continuousDays; } public int getSignInScore() { return signInScore; } // 判断是否连续签到 private boolean isContinuousSignIn(Date lastSignInDate, Date today) { Calendar lastCal = Calendar.getInstance(); lastCal.setTime(lastSignInDate); int lastYear = lastCal.get(Calendar.YEAR); int lastDayOfYear = lastCal.get(Calendar.DAY_OF_YEAR); Calendar todayCal = Calendar.getInstance(); todayCal.setTime(today); int todayYear = todayCal.get(Calendar.YEAR); int todayDayOfYear = todayCal.get(Calendar.DAY_OF_YEAR); return lastYear == todayYear && todayDayOfYear - lastDayOfYear == 1; } } ``` 实现App签到功能的Java代码可以参考以下示例: ``` // 定义签到记录类 public class SignInRecord { private Date signInDate; public SignInRecord(Date signInDate) { this.signInDate = signInDate; } public Date getSignInDate() { return signInDate; } } // 定义签到管理类 public class SignInManager { private List<SignInRecord> signInRecords; public SignInManager() { signInRecords = new ArrayList<>(); } public void signIn() { Date today = new Date(); signInRecords.add(new SignInRecord(today)); } public int getSignInCount() { return signInRecords.size(); } public boolean hasSignedInToday() { if (signInRecords.isEmpty()) { return false; } Date lastSignInDate = signInRecords.get(signInRecords.size() - 1).getSignInDate(); Calendar lastCal = Calendar.getInstance(); lastCal.setTime(lastSignInDate); int lastYear = lastCal.get(Calendar.YEAR); int lastDayOfYear = lastCal.get(Calendar.DAY_OF_YEAR); Calendar todayCal = Calendar.getInstance(); int todayYear = todayCal.get(Calendar.YEAR); int todayDayOfYear = todayCal.get(Calendar.DAY_OF_YEAR); return lastYear == todayYear && lastDayOfYear == todayDayOfYear; } } ``` 这两份代码仅供参考,实际应用中还需要根据业务需求进行相应的调整和优化
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值