1. Arrays 中的双轴快排
Jdk 1.7 之后,工具类 Arrays
中的排序方法在排序对象为基本数据类型的时候会使用双轴快排
,这种排序方法比经典排序更快
public static void sort(int[] a) {
DualPivotQuicksort.sort(a, 0, a.length - 1, null, 0, 0);
}
如我们所知,经典快排
会挑一个数(Pivot)把数组分成两份,而 Dual-Pivot
其实就是用两个Pivot,把整个数组分成三份
经典快排核心思想
接受一个数组,挑一个数(Pivot),把比这个数小的那一摊数放在它的左边,比它大的那一摊数放在它的右边,然后再对这个数左右两摊数递归的执行快排过程,直到子数组只剩一个数为止
我们通常用时间复杂度
来衡量一个算法的性能,对于排序算法,时间复杂度的计算依据的是排序导致的元素比较的次数,主要和 CPU 相关。如果按照元素比较次数
来比较的话,其实双轴快排
的元素比较次数比经典快排
要多,而双轴快排
更快的原因如下,它拥有比经典快排更少的元素扫描动作
CPU速度和内存速度的不匹配
CPU速度远比内存速度更快,因为CPU会等待内存传输数据,内存墙(Memory Wall)的存在限制了程序运行速度。这种情况下,
元素比较次数
的多少已经无法完全反映算法优劣,因为这种比较的方法只考虑了CPU的因素,没有考虑内存的因素新的性能衡量标准:扫描元素个数
为了解决时间复杂度无法反映算法真实性能的问题,双轴排序的作者提出了新的衡量标准,即
扫描元素个数
。这种标准把对于数组里面一个元素的访问array[i]
称为一次扫描,对于同一个下标其对应的值不变的话,即使多次读写也只算一次扫描。这种标准实际反映的是CPU与内存之间的数据流量的大小,将比较慢的内存的因素也考虑进去了,更能体现算法在当下计算机里面的性能
参考: https://arxiv.org/pdf/1511.01138.pdf
2. Java 中 switch 支持字符串的原理
一个 switch
的使用示例如下,代码很简单,我们无法获知任何有关于其原理的信息,因为这些都被隐藏在虚拟机的实现里
public static void main(String[] args) {
String s = "nathan";
getString(s);
}
public static void getString(String s) {
switch (s) {
case "good":
System.out.println("good");
break;
case "ok":
System.out.println("ok");
break;
default:
System.out.println(s);
break;
}
}
编译以上代码,使用 IDEA 查看其 class 文件,立马在其中发现端倪。从反编译的文件中很明显可以看到,switch 支持字符串是通过 equals() 和 hashCode() 方法来实现的
仔细看可以发现,进行 switch 的实际是字符串 hashCode() 方法返回的 int 类型哈希值,case 命中后通过 equals() 方法进一步进行安全检查,以避免哈希碰撞导致的错误命中
其实 switch 底层只支持整型数据,比如 byte、short、char(ASCII码是整型)以及int,其他数据类型都是转换成整型之后再使用的
public static void main(String[] args) {
String s = "nathan";
getString(s);
}
public static void getString(String s) {
byte var2 = -1;
switch(s.hashCode()) {
case 3548:
if (s.equals("ok")) {
var2 = 1;
}
break;
case 3178685:
if (s.equals("good")) {
var2 = 0;
}
}
switch(var2) {
case 0:
System.out.println("good");
break;
case 1:
System.out.println("ok");
break;
default:
System.out.println(s);
}
}
3. Java 中 break,continue 标签的用法
Java 的控制循环结构中没有 goto
关键字,为了弥补这方面的不足,Java 提供了 break
和 continue
的标签用法。Java 中的标签就是一个紧跟着冒号:
的标识符,标签必须放在循环前面才有作用,其语法示例如下,Java 中需要使用标签的唯一理由是想从多层嵌套中 break 或者 continue
break redo 跳到 redo 处,且不再进入循环
continue redo 跳到 redo 处,且再次进入循环
public static void main(String[] args) throws Exception {
redo:
for (; ; ) {
continue redo;
}
}