9個小技巧提高你的Java性能

任何傻瓜都能寫出計算機能夠理解的代碼, 但只有優秀的程序眼才能寫出人類能夠理解的代碼 — Martin Fowler

可是總有渴望編寫高性能代碼的程序員存在吧,讓咱們來看看如何編寫運行更快的Java代碼吧!java

注意:JVM對代碼進行了有效的優化。所以,您不須要針對通常用例對其進行優化。可是,若是您想讓JVM發揮最大性能。咱們開始吧。
全部測試用例都在Macbook Pro 2015的Java12 HotSpot(TM) 64-Bit Server VM上運行

1 、在構造函數中實例化

若是你的Collections只初始化一次,則最好集合構造器中對它的值進行初始化,而不是實例化集合後使用addalladd方法設置值.程序員

// 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

二、addAlladd更快

一樣的,addAlladd每秒提供更高的操做次數,因此下次當你向數組中添加的時,必定要先把它們收集起來,而後用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版本的兩倍。app

三、遍歷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函數

四、使用SingleList代替只有單個元素的數組

// Faster 🚀
var list = Collections.singletonList("S"); 

// Slower 🚶‍♂️
var list = new ArrayList(Arrays.asList("S"));

五、使用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這裏性能

六、不要隨意的初始化對象,儘可能重複使用

// 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;

七、使用String.isEmpty()方法來檢查字符串是否爲空

由於String是一個byte[]isEmpty方法只是檢查數組的長度,因此更快測試

public boolean isEmpty() {
    return value.length == 0;
}

八、若是使用只有一個字符的字符串,請用單引號代替雙引號

// Faster 🚀
 var r = 'R' ;
 var g = 'G' ;
 var b = 'B' ;

 // Slower 🚶‍♂️
 var r = "R" ;
 var g = "G" ;
 var b = "B" ;

九、儘量使用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+更快
相關文章
相關標籤/搜索