相信每个人都喜欢看简洁易懂的代码,代码可读性也是衡量代码质量的重要标准之一,而本文则通过十个具体的编程小技巧(部分技巧并不局限于Java
),希望能够帮助到你。
1. 使用三元表达式
考虑以下代码:
public boolean isOdd(int num) {
if (num % 2 == 1) {
return true;
} else {
return false;
}
}
我们可能会经常见到类似上面的代码,只需要一次简单的if ... else ...
判断,而判断所得到的内容也很简单,在这种时候,我们就可以使用三元表达式来简化我们的代码,就像下面这样:
public boolean isOdd(int num) {
return num % 2 == 1 ? true : false;
}
可以发现,通过使用... ? ... : ...
三元表达式,我们能够写出更加简单的代码,当然这个代码仍然不够简洁,在接下来第二点会进行讨论,这里先讨论三元表示式。虽然三元表示式可以简化我们的代码,在很多时候也能够简化我们的代码,但是当判断的情况过多并且语句较长的时候,我们就不应该使用三元表达式了,就像下面这种情况,我们就不应该采用三元表达式:
public int getMaxDays(int year, int month) {
// 当条件过多时, 使用三元表示就无法体现发挥简洁的优势了,我们应该考虑其它的方法
return month == 2 ? (isLeapYear(year) ? 29 : 28) :
(month == 4 || month == 6 || month == 9 || month == 11) ? 30 : 31;
}
private boolean isLeapYear(int year) {
return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
}
2. 简化布尔条件表达式
再次考虑1中的代码:
public boolean isOdd(int num) {
return num % 2 == 1 ? true : false;
}
由于我们返回的结果是布尔值,所以在这种情况下,我们就可以直接返回布尔表达式即可,而不需要再进行条件判断,让代码不够简洁:
public boolean isOdd(int num) {
return num % 2 == 1;
}
3. 使用卫语句
在1中我们以一个反例讲诉了三元表示不用改被乱用,这里我们就先通过if
语句,来进行改写,写出第一版代码:
public int getMaxDays(int year, int month) {
int result;
if (month == 2) {
result = isLeapYear(year) ? 29 : 28;
} else if (month == 4 || month == 6 || month == 9 || month == 11) {
result = 30;
} else {
result = 31;
}
return result;
}
private boolean isLeapYear(int year) {
return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0) ;
}
我们为了实现单入口单出口
原则,而让多次判断都有依赖,如果每个判断条件内存在大量的语句的时候,更会显示出这种编写方式的弊端,因此对于这种情况,我们通常会编写单入口多出口
的代码,让简便情况尽快得到判断,每个判断语句之间不必耦合:
public int getMaxDays(int year, int month) {
if (month == 2) {
return isLeapYear(year) ? 29 : 28;
}
if (month == 4 || month == 6 || month == 9 || month == 11) {
return 30;
}
return 31;
}
private boolean isLeapYear(int year) {
return ((year % 4 == 0 && year % 100 != 0) || year % 400 == 0);
}
4. 使用arraycopy
拷贝数组
我们有时可能因为不能在原数组上进行修改,需要对原数组进行一份拷贝,然后写出类似下面这样的代码:
int[] arr = { 1, 2, 3, 4, 5};
int[] temp = new int[arr.length];
for (int i = 0; i < arr.length; i++) {
temp[i] = arr[i];
}
不过我们可以通过使用系统自带的arraycopy
函数,让代码更易读,效率也更高:
int[] arr = { 1, 2, 3, 4, 5};
int[] temp = new int[arr.length];
System.arraycopy(arr, 0, temp, 0, arr.length);
5. 使用try ... with ...
进行资源管理
当使用io
流时,我们由于需要对资源进行管理,需要写出类似下面的代码,当多个资源需要管理的时候,不仅难以管理,我们还可能经常会忘记对资源的关闭:
FileInputStream fis = null;
try {
fis = new FileInputStream(new File(""));
fis.read();
} catch (Exception e) {
e.printStackTrace();
} finally {
try {
fis.close();
} catch (IOException e) {
e.printStackTrace();
}
}
但是通过使用try ... with ...
就可以写出下面这样简便的代码,我们不需要再担心资源的管理问题:
try (FileInputStream fis = new FileInputStream(new File(""))) {
fis.read();
} catch (Exception e) {
e.printStackTrace();
}
6. 位运算小技巧
相信很多人都知道,位运算在很多情况下都能表现出更好地性能,在某些情况下也能够简化代码,这里就讲两个小例子:
-
使用位移代替乘除法:
a << n // <=> a * 2^n a >> n // <=> a / 2^n
-
使用位运算避免二分法中的加法溢出:
相信很多人都知道在二分查找中,以下代码可能因为加法运算的溢出,导致无法得到正确的结果:
int mid = (left + right) / 2;
如果使用下面这种方法就可以避免:
int mid = left + (right - left) / 2;
但是通过使用无符号位移,我们就可以写出下面这样简洁的代码,而且也可以避免溢出:
int mid = (left + right) >>> 1;
-
判断两号是否同号
如果不使用位运算,我们需要写出下面这样的代码:
(a >= 0 && b >= 0) || (a < 0 && b < 0)
但是如果使用
^
的特性,我们就可以写出下面这样简洁的代码:(a ^ b) >= 0
7. 分割字符串
很多时候,我们为了进行打印调试,都需要对数组的数据或者字符串进行分割打印,例如将[1, 2, 3]
或者"123"
打印为1, 2, 3
的格式,按照传统的方式,我们需要写出下面这样的代码:
int[] arr = { 1, 2, 3};
StringBuilder sb = new StringBuilder(String.valueOf(arr[0]));
for (int i = 1; i < arr.length; i++) {
sb.append(", ").append(arr[i]);
}
// output: 1, 2, 3
System.out.println(sb);
String str = "123";
String[] split = Pattern.compile("").split(str);
StringBuilder sb = new StringBuilder(split[0]);
for (int i = 1; i < split.length; i++) {
sb.append(", ").append(split[i]);
}
// output: 1, 2, 3
System.out.println(sb);
但是通过使用Stream
的特性,我们就能写出下面这样更加方便易读的代码(为了代码的可读性,我们通常会将链式调用拆成多行):
int[] arr = { 1, 2, 3};
// output: 1, 2, 3
System.out.println(
IntStream.of(arr)
.mapToObj(String::valueOf)
.collect(Collectors.joining(", "))
);
String str = "123";
// output: 1, 2, 3
System.out.println(
Pattern.compile("")
.splitAsStream(str)
.collect(Collectors.joining(", "))
);
8. 使用系统方法打印数组
我们通常需要打印数组进行调试,正如7中所说的那样,但是我们通常只要能够得出数组的数据即可,而不在乎其形式,但是如果直接打印数组,只会得到一个内存地址,不过我们其实可以通过调用Arrays.toString(arr)
,很容易就实现我们的需求:
int[] arr = { 1, 2, 3};
// output: [1, 2, 3]
System.out.println(Arrays.toString(arr));
9. Stream
实现计数器
有时,我们需要对数组或者集合中的数据进行统计其次数,我们会写出如下所示的代码:
int[] arr = { 1, 2, 3, 4, 5, 6};
Map<Integer, Integer> map = new HashMap<>();
for (int val : arr) {
map.put(val, map.getOrDefault(val, 0) + 1);
}
如果利用Stream
,我们就能够更加专注于我们的业务逻辑,也可加易读:
int[] arr = { 1, 2, 3, 4, 5, 6};
Map<Integer, Integer> map = IntStream.of(arr)
.boxed()
.collect(Collectors.toMap(k -> k, k -> 1, Integer::sum));
10. 使用Arrays.asList(arr)
将对象数组转换为集合
有时我们需要为了将数组转换为集合,然后写出类似下面的代码:
String[] strs = new String[10];
List<String> list = new ArrayList<>();
for (String str : strs) {
list.add(str);
}
但是利用Array.asList(arr)
就可以写出下面这样简洁的代码:
String[] strs = new String[10];
// Array.asList(arr)生成的结果集合无法进行数据的修改,因此需要使用 new ArrayList<>();
List<String> list = new ArrayList<>(Arrays.asList(strs));
或者使用Stream
这样进行转换:
String[] strs = new String[10];
List<String> list = Stream.of(strs).collect(Collectors.toList());
以上就是关于Java
的一些编程小技巧,希望能够对你有些帮助。