大量的if-else优化(判断具有一定的规律)

大概需求:希望对每个区间的数据量进行统计。

这是最初的写法

           List<String> oilRateList = new ArrayList<>();  //数据集合
            int[] spotDistributing = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0};
            oilRateList.forEach(s -> {
                double aDouble = Double.parseDouble(s) * 100;
                //包前不包后
                if (aDouble >= 0 && aDouble < 5) {
                    spotDistributing[0]++;
                } else if (aDouble >= 5 && aDouble < 10) {
                    spotDistributing[1]++;
                } else if (aDouble >= 10 && aDouble < 15) {
                    spotDistributing[2]++;
                } else if (aDouble >= 15 && aDouble < 20) {
                    spotDistributing[3]++;
                } else if (aDouble >= 20 && aDouble < 25) {
                    spotDistributing[4]++;
                } else if (aDouble >= 25 && aDouble < 30) {
                    spotDistributing[5]++;
                } else if (aDouble >= 30 && aDouble < 35) {
                    spotDistributing[6]++;
                } else if (aDouble >= 35 && aDouble < 40) {
                    spotDistributing[7]++;
                } else if (aDouble >= 40 && aDouble < 45) {
                    spotDistributing[8]++;
                } else {
                    spotDistributing[9]++;
                }
            });

性能并不差,但是很丑。所以需要优化

第一版本

大佬给一些思路,优化后的:

@Data
public class IntervalData {


    /**
     * 数据区间对象集合
     */
    private static List<Rule> rules;

    static {
        rules = new ArrayList<>();
        rules.add(new Rule(0D, 5D, DataRangeEnum.ZeroIsFive));
        rules.add(new Rule(5D, 10D, DataRangeEnum.FiveIsTen));
        rules.add(new Rule(10D, 15D, DataRangeEnum.TenIsFifteen));
        rules.add(new Rule(15D, 20D, DataRangeEnum.FifteenIsTwenty));
        rules.add(new Rule(20D, 25D, DataRangeEnum.TwentyIsTwentyFive));
        rules.add(new Rule(25D, 30D, DataRangeEnum.TwentyFiveIsThirty));
        rules.add(new Rule(30D, 35D, DataRangeEnum.ThirtyIsThirtyFive));
        rules.add(new Rule(35D, 40D, DataRangeEnum.ThirtyFiveIsForty));
        rules.add(new Rule(40D, 45D, DataRangeEnum.FortyIsFortyFive));
        rules.add(new Rule(45D, -1D, DataRangeEnum.FortyFiveIsAll));
    }

    /**
     * 生成数据
     */
    public static Map<DataRangeEnum, Integer> check(List<String> doubles) {
        Map<Rule, AtomicInteger> counts = new HashMap<>();
        doubles.forEach(v -> {
            Double doublev = Double.parseDouble(v) * 100;
            for (Rule rule : rules) {
                boolean aTrue = rule.isTrue(doublev);
                if (!aTrue) continue;
                if (counts.containsKey(rule)) {
                    counts.get(rule).addAndGet(1);
                } else {
                    counts.put(rule, new AtomicInteger(1));
                }
            }
        });
        Map<DataRangeEnum, Integer> data = new HashMap<>();
        counts.keySet().forEach(v -> data.put(v.dataRangeEnum, counts.get(v).get()));
        Map<DataRangeEnum, Integer> returnData = new LinkedHashMap<>();
        Arrays.stream(DataRangeEnum.values()).forEach(v -> {
            Integer integer = data.get(v);
            if (integer == null) {
                returnData.put(v, 0);
            } else {
                returnData.put(v, integer);
            }
        });
        return returnData;
    }

    /**
     * 数据区间对象
     */
    static class Rule {

        /**
         * 左侧
         */
        private Double left;

        /**
         * 右侧
         */
        private Double right;

        /**
         * 对应的枚举
         */
        private DataRangeEnum dataRangeEnum;

        public Rule(Double left, Double right, DataRangeEnum dataRangeEnum) {
            this.left = left;
            this.right = right;
            this.dataRangeEnum = dataRangeEnum;
        }

        public boolean isTrue(Double number) {
            if (this.right == -1D) {
                return number >= left;
            }
            return number >= left && number < right;
        }
    }
}

对应的枚举代码

@Getter
public enum DataRangeEnum {

    /**
     * 0-5
     */
    ZeroIsFive,

    /**
     * 5-10
     */
    FiveIsTen,

    /**
     * 10-15
     */
    TenIsFifteen,

    /**
     * 15-20
     */
    FifteenIsTwenty,

    /**
     * 20-25
     */
    TwentyIsTwentyFive,

    /**
     * 25-30
     */
    TwentyFiveIsThirty,

    /**
     * 30-35
     */
    ThirtyIsThirtyFive,

