Switch语句的发展历程
1.最初版本的Switch语句:
1 | switch (表达式) { |
特点
- 只能用于 整数类型
- byte
- short
- char
- int
case必须是 编译期常量- 必须写 break
如果不写 break 就会出现 fall-through(贯穿执行)
表达式可以是变量,可以是有返回值的函数,也可以是计算结果
1 | switch (getUser()) { |
1 | int a = 10; |
2.Java5时期引入了enum枚举类型,Switch也将其引入
1 | enum Day { |
3.Java7之后Switch开始支持String字符串类型
1 | String fruit = "apple"; |
我认为这是一个很重要的开始
对字符串的支持,实际上底层是使用的hashcode()方法实现的;
Java 编译器会把
1 | switch(String) |
转换成
1 | switch(hashCode) |
大致逻辑:
1 | switch (fruit.hashCode()) { |
流程:
1 | String |
这也恰巧说明了重写hashcode()方法也要重写对应的equals()方法,否则会引起一系列的问题;两者hashcode相同但两者不equals的话两者其实是不相同的;但是如果没有重写equals方法的话,就可能认为两者相同,导致一系列问题;
例如在hashset中,如果没有重写存储对象的hashcode方法和equals方法的话会有一系列问题:
如果只重写hashcode但是不重写equals方法: 两个逻辑上相等的对象算出相同的哈希值,由于你没重写 equals,它调用的是 Object 默认的地址比较(==),两个对象地址不同,被放进不同的hash桶,就会导致其失去其重要的去重能力,你就会在hashset中取到相同的对象;
如果不重写hashcode但是重写equals方法: 两个逻辑相同的对象,因为内存地址不同,计算出了完全不同的哈希值;直接存进了不同的哈希桶中,也无法去重;
hashset底层就是hashmap,所以hashmap也是如此;
分享一个相关的严重的计算机事故:
划时代的危机:HashDoS 攻击
2011 年左右(正是 Java 7 发布前后),全球互联网爆发了著名的 HashDoS(哈希拒绝服务攻击)。
什么是 HashDoS?
由于 Java(以及当时的 PHP、Python 等)使用简单的哈希算法来存储请求参数(如 HashMap),攻击者可以伪造成千上万个 哈希值完全相同 的字符串。
- 后果:当这些字符串进入服务器时,原本极快的哈希表会退化成一个极其缓慢的链表。
- 计算量爆炸:原本 $O(1)$ 的操作变成了 $O(n)$。攻击者只需发送极小的流量,就能让服务器 CPU 飙升到 100% 导致瘫痪。
连锁反应:数据结构的进化
为了应对这个问题,Java 随后在 Java 8 中对 HashMap 进行了史诗级更新:
- 红黑树优化:当哈希碰撞超过一定阈值(默认 8 个)时,链表会转换为红黑树。
- 性能保证:即使在极端攻击下,查询时间也能保证在 O(log n),彻底终结了简单 HashDoS 攻击。
这就引发了hashmap底层向数组+链表+红黑树的转变;当数组的长度大于64同时链表长度大于8,链表就会转换为红黑树;
红黑树是一种自平衡的二叉搜索树,查询的时间复杂度是O(log n),相较于链表的O(n)复杂的会快很多;
红黑树实现的是大致平衡,最长的路径不会大于其他路径的两倍,相较于二叉平衡树的严格平衡策略(两个子树的高度差最多为 1),在增加和删除节点时需要更少的旋转来平衡,具有更少的消耗; 并且红黑树在Java中已经有更完整的使用方案;
4.Java12又产生了新的写法:
1 | int result = switch (day) { |
这样不用写break;也可以避免贯穿执行的问题;
还可以合并多个case:
1 | int num = switch(day) { |
1 | int result = switch(day) { |
对于多行代码要使用 yield 来返回值;
5.Java17之后又推出了模式匹配
1 | Object obj = "Hello"; |
不再是常量匹配, 出现了类型匹配;
6.Java 21又出现了 条件匹配 和 null匹配:
1 | switch (obj) { |
1 | switch (obj) { |

