幾個小細節幫你提高java代碼運行效率

引言

千萬不要小看代碼細節的優化,有時候一個很小的優化就要你的代碼執行效率數倍提高,若是這個優化點調用比較頻繁,甚至有可能解決你整個系統的性能瓶頸。html

orElse和orElseGet

官方文檔上是這麼說的,java

  • orElse:Return the value if present, otherwise return other.
  • orElseGet:Return the value if present, otherwise invoke other and return the result of that invocation.

描述可能沒這麼直觀,來個例子你就明白了。數據庫

public class App {

    public static void main(String[] args) {
        String input = "input";
        String result = Optional.ofNullable(input).orElse(defaultProcess());
        System.out.println(result);

    }

    public static String defaultProcess() {
        System.out.println("defalut process");
        return "default";
    }

}

運行結果:api

defalut process
input

而後你猜下下面這段代碼的運行結果是啥,數組

public class App {

    public static void main(String[] args) {
        String input = "input";
        String result = Optional.ofNullable(input).orElseGet(() -> defaultProcess());
        System.out.println(result);

    }

    public static String defaultProcess() {
        System.out.println("defalut process");
        return "default";
    }

}

結果是:安全

input

到這裏你應該已經明白了,orElse裏的邏輯在任什麼時候候都會執行,即便optional不爲空。而orElseGet只在optional是空的時候纔會執行。多線程

能夠想象,若是在實際項目中defaultProcess裏的邏輯很耗時,使用後者對性能的提示仍是很明顯的。oracle

循環中減小重複計算

好比把下面這種循環,app

for (int i = 0; i < list.size(); i++)
{...}

改爲以下這種:性能

for (int i = 0, length = list.size(); i < length; i++)
{...}

簡單的size計算可能對性能影響不大,可是若是循環中的方法計算是相似從數據庫count等耗時類的操做,有可能就成爲系統的性能瓶頸。

集合數組類的對象初始化指定初始長度。

若是咱們能估計大概的內容長度,集合類的實例在建立時最好分配一個初始空間。能夠明顯的提高性能。這裏拿StringBuilder舉個例子,若是咱們使用默認的構建器,會初始分配16字符空間,以下:

/**
     * Constructs a string builder with no characters in it and an
     * initial capacity of 16 characters.
     */
    public StringBuilder() {
        super(16);
    }

append操做的時候,若是發現到達了最大容量,它會將自身容量增長到當前的2倍再加2,而後從舊的空間拷貝數據到新的空間。源碼以下:

/**
     * This implements the expansion semantics of ensureCapacity with no
     * size check or synchronization.
     */
    void expandCapacity(int minimumCapacity) {
        int newCapacity = value.length * 2 + 2;
        if (newCapacity - minimumCapacity < 0)
            newCapacity = minimumCapacity;
        if (newCapacity < 0) {
            if (minimumCapacity < 0) // overflow
                throw new OutOfMemoryError();
            newCapacity = Integer.MAX_VALUE;
        }
        value = Arrays.copyOf(value, newCapacity);
    }

這是一個耗時的動做,並且有時候會浪費空間。試想一下,若是你知道業務場景是大概須要1000個字符。若是沒有指定初始值,StringBuilderappend過程當中要屢次分配空間,拷貝數據。並且在接近1000的時候若是再次分配也是直接翻倍的增長空間,就形成了空間的浪費。

使用並行流

這裏說的是streamparallelStream的區別。

parallelStream並行流就是一個把內容分紅多個數據塊,並用不不一樣的線程分別處理每一個數據塊的流。最後合併每一個數據塊的計算結果。處理的線程數就是機器的處理器核心數。

從原理上講,大部分場景下並行流處理是更快,來看個例子:

@Test
    public void streamCostTest(){
        List<Integer> intList = mockData();
        useStream(intList);
        useParallelStream(intList);
    }

    /**
     * 構造數據
     *
     * @return
     */
    public List<Integer> mockData() {

        List<Integer> intList = new ArrayList<Integer>();
        for (int i = 0; i < 1000000; i++) {
            intList.add(i);
        }
        return intList;
    }



    public void useStream(List<Integer> integerList) {
        long start = System.currentTimeMillis();

        long count = integerList.stream().filter(x -> (x%2==0)).count();
        System.out.println(count);

        long end = System.currentTimeMillis();
        System.out.println("useStream cost:" + (end - start));
    }


    public void useParallelStream(List<Integer> integerList) {

        long start = System.currentTimeMillis();

        long count = integerList.parallelStream().filter(x -> (x%2==0)).count();
        System.out.println(count);

        long end = System.currentTimeMillis();

        System.out.println("useParallelStream cost:" + (end - start));
    }

測試結果:

500000
useStream cost:42
500000
useParallelStream cost:13

注意上面我提到了大部分場景下。也就是說並行流並非大殺器,一勞永逸的解決方案。有些地方使用並行流反而性能更差,這裏只給一個建議,就是必定要本身測試,測試性能,測試多線程安全等。


參考:

https://docs.oracle.com/javas...

相關文章
相關標籤/搜索