从性能角度看 Java 8

一则或许对你有用的小广告

欢迎加入小哈的星球 ,你将获得:专属的项目实战 / Java 学习路线 / 一对一提问 / 学习打卡/ 赠书活动

目前,正在 星球 内带小伙伴们做第一个项目:全栈前后端分离博客项目,采用技术栈 Spring Boot + Mybatis Plus + Vue 3.x + Vite 4手把手,前端 + 后端全栈开发,从 0 到 1 讲解每个功能点开发步骤,1v1 答疑,陪伴式直到项目上线,目前已更新了 204 小节,累计 32w+ 字,讲解图:1416 张,还在持续爆肝中,后续还会上新更多项目,目标是将 Java 领域典型的项目都整上,如秒杀系统、在线商城、IM 即时通讯、权限管理等等,已有 870+ 小伙伴加入,欢迎点击围观

作为一名软件架构师,我更感兴趣的是速度、安全性、可扩展性等方面的新特性或改进。为此,我针对 Java 8 的一些特性做了一些测试。


要点:

  • 在遍历 List 时, for ( int i = 0; i < numbers .size(); i++) 比其他任何方法都快得多,无论是并行计算还是串行计算,尽管它看起来很丑。您可以结合 Predicate 减少代码脏度,并以牺牲一些速度为代价提高可重用性,但仍然比其他速度更快,尽管不是最快的。因此,您的选择取决于您对速度的关心程度。
  • 在遍历 Map 时,Java 8 更快,但不是很多。总的来说,Java 8 在编码工作和性能方面都胜出。
  • 默认函数执行没有区别。


1. 使用 Lambda 表达式遍历列表 VS 不使用


 import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.elasticsearch.common.netty.util.internal.ThreadLocalRandom;

public class TestLambdaList { final static List<Integer> numbers = new ArrayList<Integer>(); static int nThreads=4; static ExecutorService es=Executors.newFixedThreadPool(nThreads);

static void prepare() { ThreadLocalRandom tlr = ThreadLocalRandom.current(); int c = 10_000_000; while (--c >= 0) { numbers.add(tlr.nextInt(10_0000)); } }

static long countParallel8(){ long s = System.currentTimeMillis(); long t = numbers.parallelStream().filter(e -> (e & 1) == 1).count(); long dif = System.currentTimeMillis() - s; return dif; } static void countParallelOld(long difLambda){ long s = System.currentTimeMillis(); int step=numbers.size()/nThreads; List<Future<Long>> fs=new ArrayList<Future<Long>>();; for(int i=0;i<nThreads;i++){ int start=i*step; int end=(i+1)step; if(i==nThreads-1){ end=numbers.size(); } final int endEnclosed=end; Future<Long> f=es.submit(new Callable<Long>(){ public Long call() throws Exception { long t=0; for(int j=start;j<endEnclosed;j++){ if ((numbers.get(j) & 1) == 1) { t++; } } return t; } }); fs.add(f); } long t=0; for(int i=0;i<fs.size();i++){ try { t+=fs.get(i).get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } long dif = System.currentTimeMillis() - s; System.out.println(Math.round(100.0difLambda/dif)+"\tStream Parallel/(Old Parallel)\t"); } static long countOddInNovelWay() { long s = System.currentTimeMillis(); long t = numbers.stream().filter(e -> (e & 1) == 1).count(); long dif = System.currentTimeMillis() - s; return dif; }

static void countOddInOldWay(long difLambda) { long s = System.currentTimeMillis(); long t = 0; for (int i = 0; i < numbers.size(); i++) { if ((numbers.get(i) & 1) == 1) { t++; } } long dif=System.currentTimeMillis()-s; System.out.println(Math.round(100.0*difLambda/dif)+"\tLambda/(Looping like array)\t");

combine(difLambda,i->(i & 1)==1);

s = System.currentTimeMillis(); t=0; for (Iterator<Integer> it = numbers.iterator(); it.hasNext();) { if ((it.next() & 1) == 1) { t++; } } dif=System.currentTimeMillis()-s; System.out.println(Math.round(100.0*difLambda/dif)+"\tLambda/(Looping via Iterator)\t");

s = System.currentTimeMillis(); t=0; for (int n : numbers) { if ((n & 1) == 1) { t++; } } dif=System.currentTimeMillis()-s; System.out.println(Math.round(100.0*difLambda/dif)+"\tLambda/(Looping via for each syntax)\t");

s = System.currentTimeMillis(); t=0; for (ListIterator<Integer> iter = numbers.listIterator(); iter.hasNext(); ) { if ((iter.next() & 1) == 1) { t++; } } dif=System.currentTimeMillis()-s; System.out.println(Math.round(100.0difLambda/dif)+"\tLambda/(Looping via ListIterator)\t"); } static void combine(long difLambda, Predicate<Integer> p){ long s = System.currentTimeMillis(); long t = 0; for (int i = 0; i < numbers.size(); i++) { if (p.idOdd(i)) { t++; } } long dif=System.currentTimeMillis()-s; System.out.println(Math.round(100.0difLambda/dif)+"\tLambda/(Looping Array-Like Combined with Predicate)\t"); } static interface Predicate<Integer>{ boolean idOdd(Integer i); } public static void main(String[] args) { prepare(); for (int i = 0; i < 10_000; i++) { //long r=countOddInNovelWay(); //countOddInOldWay(r);

long r=countParallel8(); countParallelOld(r); } } }

Results: (The number is a speed ratio of new way to old way ) 298Lambda/(Looping like array) 189Lambda/(Looping Array-Like Combined with Predicate) 99Lambda/(Looping via Iterator) 90Lambda/(Looping via for each syntax) 100Lambda/(Looping via ListIterator) 198Lambda/(Looping like array) 160Lambda/(Looping Array-Like Combined with Predicate) 89Lambda/(Looping via Iterator) 100Lambda/(Looping via for each syntax) 83Lambda/(Looping via ListIterator) 198Lambda/(Looping like array) 160Lambda/(Looping Array-Like Combined with Predicate) 89Lambda/(Looping via Iterator) 80Lambda/(Looping via for each syntax) 92Lambda/(Looping via ListIterator)

488Stream Parallel/(Old Parallel) 252Stream Parallel/(Old Parallel) 488Stream Parallel/(Old Parallel) 252Stream Parallel/(Old Parallel) 232Stream Parallel/(Old Parallel) 488Stream Parallel/(Old Parallel) 488Stream Parallel/(Old Parallel) 291Stream Parallel/(Old Parallel) 279Stream Parallel/(Old Parallel) 463Stream Parallel/(Old Parallel) 252Stream Parallel/(Old Parallel) 321Stream Parallel/(Old Parallel)


2. 使用 Lambda 表达式遍历 Map VS 不使用

Map.forEach 和 Map.compute 比旧方法表现更好,但不是很好,但仍然很酷。


 import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.elasticsearch.common.netty.util.internal.ThreadLocalRandom;

public class TestLambdaList { final static List<Integer> numbers = new ArrayList<Integer>(); static int nThreads=4; static ExecutorService es=Executors.newFixedThreadPool(nThreads);

static void prepare() { ThreadLocalRandom tlr = ThreadLocalRandom.current(); int c = 10_000_000; while (--c >= 0) { numbers.add(tlr.nextInt(10_0000)); } }

static long countParallel8(){ long s = System.currentTimeMillis(); long t = numbers.parallelStream().filter(e -> (e & 1) == 1).count(); long dif = System.currentTimeMillis() - s; return dif; } static void countParallelOld(long difLambda){ long s = System.currentTimeMillis(); int step=numbers.size()/nThreads; List<Future<Long>> fs=new ArrayList<Future<Long>>();; for(int i=0;i<nThreads;i++){ int start=i*step; int end=(i+1)step; if(i==nThreads-1){ end=numbers.size(); } final int endEnclosed=end; Future<Long> f=es.submit(new Callable<Long>(){ public Long call() throws Exception { long t=0; for(int j=start;j<endEnclosed;j++){ if ((numbers.get(j) & 1) == 1) { t++; } } return t; } }); fs.add(f); } long t=0; for(int i=0;i<fs.size();i++){ try { t+=fs.get(i).get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } long dif = System.currentTimeMillis() - s; System.out.println(Math.round(100.0difLambda/dif)+"\tStream Parallel/(Old Parallel)\t"); } static long countOddInNovelWay() { long s = System.currentTimeMillis(); long t = numbers.stream().filter(e -> (e & 1) == 1).count(); long dif = System.currentTimeMillis() - s; return dif; }

static void countOddInOldWay(long difLambda) { long s = System.currentTimeMillis(); long t = 0; for (int i = 0; i < numbers.size(); i++) { if ((numbers.get(i) & 1) == 1) { t++; } } long dif=System.currentTimeMillis()-s; System.out.println(Math.round(100.0*difLambda/dif)+"\tLambda/(Looping like array)\t");

combine(difLambda,i->(i & 1)==1);

s = System.currentTimeMillis(); t=0; for (Iterator<Integer> it = numbers.iterator(); it.hasNext();) { if ((it.next() & 1) == 1) { t++; } } dif=System.currentTimeMillis()-s; System.out.println(Math.round(100.0*difLambda/dif)+"\tLambda/(Looping via Iterator)\t");

s = System.currentTimeMillis(); t=0; for (int n : numbers) { if ((n & 1) == 1) { t++; } } dif=System.currentTimeMillis()-s; System.out.println(Math.round(100.0*difLambda/dif)+"\tLambda/(Looping via for each syntax)\t");

s = System.currentTimeMillis(); t=0; for (ListIterator<Integer> iter = numbers.listIterator(); iter.hasNext(); ) { if ((iter.next() & 1) == 1) { t++; } } dif=System.currentTimeMillis()-s; System.out.println(Math.round(100.0difLambda/dif)+"\tLambda/(Looping via ListIterator)\t"); } static void combine(long difLambda, Predicate<Integer> p){ long s = System.currentTimeMillis(); long t = 0; for (int i = 0; i < numbers.size(); i++) { if (p.idOdd(i)) { t++; } } long dif=System.currentTimeMillis()-s; System.out.println(Math.round(100.0difLambda/dif)+"\tLambda/(Looping Array-Like Combined with Predicate)\t"); } static interface Predicate<Integer>{ boolean idOdd(Integer i); } public static void main(String[] args) { prepare(); for (int i = 0; i < 10_000; i++) { //long r=countOddInNovelWay(); //countOddInOldWay(r);

long r=countParallel8(); countParallelOld(r); } } }

Results: (The number is a speed ratio of new way to old way ) 298Lambda/(Looping like array) 189Lambda/(Looping Array-Like Combined with Predicate) 99Lambda/(Looping via Iterator) 90Lambda/(Looping via for each syntax) 100Lambda/(Looping via ListIterator) 198Lambda/(Looping like array) 160Lambda/(Looping Array-Like Combined with Predicate) 89Lambda/(Looping via Iterator) 100Lambda/(Looping via for each syntax) 83Lambda/(Looping via ListIterator) 198Lambda/(Looping like array) 160Lambda/(Looping Array-Like Combined with Predicate) 89Lambda/(Looping via Iterator) 80Lambda/(Looping via for each syntax) 92Lambda/(Looping via ListIterator)

488Stream Parallel/(Old Parallel) 252Stream Parallel/(Old Parallel) 488Stream Parallel/(Old Parallel) 252Stream Parallel/(Old Parallel) 232Stream Parallel/(Old Parallel) 488Stream Parallel/(Old Parallel) 488Stream Parallel/(Old Parallel) 291Stream Parallel/(Old Parallel) 279Stream Parallel/(Old Parallel) 463Stream Parallel/(Old Parallel) 252Stream Parallel/(Old Parallel) 321Stream Parallel/(Old Parallel)

3. 默认函数 VS 传统静态函数

完全没有显着差异,这很好。


 import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.ListIterator;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;

import org.elasticsearch.common.netty.util.internal.ThreadLocalRandom;

public class TestLambdaList { final static List<Integer> numbers = new ArrayList<Integer>(); static int nThreads=4; static ExecutorService es=Executors.newFixedThreadPool(nThreads);

static void prepare() { ThreadLocalRandom tlr = ThreadLocalRandom.current(); int c = 10_000_000; while (--c >= 0) { numbers.add(tlr.nextInt(10_0000)); } }

static long countParallel8(){ long s = System.currentTimeMillis(); long t = numbers.parallelStream().filter(e -> (e & 1) == 1).count(); long dif = System.currentTimeMillis() - s; return dif; } static void countParallelOld(long difLambda){ long s = System.currentTimeMillis(); int step=numbers.size()/nThreads; List<Future<Long>> fs=new ArrayList<Future<Long>>();; for(int i=0;i<nThreads;i++){ int start=i*step; int end=(i+1)step; if(i==nThreads-1){ end=numbers.size(); } final int endEnclosed=end; Future<Long> f=es.submit(new Callable<Long>(){ public Long call() throws Exception { long t=0; for(int j=start;j<endEnclosed;j++){ if ((numbers.get(j) & 1) == 1) { t++; } } return t; } }); fs.add(f); } long t=0; for(int i=0;i<fs.size();i++){ try { t+=fs.get(i).get(); } catch (InterruptedException e) { e.printStackTrace(); } catch (ExecutionException e) { e.printStackTrace(); } } long dif = System.currentTimeMillis() - s; System.out.println(Math.round(100.0difLambda/dif)+"\tStream Parallel/(Old Parallel)\t"); } static long countOddInNovelWay() { long s = System.currentTimeMillis(); long t = numbers.stream().filter(e -> (e & 1) == 1).count(); long dif = System.currentTimeMillis() - s; return dif; }

static void countOddInOldWay(long difLambda) { long s = System.currentTimeMillis(); long t = 0; for (int i = 0; i < numbers.size(); i++) { if ((numbers.get(i) & 1) == 1) { t++; } } long dif=System.currentTimeMillis()-s; System.out.println(Math.round(100.0*difLambda/dif)+"\tLambda/(Looping like array)\t");

combine(difLambda,i->(i & 1)==1);

s = System.currentTimeMillis(); t=0; for (Iterator<Integer> it = numbers.iterator(); it.hasNext();) { if ((it.next() & 1) == 1) { t++; } } dif=System.currentTimeMillis()-s; System.out.println(Math.round(100.0*difLambda/dif)+"\tLambda/(Looping via Iterator)\t");

s = System.currentTimeMillis(); t=0; for (int n : numbers) { if ((n & 1) == 1) { t++; } } dif=System.currentTimeMillis()-s; System.out.println(Math.round(100.0*difLambda/dif)+"\tLambda/(Looping via for each syntax)\t");

s = System.currentTimeMillis(); t=0; for (ListIterator<Integer> iter = numbers.listIterator(); iter.hasNext(); ) { if ((iter.next() & 1) == 1) { t++; } } dif=System.currentTimeMillis()-s; System.out.println(Math.round(100.0difLambda/dif)+"\tLambda/(Looping via ListIterator)\t"); } static void combine(long difLambda, Predicate<Integer> p){ long s = System.currentTimeMillis(); long t = 0; for (int i = 0; i < numbers.size(); i++) { if (p.idOdd(i)) { t++; } } long dif=System.currentTimeMillis()-s; System.out.println(Math.round(100.0difLambda/dif)+"\tLambda/(Looping Array-Like Combined with Predicate)\t"); } static interface Predicate<Integer>{ boolean idOdd(Integer i); } public static void main(String[] args) { prepare(); for (int i = 0; i < 10_000; i++) { //long r=countOddInNovelWay(); //countOddInOldWay(r);

long r=countParallel8(); countParallelOld(r); } } }

Results: (The number is a speed ratio of new way to old way ) 298Lambda/(Looping like array) 189Lambda/(Looping Array-Like Combined with Predicate) 99Lambda/(Looping via Iterator) 90Lambda/(Looping via for each syntax) 100Lambda/(Looping via ListIterator) 198Lambda/(Looping like array) 160Lambda/(Looping Array-Like Combined with Predicate) 89Lambda/(Looping via Iterator) 100Lambda/(Looping via for each syntax) 83Lambda/(Looping via ListIterator) 198Lambda/(Looping like array) 160Lambda/(Looping Array-Like Combined with Predicate) 89Lambda/(Looping via Iterator) 80Lambda/(Looping via for each syntax) 92Lambda/(Looping via ListIterator)

488Stream Parallel/(Old Parallel) 252Stream Parallel/(Old Parallel) 488Stream Parallel/(Old Parallel) 252Stream Parallel/(Old Parallel) 232Stream Parallel/(Old Parallel) 488Stream Parallel/(Old Parallel) 488Stream Parallel/(Old Parallel) 291Stream Parallel/(Old Parallel) 279Stream Parallel/(Old Parallel) 463Stream Parallel/(Old Parallel) 252Stream Parallel/(Old Parallel) 321Stream Parallel/(Old Parallel)