本文主要是介绍switch case结合枚举值使用,借助枚举的值来做case分支判断,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!
场景描述
在写业务的时候,遇到这么个场景,有一个字段,在数据库中以多种不同的代码来区分,具体一点就是一个 类别_id
,数据库里它是以 001/002/003/004
几个不同的字段形式去区分的。
在业务中需要将这些信息全部取出来,然后再通过字段区分,在区分时我在想能否借助switch case
而不是一直在用的 if else if
,放入对应的list中,并以键值对形式返回前端。
那么在这里就有问题了,这些用来区分类别的特殊代码,是对应着不同的中文意思,而且设置键值对时,key值也不好用中文返回给前端,那么,这些用不同特殊代码区分的不同的list是具有不同的key值,并且具有用以区分的不同的特殊代码。
如果把这些特殊字段的代码啊key值写死在service的代码里,个人觉得是不太美观的,而且修改的时候还要来这边看,一大堆东西写下来,我觉得挺难看的,所以我就思考能否将数据抽取出来,弄成枚举值去解决这个问题。
问题简化
-
以中文为枚举值,内含对应的自己设定的key值和对应的特殊代码。
-
枚举值设置好之后去结合
switch case
分支使用。
场景复现
未使用枚举类代码前,想要把什么东西都写死在代码里:
//初始化装载list的map,key为类别名称,value是对应的listHashMap<String, List<ArcAdmLccInfoDTO>> listMap = new HashMap<>(4);//数据库查出的未分类的全部数据List<TestObject> queryList = xxx.findAllWithCategoryCode(Object);//初始化4个装载对应对象的listArrayList<TestObject> apple = new ArrayList<>();ArrayList<TestObject> waterMelon = new ArrayList<>();ArrayList<TestObject> grape = new ArrayList<>();ArrayList<TestObject> hamiMelon = new ArrayList<>();//对全部对象进行遍历筛选for (TestObject to : queryList) {switch (to.getCategoryCode()) {case "a-1":apple.add(to);break;case "w-2":waterMelon.add(to);break;case "g-3":grape.add(to);break;case "h-4":hamiMelon.add(to);break;default:break;}}listMap.put("apple", apple);listMap.put("waterMelon", waterMelon);listMap.put("grape", grape);listMap.put("hamiMelon", hamiMelon);return listMap;
后面整理了一下,归类后的枚举类代码如下:
public enum TestObejctEnums {苹果大厦("apple","a-1"),西瓜公寓("water-melon","w-2"),葡萄居所("grape","g-3"),哈密瓜楼("hami-melon","h-4"),;/*** categoryCode 是数据库中标识的特殊字段代码* specialKey 是用来作为list的key值使用*/private final String categoryCode;private final String specialKey;LccInfoEnums(String specialKey ,String categoryCode) {this.specialKey = specialKey;this.categoryCode = categoryCode;}public String getSpecialKey() {return specialKey;}public String getCategoryCode() {return categoryCode;}}
然后结合了switch case后理想效果应当如下:
//对全部对象进行遍历筛选for (TestObject to : queryList) {//这里面进行改动,case判断分支里用枚举值switch (to.getCategoryCode()) {case TestObejctEnums.苹果大厦.getCategoryCode():apple.add(to);break;case TestObejctEnums.西瓜公寓.getCategoryCode():waterMelon.add(to);break;case TestObejctEnums.葡萄居所.getCategoryCode():grape.add(to);break;case TestObejctEnums.哈密瓜楼.getCategoryCode():hamiMelon.add(to);break;default:break;}}
但是一切都不尽人意:
在case分支判断那里需要声明常量(需要在编译时被指定/避免二义性,balabala,这个地方为什么不能使用枚举类型获取值,个人感觉和JVM加载顺序有关,然后查了查,有说法是因为需要在编译时进行类型检查,编译的时候就应该指定,运行时候知道是什么值),这样的话,就需要对枚举类进行改造,才能在switch case中使用枚举类进行分支判断。但是该种改造还是存在一定的问题,并不能达到理想中的效果。
改造后的代码实现
参照网络上的方法进行改造之后,加入一个比对的方法
public enum TestObejctEnums {苹果大厦("apple","a-1"),西瓜公寓("water-melon","w-2"),葡萄居所("grape","g-3"),哈密瓜楼("hami-melon","h-4"),;/*** categoryCode 是数据库中标识的特殊字段代码* specialKey 是用来作为list的key值使用*/private final String categoryCode;private final String specialKey;LccInfoEnums(String specialKey ,String categoryCode) {this.specialKey = specialKey;this.categoryCode = categoryCode;}public String getSpecialKey() {return specialKey;}public String getCategoryCode() {return categoryCode;}//添加一个代码的匹配方法,对全部枚举值进行遍历,与传入的值进行比对,返回匹配的值public static TestObejctEnums matchCode(String categoryCode) {for (TestObejctEnums testObejctEnums : TestObejctEnums.values()) {//在这里可以根据需求更改拓展,我这里需要的是将枚举值里才存储的特殊代码段与数据库查出数据进行比对if (testObejctEnums.getCategoryCode().equals(categoryCode)) {return testObejctEnums;}}//根据需求变更return null;}}
改造后的根据枚举值比对的switch case方法:
switch(TestObjectEnums.matchCode(testObejct.getCategoryCode())){case 苹果大厦://把对象加入apple队列break;case 西瓜公寓://把对象加入waterMelon队列break;case 葡萄居所://把对象加入grape队列break;case 哈密瓜楼://把对象加入hamiMelon队列break;default:break;}listMap.put(TestObjectEnums.苹果大厦.getSpecialKey(),苹果队列);// ....以此类推return listMap;
问题反思
至此基本上就把我当时想要的效果表现出来了,即:将switch case
选择分支与枚举类结合使用,将固定的数据字段放入枚举类中,按需取出比对,同时还可以借助枚举去存取约定数据。
以上的实现只是我个人对该种需求实现的一个小想法,因为这样就可以将一些特殊字段取出枚举值中便于管理,同时有特殊的字段需求时,也可以一并加入里面,存取使用。
两个结构的对比分析:
其实在做这个的时候,我首先还是想对比switch case
和if elseif
两者的性能差别(其实如果数据量不大的时候两者的差别也不算特别大,数据量特别大的时候也不会采取这种设计)
在对这两个结构的查询中,了解到的一些简单的知识如下:
- 对于
switch case
分支选择结构,switch...case
编译后会生成储存各个case分支常量的地址
的跳表,这个会占用一点的空间,以空间换时间嘛。程序首先判断switch
中的变量地址是否大于最大case分支常量
的地址 ,若大于,则直接跳到default分支处理;否则取得索引号为switch变量大小的跳表项的地址(即跳表的起始地址+表项大小或索引号),程序接着跳到此地址执行,到此完成了分支的跳转。总的来说,就是switch case
编译后生成一个跳表,借助跳表的数据结构提高了检索速度。 - 对于
if else if
选择结构,简单地来说,就是从上而下逐个判断,有多少个分支,就进行多少次判断,直到命中分支之后返回对应位置。
那么在时间复杂度的计算上,对于switch case
的时间复杂度,就可以按照跳表的时间复杂度即O(log n)
来计算;对于if else if
的时间复杂度,即有多少个判断分支,就要判断多少次,即时间复杂度为 O(n)
。
上面虽然是这样去分析,但是结合实际场景来思考的话,我个人总结的几点如下:
1. 占用空间上,switch case
需要去生成一个跳表,那么其占用空间是比简单的if else if
占用的空间多一点,是以空间换时间的思想。
2. 在分支比较多的时候,switch case
是比if else if
更易于拓展,且具有更好的阅读体验
3. 但是在常量分布范围很大,但是需要筛选的数据量并不大时,switch case
此时占用过多的空间,对比if else if
毫无疑问失去了优势。
4. switch case
对于case
分支,是需要以常量来做判断的,在这里if else if
是可以判断一个范围,在一些场景使用更灵活。
我的做法存在的问题
在我这个业务场景中,为了实现枚举值与switch case
的结合,在枚举值中添加的循环方法,毫无疑问给整个判断结构添加了一个问题:在每次枚举值内数据与数据库对象字段对比时,都是一次循环比对,循环方法加入了一个O(n)
的时间复杂度,那么这时候就是 循环 + 跳表
,综合起来时间复杂度为 O(n*log n )
,毫无疑问,这样设计的话,仅仅是提高了程序内的可读性,却降低了性能。
//add on 20/11/23
在一个关于枚举的博客的评论中看到一个大佬提到的建议,为什么不直接尝试用hashmap去初始化数据呢?直接将所有枚举数据添加到hashmap中通过getKey方法去获取,我觉得这个可以思考一下如何实现
//todo 在枚举值初始化时添加到hashmap然后使用getKey操作,如果可以测试一下两种方法的优劣就最好了
最后
所以到最后,我都还是选择了if else if
这种经典的分支选择,但是知道了怎么把switch case
和枚举值结合按需使用,顺便了解了一下switch case
与if else if
的一些小知识。
对于两者的更多底层知识,我建议看一下这个文章:https://www.cnblogs.com/mukekeheart/p/10558167.html
这个博主从源码角度去分析了一下两个数据结构,我还没这个耐心和能力…要向更强的人看齐啊
这篇关于switch case结合枚举值使用,借助枚举的值来做case分支判断的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!