java-redis排行榜value+time排序

本文预览

需求

做一个送花排行榜

  • 根据玩家送花数量倒叙排列
  • 送花数量相同时,先送花的在前面

思路

用redis存取数据,自动维护有序集合 。
但值一样时,无法保证谁在前谁在后。所以可以把存入的时间考虑进去。

这样排序就变成了: 数值 + 时间。
因为送花数量是整数,那能不能把时间变成小数加在后面呢,这样取数据的时候,直接取整就是原始数据了。

公式:
newValue = value + 1 - time / Math.pow(10, (int) Math.log10(time) + 1d)
value原始数据,time是存入时间毫秒数,newValue存入redis中的数据
newValue整数部分等于value

极值大:value大&time小。
两个玩家value值一样,先进榜(time小)的应该在前面。

代码

public class RankWithTime {
    // 玩家类
    static class Player {
        String name;
        int value;
        double saveTime;

        public Player(String name, int value) {
            this.name = name;
            this.value = value;
        }

        public void setSaveTime(double saveTime) {
            this.saveTime = saveTime;
        }

        public int getValue() {
            return value;
        }

        public String getName() {
            return name;
        }

        @Override
        public String toString() {
            return "Player{" +
                    "name='" + name + '\'' +
                    ", value=" + value +
                    ", saveTime=" + saveTime +
                    '}';
        }
    }

    // 数据存储部分 启动类redis作用
    static class Redis {
        private static Redis INSTANCE;

        private Redis() {}

        public static Redis getInstance() {
            if (INSTANCE == null) {
                INSTANCE = new Redis();
            }
            return INSTANCE;
        }
        /**
         * 假设这是redis中保存的数据,这里没有顺序要求
         */
        private Map<String, Double> dbData = new HashMap<>();

        /**
         * 向redis中存入数据
         * 保存的是经过处理之后的数据
         * @param key
         * @param value
         */
        public long put(String key, int value) {
            long time = System.currentTimeMillis();
            double dValue = value + 1 - time / Math.pow(10, (int) Math.log10(time) + 1d);
            dbData.put(key, dValue);
            return time;
        }

        /**
         * 获取数据 升序
         * @return ArrayList
         */
        public List<Map.Entry<String, Double>> getListAsc() {
            return dbData.entrySet().stream().sorted(Comparator.comparing(Map.Entry::getValue)).collect(Collectors.toList());
    }

        /**
         * 获取数据 降序
         * @return ArrayList
         */
        public List<Map.Entry<String, Double>> getListDesc() {
            return dbData.entrySet().stream().
                    sorted((o1, o2) -> (int) (o2.getValue() - o1.getValue())).collect(Collectors.toList());
        }

        /**
         * 获取数据 升序
         * @return Map
         */
        public Map<String, Double> getMapAsc() {
            // HashMap
            Map<String, Double> hashMap = dbData.entrySet().stream()
                    .sorted((o1, o2) -> (int) (o1.getValue() - o2.getValue()))
                    .collect(Collectors.toMap(o -> o.getKey(), o -> o.getValue(), (oldVal, newVal) -> newVal));

            // LinkedHashMap 有序的
            Map<String, Double> linkedHashMap = dbData.entrySet()
                    .stream()
                    .sorted((o1, o2) -> (int) (o1.getValue() - o2.getValue()))
                    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (oldVal, newVal) -> newVal, LinkedHashMap::new));

            return linkedHashMap;
        }

        /**
         * 获取数据 降序
         * @return LinkedHashMap
         */
        public Map<String, Double> getMapDesc() {
            return dbData.entrySet()
                    .stream()
                    .sorted((o1, o2) -> (int) (o2.getValue() - o1.getValue()))
                    .collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue, (oldVal, newVal) -> newVal, LinkedHashMap::new));
        }
    }


    public static void main(String[] args) {
        // 创建 redis
        final Redis redis = Redis.getInstance();

        // 值相同 时间不同 结论:时间小的在前
        int value = 100;
        List<Player> players = new ArrayList<>();
        players.add(new Player("玩家1", value));
        players.add(new Player("玩家2", value + 1));
        players.add(new Player("玩家3", value));

        // 存数据
        players.forEach(player -> {
            long putTime = redis.put(player.getName(), player.getValue());
            player.setSaveTime(putTime);
            System.out.println("存入数据顺序 === " + player);

            // 延迟
            try {
                Thread.sleep(1L);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        // 取数据 降序
        List<Map.Entry<String, Double>> listAsc = redis.getListDesc();
        listAsc.forEach(entry -> System.out.println("取出数据顺序 === " + new Player(entry.getKey(), entry.getValue().intValue())));
    }
        /*
            存入数据顺序 === Player{name='玩家1', value=100, saveTime=1.610187990435E12}
            存入数据顺序 === Player{name='玩家2', value=101, saveTime=1.610187990441E12}
            存入数据顺序 === Player{name='玩家3', value=100, saveTime=1.610187990442E12}
            取出数据顺序 === Player{name='玩家2', value=101, saveTime=0.0}
            取出数据顺序 === Player{name='玩家1', value=100, saveTime=0.0}
            取出数据顺序 === Player{name='玩家3', value=100, saveTime=0.0}
         */
}

总结

对照输出结果来看:

  • value不同,value大的在前
  • value相同,先来的(time小)在前

完全满足需要

上一篇:TPS与事务


下一篇:十、Atomic和ABA问题