    /**
     * 35-40
     */
    ThirtyFiveIsForty,

    /**
     * 40-45
     */
    FortyIsFortyFive,

    /**
     * 45-
     */
    FortyFiveIsAll;
}

这个解决方法是通过对象来优化那些if-else
获取数据只用调一句话就行

Map<DataRangeEnum, Integer> check = IntervalData.check(oilRateList);
xx.setData(check.values());

很简便,但是通过观察大家可以看出,这个的获取数据的规则其实是写死的,正好我这个项目里面有两套规则,一套是上面的0-5,5-10…,还有一套是0-10,10-20…,上面写死了,如果要加的话,很不好看,这样不好,需要优化

我这里个人的解决方式是把规则提取出来

第二版本

上代码,规则枚举

@Getter
public enum BigRangeEnum {


    /**
     * 0-50 以5为间隔 统计十次
     */
    OneSpace(0, -1, 5, 10),

    /**
     * 0-100 以10为间隔 统计十次
     */
    TwoSpace(0, -1, 10, 10);

    /**
     * 范围左侧
     */
    private Integer bigLeft;

    /**
     * 范围右侧 (-1的意思是 最有一个判断区间范围 仅使用>=即可)
     */
    private Integer bigRight;

    /**
     * 间隔
     */
    private Integer space;

    /**
     * 计算次数
     */
    private Integer count;


    BigRangeEnum(Integer bigLeft, Integer bigRight, Integer space, Integer count) {
        this.bigLeft = bigLeft;
        this.bigRight = bigRight;
        this.space = space;
        this.count = count;
    }
}

上上一个枚举的升级版本

@Getter
public enum DataRangeEnum {

    /**
     * 0-5
     */
    ZeroIsFive(BigRangeEnum.OneSpace, 1),

    /**
     * 5-10
     */
    FiveIsTen(BigRangeEnum.OneSpace, 2),

    /**
     * 10-15
     */
    TenIsFifteen(BigRangeEnum.OneSpace, 3),

    /**
     * 15-20
     */
    FifteenIsTwenty(BigRangeEnum.OneSpace, 4),

    /**
     * 20-25
     */
    TwentyIsTwentyFive(BigRangeEnum.OneSpace, 5),

    /**
     * 25-30
     */
    TwentyFiveIsThirty(BigRangeEnum.OneSpace, 6),

    /**
     * 30-35
     */
    ThirtyIsThirtyFive(BigRangeEnum.OneSpace, 7),

    /**
     * 35-40
     */
    ThirtyFiveIsForty(BigRangeEnum.OneSpace, 8),

    /**
     * 40-45
     */
    FortyIsFortyFive(BigRangeEnum.OneSpace, 9),

    /**
     * 45-
     */
    FortyFiveIsAll(BigRangeEnum.OneSpace, 10),


    /**
     * 0-10
     */
    ZeroIsTen(BigRangeEnum.TwoSpace, 1),

    /**
     * 10-20
     */
    TenIsTwenty(BigRangeEnum.TwoSpace, 2),

    /**
     * 20-30
     */
    TwentyIsThirty(BigRangeEnum.TwoSpace, 3),

    /**
     * 30-40
     */
    ThirtyIsForty(BigRangeEnum.TwoSpace, 4),

    /**
     * 40-50
     */
    FortyIsFifty(BigRangeEnum.TwoSpace, 5),

    /**
     * 50-60
     */
    FiftyIsSixty(BigRangeEnum.TwoSpace, 6),

    /**
     * 60-70
     */
    SixtyIsSeventy(BigRangeEnum.TwoSpace, 7),

    /**
     * 70-80
     */
    SeventyIsEighty(BigRangeEnum.TwoSpace, 8),

    /**
     * 80-90
     */
    EightyIsNinety(BigRangeEnum.TwoSpace, 9),

    /**
     * 90-
     */
    NinetyIsAll(BigRangeEnum.TwoSpace, 10);


    private BigRangeEnum belong;


    private Integer sort;

    DataRangeEnum(BigRangeEnum belong, Integer sort) {
        this.belong = belong;
        this.sort = sort;
    }


    /**
     * 根据归属和排序 获取枚举
     */
    public static DataRangeEnum getDataRangeEnumByBelongAndSort(BigRangeEnum belong, Integer sort) {
        DataRangeEnum[] dataRangeEnums = DataRangeEnum.values();
        Optional<DataRangeEnum> first = Arrays.stream(dataRangeEnums).filter(v -> v.getBelong().equals(belong) && v.getSort().equals(sort)).findFirst();
        return first.orElse(null);
    }

    /**
     * 根据归属,获取枚举集合
     */
    public static List<DataRangeEnum> getListByBelong(BigRangeEnum belong) {
        DataRangeEnum[] dataRangeEnums = DataRangeEnum.values();
        return Arrays.stream(dataRangeEnums).filter(v -> v.getBelong().equals(belong)).collect(Collectors.toList());
    }
}

