Java8 Stream性能如何及評測工具推薦


作為技術人員,學習新知識是基本功課。有些知識是不得不學,有些知識是學了之后如虎添翼,Java8的Stream就是兼具兩者的知識。不學看不懂,學了寫起代碼來如虎添翼。

在上篇《Java8 Stream新特性詳解及實戰》中我們介紹了Java8 Stream的基本使用方法,嘗試一下是不是感覺很爽?當只用一行代碼就搞定最終結果時,是不是再也不想用for循環一遍遍去迭代了。

同時,你是否又看到類似《Java8 Lambda表達式和流操作如何讓你的代碼變慢5倍》這樣的文章,那么今天就帶大家通過編寫測試程序來一探究竟,看看Stream的性能到底如何。同時,帶大家認識一個非常不錯的性能測試工具junitperf。

測試環境

先同步一下測試環境及工具信息:

  • JDK版本:1.8.0_151。
  • 電腦配置:MacBook Pro i7,16G內存。
  • Java測試工具:junitperf及Junit。
  • IDE:intellij IDEA。

在測試的過程中電腦中還開了其他很多應用,但基本上都沒進行操作。

實驗一:基本類型迭代

基本測試方案,先初始化一個int數組,5億個隨機數。然后從這個數組中找到最小的一個數。

采用三個單元測試方法來對照參考:

  • testIntFor:測試for循環執行時間;
  • testIntStream:測試串行Stream執行時間;
  • testIntParallelStream:測試並行Stream執行時間;

測試程序相關代碼:

public class StreamTest {

	public static int[] arr;

	@BeforeAll
	public static void init() {
		arr = new int[500000000];
		randomInt(arr);
	}

	@JunitPerfConfig(duration = 10000, warmUp = 1000, reporter = {HtmlReporter.class})
	public void testIntFor() {
		minIntFor(arr);
	}

	@JunitPerfConfig(duration = 10000, warmUp = 1000, reporter = {HtmlReporter.class})
	public void testIntParallelStream() {
		minIntParallelStream(arr);
	}

	@JunitPerfConfig(duration = 10000, warmUp = 1000, reporter = {HtmlReporter.class})
	public void testIntStream() {
		minIntStream(arr);
	}

	private int minIntStream(int[] arr) {
		return Arrays.stream(arr).min().getAsInt();
	}

	private int minIntParallelStream(int[] arr) {
		return Arrays.stream(arr).parallel().min().getAsInt();
	}

	private int minIntFor(int[] arr) {
		int min = Integer.MAX_VALUE;
		for (int anArr : arr) {
			if (anArr < min) {
				min = anArr;
			}
		}
		return min;
	}

	private static void randomInt(int[] arr) {
		Random r = new Random();
		for (int i = 0; i < arr.length; i++) {
			arr[i] = r.nextInt();
		}
	}
}

基本操作流程:通過@BeforeAll注解的init方法對數組進行隨機初始化,然后再統一執行上面三個測試方法。

在單元測試的方法上都有下面的注解:

@JunitPerfConfig(duration = 10000, warmUp = 1000, reporter = {HtmlReporter.class})

該注釋為junitperf提供的注解,其中duration為持續執行這段代碼的時間,單位毫秒;warmUp預熱時間,這里預熱1秒;reporter輸出報表格式,這里采用HTML展示,可以更直觀看到效果。

好上面的一切都准備好了,剩下的就是統一執行單元測試。執行結果如下三個圖。

image

image

image

針對基礎類型(int)操作,結果分析:

  • 串行Stream的執行的確不如for循環性能高,耗時大概是for循環的2倍。
  • 並行Stream的執行性能要優於for循環,耗時大概是for循環的一半。
  • 這里沒有用不同核數的機器測試,但並行Stream隨着服務器核數的增加,必然更快。

實驗二:對象迭代

生成一個List列表,列表中隨機生成10000000個字符串,然后分別通過不同的方式計算獲得最小的字符串。

基本操作與實驗一相同,不再貼出代碼,直接看測試的效果圖。

image

image

image

針對對象(String)操作,結果分析:

  • Stream的性能與for循環已經相差不大了,耗時大概是for循環的1.25倍左右。
  • 並行Stream執行的性能要優於for循環,而且比基礎類型的優勢更高,耗時已經低於for循環的一半。
  • 針對不同服務器核數,Stream效率同樣會更加高。

實驗三:復雜對象歸約

生成一個List列表,列表里面存放着1百萬個User對象。每個對象中都包含用戶名和用戶某次運動的距離,同一用戶可在List里包含多條運動記錄。現在通過不同的方式來統計用戶的總共運動了多遠距離。

基本測試思路一致,這里只貼出基於Stream的算法的代碼,以便大家了解Stream的復雜對象歸約如何使用。

// 串行寫法
users.stream().collect(
				Collectors.groupingBy(User::getUserName,
						Collectors.summingDouble(User::getMeters)));
// 並行寫法						
users.parallelStream().collect(
				Collectors.groupingBy(User::getUserName,
						Collectors.summingDouble(User::getMeters)));

下面看測試結果的數據:

image

image

image

復雜對象歸約操作,結果分析:

  • 基於Stream的操作明顯都高於for循環的效率,而且並行的效果更加明顯。
  • 同樣,隨着服務器核數的增加,並行Stream的效率會更高。

最后推薦一下這款用起來還不錯的Java性能測試工具,GitHub地址:https://github.com/houbb/junitperf。 上面有詳細的使用說明。唯一缺少的就是數據預初始化的示例,而本篇文章的示例中已經補上了這部分缺失。

小結

通過上面的幾組實驗對比,我們可以看到如下結論:

  • 針對簡單的操作,比如基礎類型的遍歷,使用for循環性能要明顯高於串行Stream操作。但Stream的並行操作隨着服務器的核數增加,會優於for循環。
  • 針對復雜操作,串行Stream性能與for循環不差上下,但並行Stream的性能已經是無法匹敵了。
  • 特別是針對一個集合進行多層過濾並歸約操作,無論從寫法上或性能上都要明顯優於for循環。

用一句話來說就是:簡單操作for循環即可,復雜操作首推Stream。

現在的Stream書寫簡單,性能不錯,如果未來JDK針對其進行優化,便同時享受了便捷和性能,何樂而不為呢。

原文鏈接《Java8 Stream性能如何及評測工具推薦


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM