目录
- 1、关于三目运算符
- 2、精度问题
- 3、0.1+0.2等于几 ?float 0.7与double 0.7谁更大?
- 4、交换a和b的值
- 5、计时攻击
- [6、
- 7、计算1+2+…+n的值
- [8、
- 9、100G数据在4G内存中排序
- 10、1亿个数据进行TopK排序
系列文章
- 面试姊妹篇1:不起眼的Java陷阱题,一看就会,一做就错
- 面试姊妹篇2:有趣的逻辑面试题分享
- 面试姊妹篇3:常见的Java计算错误
- 面试姊妹篇4:常见的Java多线程面试题
- 面试姊妹篇5:常见的Java基础面试题
1 、 关 于 三 目 运 算 符 \color{7f1A8A}1、关于三目运算符 1、关于三目运算符
- 下面的运行结果应该输出什么?
public static void main(String[] args) {
Integer a = 1;
Integer b = 2;
Integer c = null;
Integer d = a > b ? a + b : c;
System.out.println(d);
}
- 你期待的:
null
- 实际上:
Exception in thread "main" java.lang.NullPointerException
at DesignPattern.single.dfghj.main(dfghj.java:16)
Process finished with exit code 1
-
原因
- 报错的第16行是这一句:Integer d = a > b ? a + b : c;
- 首先我们明确:条件运算符是右结合的,也就是说,从右向左分组计算。例如,a?b:c?d:e将按 a?b:(c?d:e) 执行。
- 基本类型跟包装类型在做转换的时候,有个自动包装和解包装的过程,会调用.xxxxValue(),比如.booleanValue(),.intValue(),.StringValue()。
- 三目运算符的“:”左右两边会自动保持一致,那么如果类型不一致,会发生解包装的过程。
- 如果两边类型不一致,比如题目中的:左边计算出来是int3,而不是Integer3,右边的也会解包装,那么null本身再次吊用.xxxxValue(),会发生NPE。
-
解决办法:保持“:”两边一致,
- 要么修改 a+b:Integer d = a > b ? Integer.valueOf(a + b) :c;
- 要么修改 c :Integer d = a > b ? a + b :null;
-
参考博客:三目运算符的空指针问题
2 、 精 度 问 题 \color{7f1A8A}2、精度问题 2、精度问题
- 题目:下列输出结果是什么?
BigDecimal bd1 = new BigDecimal("2.0");
BigDecimal bd2 = new BigDecimal("2.00");
System.out.println("equals: " + bd1.equals(bd2));
System.out.println("compareTo: " + bd1.compareTo(bd2));
- 结果:
equals: false
compareTo: 0
- 原因:
- 因为 JDK 认为 2.0 和 2.00 的精度不一样,所以不能 equals,但值确实是相等的。
2 、 0.1 + 0.2 等 于 几 ? f l o a t 0.7 与 d o u b l e 0.7 谁 更 大 ? \color{7f1A8A}2、0.1+0.2等于几?float 0.7与double 0.7谁更大? 2、0.1+0.2等于几?float0.7与double0.7谁更大?
- 下面的运行结果应该输出什么?
double a = 0.1;
double b = 0.2;
System.out.println(a+b);
- 输出结果
0.30000000000000004
- 做错了没关系,我们再看看这道题
float a = 0.7f, a1 = 2.7f;
double b = 0.7, b1 = 2.7;
if (b > a) {
System.out.println("double 0.7 > float 0.7");
} else {
System.out.println("double 0.7 < float 0.7");
}
if (b1 > a1) {
System.out.println("double 2.7 > float 2.7");
} else {
System.out.println("double 2.7 < float 2.7");
}
- 输出结果
double 0.7 > float 0.7
double 2.7 < float 2.7
- 原因分析:
- 这跟我们大学学的十进制转换二进制有关,一定存在某些数,不是完全匹配的,就像1/3表示成小数,是无限循环,而我们存储是有限的,会发生截取,因此就不完全想等了,具体细节分析见我的另一篇博客:float类型与double类型数谁更大?
- 建议
- 如果像银行业等对精度要求比较高的行业,建议使用BigInteger和BigDecimal,它两分别表示大整数类和大浮点数类
- 但是BigDecimal的构造函数选取不当,也会造成精度缺失,具体见这篇博客:BigDecimal一定不会丢失精度吗
- 精度不缺失的原理是使用字符串相加相乘,具体见我的另外一篇博客:大整数乘法其实很简单(Java)
4 、 交 换 a 和 b 的 值 \color{7f1A8A}4、交换a和b的值 4、交换a和b的值
- 题目:不使用其他变量的情况下,如何交换a和b的值
常规写法:
public static void main(String[] args) {
int a = 5;
int b = 6;
a = a + b;
b = a - b;
a = a - b;
System.out.println("a:" + a + "b:" + b);
}
异或写法
public static void main(String[] args) {
int a = 8;
int b = 8;
a = a ^ b;
b = a ^ b;
a = a ^ b;
System.out.println("new--->" + "a:" + a + ",b:" + b);
}
- 针对有博客指出常规写法有内存溢出问题,理论上是有的,但是可能jdk1.8以后做了优化,亲测没有溢出的。
- 针对有博客指出异或写法对两个相同的数异或有bug,但是亲测也没有。
- 异或写法可能相对常规写法,会减少计算的累加次数。
5 、 计 时 攻 击 \color{7f1A8A}5、计时攻击 5、计时攻击
- 写一段判断字符串相等的代码
常规写法
public boolean equals(Object anObject) {
if (this == anObject) {
return true;
}
if (anObject instanceof String) {
String anotherString = (String)anObject;
int n = value.length;
if (n == anotherString.value.length) {
char v1[] = value;
char v2[] = anotherString.value;
int i = 0;
while (n-- != 0) {
if (v1[i] != v2[i])
return false;
i++;
}
return true;
}
}
return false;
}
- 上面这一段选自JDK的equals方法,大致先判断
- 是否引用地址一样
- 是否属于String类型
- 长度是否一致
- 挨个对比每个字符是否一样
- 写成这样就可以了,但是不完美,这会引发安全问题,暴露一种叫做计时攻击的漏洞(请自行百度计时攻击)。
- 按照下面这种写法,会牺牲效率,达到防计时攻击。
防计时攻击写法
public boolean equals(String s1, String s2) {
if (s1 == null || s2 == null) {
return false;
}
if (s1 == s2) {
return true;
}
if (s1.length() != s2.length()) {
return false;
}
int res = 0;
for (int i = 0; i < s1.length(); i++) {
res |= s1.charAt(i) ^ s2.charAt(i);
}
return res == 0;
}
7 、 计 算 1 + 2 + . . . + n 的 值 \color{7f1A8A}7、计算1+2+...+n的值 7、计算1+2+...+n的值
- 题目描述:求1+2+3+…+n,要求不能使用乘除法、for、while、if、else、switch、case等关键字及条件判断语句(A?B:C)。
- 解题思路及代码:
public class Solution {
private static int[] res = {0};
public int Sum_Solution(int n) {
/* 1、循环的写法
int sum = 0;
for(int i = 1;i <= n; i++){
sum += i;
}
return sum; */
/* 2、数学规律
return (1 + n) * n / 2; */
/* 3、递归
return n == 0 ? 0 : n + Sum_Solution(n-1); */
// 4、递归-满足题意
try{
return res[n];
}catch(Exception e) {
return n + Sum_Solution(n-1);
}
}
}
9 、 100 G 数 据 在 4 G 内 存 中 排 序 \color{7f1A8A}9、100G数据在4G内存中排序 9、100G数据在4G内存中排序
- 方法一:
- 使用bitmap,将数据转成二进制的某一位,那么4个字节可以表示:2^32个数字,
- 方法二:
- 使用多路归并:
- 第一步:每次读取4G数据,进行排序,写回外存,最终形成25个文本。【IO次数:25次】
- 第二步:从每个文本中读取最小值,共25个,进行排序,选取最最小值,写回新文本。
- 第三部:重复第二步,直到所有数据读取完。【IO次数:100G次】
- 使用多路归并:
- 方法三:
- 败者树:
- 上述提到的25个文本互相晋级,每次晋级对比的时候,仍然分成两份,一个比另一份都要大,大的晋级,小的不动。
- 败者树:
10 、 1 亿 个 数 据 进 行 T o p K 排 序 \color{7f1A8A}10、1亿个数据进行TopK排序 10、1亿个数据进行TopK排序
- 方法一:
- 维护最小堆,在内存中维护一个K大的堆,一亿个数逐个进来,最终这个堆就是TopK