核心计算代码

@Data
public class IntervalData {


    /**
     * 数据区间对象集合
     */
    private static List<Rule> rules;

    /**
     * 计算入口
     */
    public static Map<DataRangeEnum, Integer> CalculateEntrance(BigRangeEnum bigRangeEnum, List<String> doubles) {
        rules = new ArrayList<>();
        for (int i = 1; i <= bigRangeEnum.getCount(); i++) {
            Rule rule;
            if (i == bigRangeEnum.getCount()) {
                rule = new Rule(bigRangeEnum.getSpace() * (i - 1) * 1.0, bigRangeEnum.getBigRight() * 1.0, DataRangeEnum.getDataRangeEnumByBelongAndSort(bigRangeEnum, i));
            } else {
                rule = new Rule(bigRangeEnum.getSpace() * (i - 1) * 1.0, bigRangeEnum.getSpace() * i * 1.0, DataRangeEnum.getDataRangeEnumByBelongAndSort(bigRangeEnum, i));
            }
            rules.add(rule);
        }
        return check(bigRangeEnum, doubles);
    }


    /**
     * 生成数据
     */
    private static Map<DataRangeEnum, Integer> check(BigRangeEnum bigRangeEnum, List<String> doubles) {
        Map<DataRangeEnum, AtomicInteger> counts = new HashMap<>();
        doubles.forEach(v -> {
            Double doublev = Double.parseDouble(v) * 100;
            for (Rule rule : rules) {
                boolean aTrue = rule.isTrue(doublev);
                if (!aTrue) continue;
                if (counts.containsKey(rule.dataRangeEnum)) {
                    counts.get(rule.dataRangeEnum).addAndGet(1);
                } else {
                    counts.put(rule.dataRangeEnum, new AtomicInteger(1));
                }
            }
        });
        Map<DataRangeEnum, Integer> returnData = new LinkedHashMap<>();
        DataRangeEnum.getListByBelong(bigRangeEnum).forEach(v -> {
            AtomicInteger integer = counts.get(v);
            if (integer == null) {
                returnData.put(v, 0);
            } else {
                returnData.put(v, integer.get());
            }
        });
        return returnData;
    }

    /**
     * 数据区间对象
     */
    static class Rule {

        /**
         * 左侧
         */
        private Double left;

        /**
         * 右侧
         */
        private Double right;

        /**
         * 对应的枚举
         */
        private DataRangeEnum dataRangeEnum;

        public Rule(Double left, Double right, DataRangeEnum dataRangeEnum) {
            this.left = left;
            this.right = right;
            this.dataRangeEnum = dataRangeEnum;
        }

        public boolean isTrue(Double number) {
            if (this.right == -1D) {
                return number >= left;
            }
            return number >= left && number < right;
        }
    }
}

这里其实就是通过指定的规则,去生成rules然后沿用上一版本的计算方法。

看到这,大家可能心里会有一个疑问,这样的处理,好看是好看了,代码性能怎么样。经过我的测试,在数据量小于10w条的时候,第二版本的运行时间是小于大量的if-else的,但是数据量一旦超过10w条,第二版本的性能就大打折扣,测试中1000w条数据,大量的if-else的执行时间平均为600ms,但是第二版本的执行时间平均达到了1200ms,性能太差了,需要优化。

通过观察第二版本的核心计算代码其实可以看出来doubles为数据总量,rules是每一个范围的抽象对象
——大量的if-else的循环次数就=doubles个数
——第二版本的总循环次数=doubles个数 * rules个数
当数据量较小的时候,看不出来,一旦数据量足够多,比如1000w条,那么此时大量的if-else的循环次数为1000w次,但是第二版本的循环次数达到了1000w*10,一亿次,也怪不得性能差了。需要优化

第三版本

核心计算代码

@Data
public class IntervalData {
    

    /**
     * 数据区间对象集合
     */
    private static List<Rule> rules;

    /**
     * 计算入口
     */
    public static Map<DataRangeEnum, Integer> CalculateEntrance(BigRangeEnum bigRangeEnum, List<String> doubles) {
        rules = new ArrayList<>();
        for (int i = 1; i <= bigRangeEnum.getCount(); i++) {
            Rule rule;
            if (i == bigRangeEnum.getCount()) {
                rule = new Rule(bigRangeEnum.getSpace() * (i - 1) * 1.0, bigRangeEnum.getBigRight() * 1.0, DataRangeEnum.getDataRangeEnumByBelongAndSort(bigRangeEnum, i));
            } else {
                rule = new Rule(bigRangeEnum.getSpace() * (i - 1) * 1.0, bigRangeEnum.getSpace() * i * 1.0, DataRangeEnum.getDataRangeEnumByBelongAndSort(bigRangeEnum, i));
            }
            rules.add(rule);
        }
        return check(bigRangeEnum, doubles);
    }


    /**
     * 生成数据
     */
    private static Map<DataRangeEnum, Integer> check(BigRangeEnum bigRangeEnum, List<String> doubles) {
        Map<DataRangeEnum, AtomicInteger> counts = new HashMap<>();
        doubles.forEach(v -> {
            Double doublev = Double.parseDouble(v) * 100;
            int i = (int) (doublev / bigRangeEnum.getSpace());
            Rule rule = (i >= rules.size()) ? rules.get(rules.size() - 1) : rules.get(i);
            if (counts.containsKey(rule.dataRangeEnum)) {
                counts.get(rule.dataRangeEnum).addAndGet(1);
            } else {
                counts.put(rule.dataRangeEnum, new AtomicInteger(1));
            }

        });
        Map<DataRangeEnum, Integer> returnData = new LinkedHashMap<>();
        DataRangeEnum.getListByBelong(bigRangeEnum).forEach(v -> {
            AtomicInteger integer = counts.get(v);
            if (integer == null) {
                returnData.put(v, 0);
            } else {
                returnData.put(v, integer.get());
            }
        });
        return returnData;
    }

    /**
     * 数据区间对象
     */
    static class Rule {

        /**
         * 左侧
         */
        private Double left;

        /**
         * 右侧
         */
        private Double right;

        /**
         * 对应的枚举
         */
        private DataRangeEnum dataRangeEnum;

        public Rule(Double left, Double right, DataRangeEnum dataRangeEnum) {
            this.left = left;
            this.right = right;
            this.dataRangeEnum = dataRangeEnum;
        }
    }
}

通过观察第二版本的代码可以发现rules的顺序一直都是一个好的顺序,是一个按照0-5,5-10…的一个顺序,那么此时,我们就可以用数据值/规则间距得出来的值,就是rules对应的区间的数据位置,当然,需要考虑超过rules范围的值,超过范围的就去最后一个即可

这样处理,第二版本的循环数量直接减少了rules个数倍。
那么最后的结果是多少呢
跑一个1000w条数据试试
在这里插入图片描述
上面是大量的if-else的结果,下面是第三版本的结果,相差无几!!!(if-else的性能不差)

这个设计的话,还有一个好处,后续有任何需要添加规则,修改规则,去规则枚举里改就行。

到此,撒花,完美解决。

2023-03-13 补

其实再再仔细观察一下可以发现Rule 对象是没有什么用的,最初的版本需要通过它来判断区间,但是现在区间的判断不需要他了,在这个对象里面,只会用到DataRangeEnum这个枚举,每次创建对象,都需要开辟内存空间,所以,就把Rule对象给优化掉吧

上代码

@Data
public class IntervalData {


    /**
     * 数据范围枚举集合
     */
    private static List<DataRangeEnum> rangeEnumList;

    /**
     * 计算入口
     */
    public static Map<DataRangeEnum, Integer> CalculateEntrance(BigRangeEnum bigRangeEnum, List<String> doubles) {
        rangeEnumList = new ArrayList<>();
        for (int i = 1; i <= bigRangeEnum.getCount(); i++) {
            rangeEnumList.add(DataRangeEnum.getDataRangeEnumByBelongAndSort(bigRangeEnum, i));
        }
        return check(bigRangeEnum, doubles);
    }


    /**
     * 生成数据
     */
    private static Map<DataRangeEnum, Integer> check(BigRangeEnum bigRangeEnum, List<String> doubles) {
        Map<DataRangeEnum, AtomicInteger> counts = new HashMap<>();
        doubles.forEach(v -> {
            Double doublev = Double.parseDouble(v) * 100;
            int i = (int) (doublev / bigRangeEnum.getSpace());
            DataRangeEnum dataRangeEnum = (i >= rangeEnumList.size()) ? rangeEnumList.get(rangeEnumList.size() - 1) : rangeEnumList.get(i);
            if (counts.containsKey(dataRangeEnum)) {
                counts.get(dataRangeEnum).addAndGet(1);
            } else {
                counts.put(dataRangeEnum, new AtomicInteger(1));
            }

        });
        Map<DataRangeEnum, Integer> returnData = new LinkedHashMap<>();
        DataRangeEnum.getListByBelong(bigRangeEnum).forEach(v -> {
            AtomicInteger integer = counts.get(v);
            if (integer == null) {
                returnData.put(v, 0);
            } else {
                returnData.put(v, integer.get());
            }
        });
        return returnData;
    }
}

最后,再跑一次1000w数据瞅瞅
在这里插入图片描述
嗯,很完美

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值