目的 很多网站都有对于点赞功能的限制,例如一个小时内点多少赞,或者一篇文章点多少次赞,今天就来设计一下这个功能如何实现,先基于Java的数据结构进行开发,后面可拓展到Redis中的数据结构 。一小时内点在5次(可自行修改),为了方便测试,本人设计的60s内限制点赞五次
结构 文章ID(passageId) 该字段记录点赞的具体文章ID,因为Id具有唯一性,唯一标识一篇文章,所以对该字段进行记录。
用户ID(uid) 该字段记录点赞用户的ID,同上述文章ID,都具有唯一性,所以记录该字段。
点赞次数(count) 初始化时默认为1,因为该数据结构为点赞一次发生所创建的,默认值为1,后面再次点赞需要对该点赞次数变量进行更改和同步
时间轴(time_history) 这也是该数据结构中比较核心的功能
使用 PriorityQueue<Date>
这个优先队列进行存储
由于我们该点赞是基于该点赞时间点前限制一个小时内只能点赞五次,假如我现在1点20分 ,1点22分 ,1点23分 ,1点30分 ,1点40分 ,这几个时间点 点了5此赞,这时候我们1点45分 再去点一次赞就会打印点不了赞(Sout),但是如果现在是2点25 又来点赞,我们发现又可以点了
原来的time_history :
1点20分—–> 1点22分 —-> 1点23分 —> 1点30分 —> 1点40分
1点45点赞 的time_history:
1点20分—–> 1点22分 —-> 1点23分 —> 1点30分 —> 1点40分 (count已经为5,打印不能点赞)
2点25点赞 的time_history:
1点30分 —> 1点40分 —> 2点25分
原因: (count为3,因为1点20,1点22,1点23已经超出2点25前的一个小时了)
哈希表 我们利用HashMap将上述字段进行存储,格式如下图
代码 初始化结构 将点赞次数设置为0次,若要持久化到Redis中(Springboot网络项目),点赞次数设为1,因为该程序为本地调试
public StarObject (int passageId,int uid) { this .passageId = passageId; this .uid = uid; this .starCount = 0 ; map.put("starCount" ,this .starCount); map.put("passageId" ,this .passageId); map.put("uid" ,this .uid); map.put("time_history" ,this .time_history); }
计算当前时间与时间轴最早时间的差值(以秒为单位) public long tranverseTime (Date head,Date now) { long sub = now.getTime() - head.getTime(); sub = sub / 1000 ; return sub; }
点赞API 步骤:
首先获取当前时间点now
获得时间轴最早时间peek(可能为空或非空)
这时候分两种情况
public void star () { PriorityQueue<Date> time_history = (PriorityQueue<Date>)map.get("time_history" ); Integer starCount = (int )map.get("starCount" ); Date peek = time_history.peek(); Date now = new Date(); if (peek == null ||tranverseTime(peek,now) <= 60 ){ if (starCount < 5 ){ time_history.add(now); starCount++; }else { System.out.println("点赞60s超过5次了……" ); return ; } }else { while (tranverseTime(peek,now) > 60 ){ time_history.poll(); starCount--; peek = time_history.peek(); if (peek == null ) break ; } if (starCount < 5 ){ time_history.add(now); starCount++; }else { System.out.println("(else)点赞60s超过5次了……" ); return ; } } map.put("time_history" ,time_history); map.put("starCount" ,starCount); }
总代码 public class StarObject { int passageId; int uid; PriorityQueue<Date> time_history = new PriorityQueue<>(); int starCount; HashMap map = new HashMap<String,Object>(); public StarObject (int passageId,int uid) { this .passageId = passageId; this .uid = uid; this .starCount = 0 ; map.put("starCount" ,this .starCount); map.put("passageId" ,this .passageId); map.put("uid" ,this .uid); map.put("time_history" ,this .time_history); } public long tranverseTime (Date head,Date now) { long sub = now.getTime() - head.getTime(); sub = sub / 1000 ; return sub; } public void star () { PriorityQueue<Date> time_history = (PriorityQueue<Date>)map.get("time_history" ); Integer starCount = (int )map.get("starCount" ); Date peek = time_history.peek(); Date now = new Date(); if (peek == null ||tranverseTime(peek,now) <= 60 ){ if (starCount < 5 ){ time_history.add(now); starCount++; }else { System.out.println("点赞60s超过5次了……" ); return ; } }else { while (tranverseTime(peek,now) > 60 ){ time_history.poll(); starCount--; peek = time_history.peek(); if (peek == null ) break ; } if (starCount < 5 ){ time_history.add(now); starCount++; }else { System.out.println("(else)点赞60s超过5次了……" ); return ; } } map.put("time_history" ,time_history); map.put("starCount" ,starCount); } }
测试代码 import java.util.Date;import java.util.PriorityQueue;import java.util.HashMap;import java.util.concurrent.TimeUnit;public class StarObject { int passageId; int uid; PriorityQueue<Date> time_history = new PriorityQueue<>(); int starCount; HashMap map = new HashMap<String,Object>(); public StarObject (int passageId,int uid) { this .passageId = passageId; this .uid = uid; this .starCount = 0 ; map.put("starCount" ,this .starCount); map.put("passageId" ,this .passageId); map.put("uid" ,this .uid); map.put("time_history" ,this .time_history); } public long tranverseTime (Date head,Date now) { long sub = now.getTime() - head.getTime(); sub = sub / 1000 ; return sub; } public void star () { PriorityQueue<Date> time_history = (PriorityQueue<Date>)map.get("time_history" ); Integer starCount = (int )map.get("starCount" ); Date peek = time_history.peek(); Date now = new Date(); if (peek == null ||tranverseTime(peek,now) <= 60 ){ if (starCount < 5 ){ time_history.add(now); starCount++; }else { System.out.println("点赞60s超过5次了……" ); return ; } }else { while (tranverseTime(peek,now) > 60 ){ time_history.poll(); starCount--; peek = time_history.peek(); if (peek == null ) break ; } if (starCount < 5 ){ time_history.add(now); starCount++; }else { System.out.println("(else)点赞60s超过5次了……" ); return ; } } map.put("time_history" ,time_history); map.put("starCount" ,starCount); } public static void main (String[] args) throws InterruptedException { StarObject starObject = new StarObject(123 ,111 ); for (int i = 0 ; i < 6 ; i++) { starObject.star(); if (i == 2 ){ System.out.println("第三次点赞……开始60s⏲" ); } TimeUnit.SECONDS.sleep(1 ); } System.out.println("starCount: " + starObject.map.get("starCount" )); System.out.println("time_history: " + starObject.map.get("time_history" )); new Thread(new TestThread(starObject)).start(); } } class TestThread implements Runnable { StarObject starObject; TestThread(StarObject starObject){ this .starObject = starObject; } @Override public void run () { try { TimeUnit.SECONDS.sleep(60 ); starObject.star(); System.out.println("(60s后)starCount: " + starObject.map.get("starCount" )); System.out.println("(60s后)time_history: " + starObject.map.get("time_history" )); } catch (InterruptedException e) { e.printStackTrace(); } } }
运行结果: