任何傻瓜都能写出计算机可以理解的代码, 但只有优秀的程序眼才能写出人类可以理解的代码 — Martin Fowler
但是总有渴望编写高性能代码的程序员存在吧,让我们来看看如何编写运行更快的Java代码吧!
注意:JVM对代码进行了有效的优化。因此,您不需要针对一般用例对其进行优化。但是,如果您想让JVM发挥最大性能。我们开始吧。
所有测试用例都在Macbook Pro 2015的Java12 HotSpot(TM) 64-Bit Server VM上运行
1 、在构造函数中实例化
如果你的Collections
只初始化一次,则最好集合构造器中对它的值进行初始化,而不是实例化集合后使用addall
或add
方法设置值.
// Slower 🚶♂️
Set<String> set = new HashSet<>();
set.addAll(Arrays.asList("one", "two", "three"));
// Faster 🚀
Set<String> set = new HashSet<>(Arrays.asList("one", "two", "three"));
让我们使用JMH基准测试来验证一下
结果的单位是每秒操作的次数(op/s),数值越大,性能越高
@State(Scope.Thread)
public static class MyState {
@Setup(Level.Trial)
public void doSetup() {
var arr = new Integer[100000];
for (var i = 0; i < 100000; i++) {
arr[i] = i;
}
list = Arrays.asList(arr);
}
public List list;
}
// Faster 🚀 > ~148,344 op/s
@Benchmark
public HashSet usingConstructor() {
var set = new HashSet<>(list);
return set;
}
// Slower 🚶♂️ > ~112,061 op/s
@Benchmark
public HashSet usingAddAll() {
var set = new HashSet<>();
set.addAll(list);
return set;
}
construtor
版本比addall
版本提供~36000 op/s
2、addAll
比add
更快
同样的,addAll
比add
每秒提供更高的操作次数,所以下次当你向数组中添加的时,一定要先把它们收集起来,然后用addAll
添加.
// Slower 🚶♂️ ~116116op/s
@Benchmark
public ArrayList<Integer> usingAdd() {
var a = new int[1000];
for (var i = 0; i < 1000; i++) {
a[i] = i;
}
var arr = new ArrayList<Integer>();
for (var i = 0; i < 1000; i++) {
arr.add(a[i]);
}
return arr;
}
// Faster 🚀 ~299130 op/s
@Benchmark
public ArrayList<Integer> usingAddAll() {
var a = new Integer[1000];
for (var i = 0; i < 1000; i++) {
a[i] = i;
}
var arr = new ArrayList<Integer>();
arr.addAll(Arrays.asList(a));
return arr;
}
addall的速度几乎是add版本的两倍。
3、遍历Map使用EntrySet
,不要再使用KeySet
// Slower 🚶♂️ ~37000 op/s
@Benchmark
public HashMap<Integer, Integer> keySetIteration(Blackhole blackhole) {
var someMap = new HashMap<Integer, Integer>();
for (var i = 0; i < 1000; i++) {
someMap.put(i, i);
}
var sum = 0;
for(Integer i: someMap.keySet()) {
sum += i;
sum += someMap.get(i);
}
blackhole.consume(sum);
return someMap;
}
// Faster 🚀 ~45000 op/s
@Benchmark
public HashMap<Integer, Integer> entrySetIteration(Blackhole blackhole) {
var someMap = new HashMap<Integer, Integer>();
for (var i = 0; i < 1000; i++) {
someMap.put(i, i);
}
var sum = 0;
for(Map.Entry<Integer, Integer> e: someMap.entrySet()) {
sum += e.getKey();
sum += e.getValue();
}
blackhole.consume(sum);
return someMap;
}
EntrySet
在一秒钟内可以运行9000个操作,远超它的变种KeySet
4、使用SingleList
代替只有单个元素的数组
// Faster 🚀
var list = Collections.singletonList("S");
// Slower 🚶♂️
var list = new ArrayList(Arrays.asList("S"));
5、使用EnumSet
代替Hashset
, EnumSet
更快
// Faster 🚀
public enum Color {
RED, YELLOW, GREEN
}
var colors = EnumSet.allOf(Color.class);
// Slower 🚶♂️
var colors = new HashSet<>(Arrays.asList(Color.values()));
关于更多EnumSet
看这里
6、不要随意的初始化对象,尽量重复使用
// Faster 🚀
var i = 0 ;
i += addSomeNumber();
i -= minusSomeNumber();
return i;
// Slower 🚶♂️
var i = 0 ;
var j = addSomeNumber();
var k = minusSomeNumber();
var l = i + j - k;
return l;
7、使用String.isEmpty()
方法来检查字符串是否为空
因为String
是一个byte[]
,isEmpty
方法只是检查数组的长度,所以更快
public boolean isEmpty() {
return value.length == 0;
}
8、如果使用只有一个字符的字符串,请用单引号代替双引号
// Faster 🚀
var r = 'R' ;
var g = 'G' ;
var b = 'B' ;
// Slower 🚶♂️
var r = "R" ;
var g = "G" ;
var b = "B" ;
9、尽可能使用StringBuilder
// Faster 🚀
StringBuilder str = new StringBuilder();
str.append("A");
str.append("B");
str.append("C");
str.append("D");
str.append("E");
....
// Slower 🚶♂️
var str = "";
str += "A";
str += "B";
str += "C";
str += "D";
str += "E";
....
特别当你需要连接字符串的时候,使用StringBuilder
比+
更快
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。