最近又参加了几场面试,非常幸运遇到一个非常好的面试官在编码的时候给我反馈,让我收获了一些编码规范上的新认识,虽然都是些很细小的东西,但感觉还是很有价值的。
循环的写法
常见的循环写法有 for 循环、while 循环,但是让你选择一个使用,会选择哪个?为什么?来看看一个简单题目:
给出一个字符串,若出现连续的奇数个空格,则将空格删除,其余情况不用管,返回最终的字符串。 例如 “a+++b++c++” (+代替空格),则最终的输出是 “ab++c++”
面试的时候遇到这种题目别提有多开心了,很快就可以写出这样的代码:
public String delBlank(String in) {
StringBuilder bd = new StringBuilder();
for (int i = 0; i < in.length(); i++) {
if (in.charAt(i) == ' ') { // 遇到空格,特殊处理
int cnt = 0, j = i;
while (j < in.length() && in.charAt(j) == ' ') {
j++;
cnt++;
}
if ((cnt & 1) == 0) { // 偶数情况添加空格
for (int k = 0; k < cnt; k++) {
bd.append(' ');
}
}
i = j - 1; // attention!
} else {
bd.append(in.charAt(i));
}
}
return bd.toString();
}
这段代码虽然逻辑上没有什么问题,但是总是有一个地方不是那么顺眼:第 15 行 i = j - 1
。可以看出,这一步操作是为了让 i 顺从 for 循环的逻辑,而生硬地增加了 -1 这个操作。这种方式对于代码阅读非常不友好:
- 编写易出错。实话说,我第一次写这个代码的时候写的就是
i = j
,最后是写了一个样例测试之后才发现了这个隐蔽的错误。 - 他人阅读时容易疑惑。当看到 for 循环中的 i++ 后,大部分人都会形成 i 每一轮都增加 1 这种预期。此时,内部突然加上对 i 的修改操作,就一下子打断了之前建立的预期,让思路非常不顺畅。
所以,如果需要手动修改循环变量的时候,请使用 while 循环;使用 for 循环,那么请保证循环变量只由 for 本身修改。
if else
条件判断分支数尽量不要太多,能合并的情况请合并起来。看一个最长公共子串的例子:
public int maxCommonSubString(String a, String b) {
int na = a.length(), nb = b.length(), res = 0;
int[][] dp = new int[na][nb];
for (int i = 0; i < na; i++) {
for (int j = 0; j < nb; j++) {
if (a.charAt(i) == b.charAt(j)) {
if (i > 0 && j > 0) {
dp[i][j] = dp[i-1][j-1] + 1;
} else if (i == 0) { // attention
dp[0][j] = 1;
} else if (j == 0) { // attention
dp[i][0} = 1;
}
} else {
dp[i][j] = 0;
}
res = Math.max(res, dp[i][j]);
}
}
return res;
}
这个是我最开始写的版本,仔细一看,其实 7~13 处的 if-else 逻辑是可以进一步简化的,因为 i 或者 j 无论哪个等于 0,都是 dp[i][j] = 0。
if (i > 0 && j > 0) {
dp[i][j] = dp[i-1][j-1] + 1;
} else {
dp[i][j] = 1;
}
总结
使用 for 循环时,不要在循环内部修改循环变量的值,否则请使用 while 循环;if-else 的分支尽量减少,简洁化代码。这些代码的细节,如果没有他人的指出还真挺难察觉的,慢慢积累吧。