千萬不要小看代碼細節的優化,有時候一個很小的優化就要你的代碼執行效率數倍提高,若是這個優化點調用比較頻繁,甚至有可能解決你整個系統的性能瓶頸。html
官方文檔上是這麼說的,java
描述可能沒這麼直觀,來個例子你就明白了。數據庫
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個字符。若是沒有指定初始值,StringBuilder
在append
過程當中要屢次分配空間,拷貝數據。並且在接近1000的時候若是再次分配也是直接翻倍的增長空間,就形成了空間的浪費。
這裏說的是stream
和parallelStream
的區別。
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
注意上面我提到了大部分場景下。也就是說並行流並非大殺器,一勞永逸的解決方案。有些地方使用並行流反而性能更差,這裏只給一個建議,就是必定要本身測試,測試性能,測試多線程安全等。
參考: