java8 lambda python_【学习笔记】java8 Lambda表达式语法及应用

本文是慕课网大牧莫邪老师的视频教程一课掌握Lambda表达式语法及应用的学习笔记。如果觉得内容对你有用,可以购买老师的课程支持一下,课程价格1元,十分良心了。

1. 课程介绍

2. 为什么引入Lambda表达式

2.1 什么是Lambda表达式

Lambda表达式也称箭头函数、匿名函数、闭包

Lambda表达式体现的是轻量级函数式编程思想

-> 符号是Lambda表达式核心操作符号,符号左侧是操作参数,符号右侧是操作表达式

Lambda表达式是 jdk1.8 提供的新特性

2.2 Model Code as Data(MCAD模式)

Model Code as Data,编码即数据,尽可能轻量级的将代码封装为数据

解决方案:接口&实现类(匿名内部类)

存在问题:语法冗余、thIs关键字、变量捕获、数据控制等

传统模式下,新线程的创建:

new Thread(new Runnable() {

@Override

public void run() {

System.out.println("threading ..." + Thread.currentThread().getName());

}

}).start();

使用jdk8新特性,lambda表达式优化线程模型

new Thread(() -> {

System.out.println("threading ..." + Thread.currentThread().getName());

}).start();

2.3 项目问题:功能接口的设计及优化

需求环境:线程类的创建

解决方案:匿名内部类的实现

解决方法:lambda表达式实现

2.4 为什么要使用lambda表达式

它不是解决未知问题的新技术

对现有解决方案的语义化优化

需要根据实际需求考虑性能问题

3. Lambda表达式的基础知识

3.1 函数式接口概述和定义

函数式接口,就是java类型系统中的接口

函数式接口,是只包含一个接口方法的特殊接口

语义化检测注解:@FunctionalInterface

可以像定义普通接口一样定义函数式接口,并且接口内只有一个抽象方法:

@FunctionalInterface

public interface IUserCredential {

/**

* 通过用户账号,验证用户身份信息的接口

* @param username 要验证的用户账号

* @return 返回身份信息[系统管理员、用户管理员、普通用户]

*/

String verifyUser(String username);

// 添加这个方法后,会报错

// boolean test();

由于接口添加了@FunctionalInterface注解,表明是一个函数式接口,内部只能有一个抽象方法,如果再添加一个抽象方法boolean test(),就会报错:Multiple non-overriding abstract methods found in interface com.imooc.IUserCredential

注意:@FunctionalInterface 注解也可以不加,函数式接口只需要满足以下两个条件即可:

定义一个接口

接口中只有一个抽象方法

3.2 默认方法和静态方法

1. 接口的默认方法:default关键字修饰

@FunctionalInterface

public interface IUserCredential {

/**

* 通过用户账号,验证用户身份信息的接口

* @param username 要验证的用户账号

* @return 返回身份信息[系统管理员、用户管理员、普通用户]

*/

String verifyUser(String username);

/**

* 接口的默认方法

*/

default String getCredential(String username) {

// 模拟方法

if ("admin".equals(username)) {

return "admin + 系统管理员用户";

} else if("manager".equals(username)){

return "manager + 用户管理员用户";

} else {

return "commons + 普通会员用户";

}

}

}

接口的默认方法使用default关键字修饰,调用时,需要用接口的实例调用:

IUserCredential instance = new xxx(); // 实例化接口中的实现类

instance.getCredential("admin");

2. 接口的静态方法:static关键字修饰

@FunctionalInterface

public interface IUserCredential {

/**

* 通过用户账号,验证用户身份信息的接口

* @param username 要验证的用户账号

* @return 返回身份信息[系统管理员、用户管理员、普通用户]

*/

String verifyUser(String username);

/**

* 接口的静态方法

*/

static String getCredential(String username) {

// 模拟方法

if ("admin".equals(username)) {

return "admin + 系统管理员用户";

} else if("manager".equals(username)){

return "manager + 用户管理员用户";

} else {

return "commons + 普通会员用户";

}

}

}

同普通类的静态方法一样,接口的静态方法在调用时,直接调用即可,不需要实例化接口实例:

// 直接使用 接口名.方法名 调用

IUserCredential.getCredential("admin");

3. 来自Object继承的方法

由于接口或类都是Object的子类,如果我们在接口中增加一个由Object类继承过来的抽象方法,接口依然不会报错:

@FunctionalInterface

public interface IUserCredential {

/**

* 通过用户账号,验证用户身份信息的接口

* @param username 要验证的用户账号

* @return 返回身份信息[系统管理员、用户管理员、普通用户]

*/

String verifyUser(String username);

/**

* 这里的toString()方法是Object类继承的,添加后并不会报错

* @return

*/

@Override

String toString();

}

以上接口虽然有两个抽象类,但由于toString()方法是从Object类继承的,因此并不会报错,该接口依然是一个函数式接口。

3.3 Lambda表达式和函数式接口的关系

在jdk1.8之前,我们使用匿名内部类,实现接口的抽象方法:

IUserCredential ic2 = new IUserCredential() {

@Override

public String verifyUser(String username) {

return "admin".equals(username)?"管理员":"会员";

}

};

在jdk1.8,使用lambda表达式,针对函数式接口的简单实现

IUserCredential ic3 = (String username) -> {

return "admin".equals(username) ? "lbd管理员" : "lbd会员";

};

lambda表达式 是 函数式接口的一种实现.

3.4 jdk中常见的函数式接口

在java.util.function提供了大量的函数式接口:

Predicate 接收参数T对象,返回一个boolean类型结果

@FunctionalInterface

public interface Predicate {

boolean test(T t);

// 省略静态方法和默认方法

}

Consumer 接收参数T对象,没有返回值

@FunctionalInterface

public interface Consumer {

void accept(T t);

// 省略静态方法和默认方法

}

Function 接收参数T对象,返回R对象

@FunctionalInterface

public interface Function {

R apply(T t);

// 省略静态方法和默认方法

}

Supplier 不接受任何参数,直接通过get()获取指定类型的对象

@FunctionalInterface

public interface Supplier {

T get();

}

UnaryOperator 接口参数T对象,执行业务处理后,返回更新后的T对象

@FunctionalInterface

public interface UnaryOperator extends Function {

static UnaryOperator identity() {

return t -> t;

}

}

BinaryOperator 接口接收两个T对象,执行业务处理后,返回一个T对象

public interface BiFunction {

R apply(T t, U u);

// 省略静态方法和默认方法

}

@FunctionalInterface

public interface BinaryOperator extends BiFunction {

// 省略静态方法和默认方法

}

示例:

// 1. Predicate 接收参数T对象,返回一个boolean类型结果

Predicate pre = (String username) -> {

return "admin".equals(username);

};

System.out.println(pre.test("manager"));

// 2. Consumer 接收参数T对象,没有返回值

Consumer con = (String message) -> {

System.out.println("要发送的消息:" + message);

System.out.println("消息发送完成");

};

con.accept("hello 慕课网的学员们..");

// 3. Function 接收参数T对象,返回R对象

Function fun = (String gender) -> {

return "male".equals(gender) ? 1 : 0;

};

System.out.println(fun.apply("male"));

// 4. Supplier 不接受任何参数,直接通过get()获取指定类型的对象

Supplier sup = () -> {

return UUID.randomUUID().toString();

};

System.out.println(sup.get());

// 5. UnaryOperator 接口参数T对象,执行业务处理后,返回更新后的T对象

UnaryOperator uo = (String img)-> {

img += "[100x200]";

return img;

};

System.out.println(uo.apply("原图--"));

// 6. BinaryOperator 接口接收两个T对象,执行业务处理后,返回一个T对象

BinaryOperator bo = (Integer i1, Integer i2) -> {

return i1 > i2? i1: i2;

};

System.out.println(bo.apply(12, 13));

3.5 Lambda表达式基本语法

声明:就是和lambda表达式绑定的接口类型

参数:包含在一对圆括号中,和绑定的接口中的抽象方法中的参数个数及顺序一致。

操作符:->

执行代码块:包含在一对大括号中,出现在操作符号的右侧

示例如下:

首先定义3个接口:

// 1. 没有参数,没有返回值的lambda表达式绑定的接口

interface ILambda1{

void test();

}

// 2. 带有参数,没有返回值的lambda表达式

interface ILambda2{

void test(String name, int age);

}

// 3. 带有参数,带有返回值的lambda表达式

interface ILambda3 {

int test(int x, int y);

}

编写Lambda表达式示例:

// 1. [接口声明] = (参数) -> {执行代码块};

ILambda1 i1 = () -> {

System.out.println("hello imooc!");

System.out.println("welcome to imooc!");

};

i1.test();

// 2. lambda表达式的返回值,如果代码块只有一行,并且没有大括号,不用写return关键字,单行代码的执行结果,会自动返回

ILambda1 i2 = () -> System.out.println("hello imooc");

i2.test();

// 3. 带有多个参数的Lambda表达式

ILambda2 i21 = (String n, int a) -> {

System.out.println(n + "say: my year's old is " + a);

};

i21.test("jerry", 18);

// 4. 不写参数类型,由jvm自动推导

ILambda2 i22 = (n, a) -> {

System.out.println(n + " 说:我今年" + a + "岁了.");

};

i22.test("tom", 22);

// 5. 带有返回值的Lambda表达式

ILambda3 i3 = (x, y) -> {

int z = x + y;

return z;

};

System.out.println(i3.test(11, 22));

// 6. 只有一行时,可以省略大括号和return字段

ILambda3 i31 = (x, y) -> x + y;

System.out.println(i31.test(100, 200));

小结:

lambda表达式,必须和接口进行绑定。

lambda表达式的参数,可以附带0个到n个参数,括号中的参数类型可以不用指定,jvm在运行时,会自动根据绑定的抽象方法中参数进行推导。

lambda表达式的返回值,如果代码块只有一行,并且没有大括号,不用写return关键字,单行代码的执行结果,会自动返回。如果添加了大括号,或者有多行代码,必须通过return关键字返回执行结果。

3.6 变量捕获——变量的访问操作

1. 匿名内部类型中对于变量的访问

public void testInnerClass() {

String s2 = "局部变量";

new Thread(new Runnable() {

String s3 = "内部变量";

@Override

public void run() {

// 访问全局变量

// System.out.println(this.s1); // 无法访问s1,这里的this关键字表示是当前内部类型的对象

System.out.println(s1);

System.out.println(s2); // 局部变量的访问

// s2 = "hello"; // 不能对局部变量进行数据的修改[final]

System.out.println(s3);

System.out.println(this.s3);

}

}).start();

}

在匿名内部类中,

this关键字表示的是当前内部类型的对象

局部变量的访问时,不能对局部变量进行数据的修改(默认为final类型)

2. lambda表达式变量捕获

public void testLambda() {

String s2 = "局部变量lambda";

new Thread(() -> {

String s3 = "内部变量lambda";

// 访问全局变量

System.out.println(this.s1);// this关键字,表示的就是所属方法所在类型的对象

// 访问局部变量

System.out.println(s2);

// s2 = "hello";// 不能进行数据修改,默认推导变量的修饰符:final

System.out.println(s3);

s3 = "labmda 内部变量直接修改";

System.out.println(s3);

}).start();

}

在Lambda表达式中,

this关键字,表示的就是所属方法所在类型的对象

修改局部时,同样会报错:默认推导变量的修饰符为final

3.7 Lambda表达式类型检查

首先定义个函数式接口:

@FunctionalInterface

interface MyInterface {

R strategy (T t, R r);

}

定义方法:

public static void test(MyInterface inter) {

List list = inter.strategy("hello", new ArrayList());

System.out.println(list);

}

匿名内部类调用:

test(new MyInterface() {

@Override

public List strategy(String s, List list) {

list.add(s);

return list;

}

});

Lambda表达式调用:

test((x, y) -> {

y.add(x);

return y;

});

Lambda表达式方法推导:

(x,y)->{..} --> test(param) --> param==MyInterface --> lambda表达式-> MyInterface类型

这个就是对于lambda表达式的类型检查,MyInterface接口就是lambda表达式的目标类型(target typing)

Lambda表达式方法参数推导:

(x,y)->{..} --> MyInterface.strategy(T r, R r)--> MyInterface inter

--> T==String R==List --> lambda--> (x, y) == strategy(T t , R r)--> x==T==String y==R==List

lambda表达式参数的类型检查

3.8 方法重载和Lambda表达式

先定义两个函数式接口:

interface Param1 {

void outInfo(String info);

}

interface Param2 {

void outInfo(String info);

}

再定义两个重载方法:

// 定义重载的方法

public void lambdaMethod(Param1 param) {

param.outInfo("hello param1 imooc!");

}

public void lambdaMethod(Param2 param) {

param.outInfo("hello param2 imooc");

}

使用匿名内部类调用:

app.lambdaMethod(new Param1() {

@Override

public void outInfo(String info) {

System.out.println(info);

}

});

app.lambdaMethod(new Param2() {

@Override

public void outInfo(String info) {

System.out.println("------");

System.out.println(info);

}

});

这里能正常运行,但是在使用Lambda表达式时,会有问题:

app.lambdaMethod( (String info) -> {

System.out.println(info);

});

异常信息:

Ambiguous method call. Both

lambdaMethod(Param1) in Test and

lambdaMethod(Param2) in Test match

jvm对Lambda表达式调用的方法推导如下:

lambda表达式存在类型检查-> 自动推导lambda表达式的目标类型

lambdaMethod() -> 方法 -> 重载方法

-> Param1 函数式接口

-> Param2 函数式接口

调用方法-> 传递Lambda表达式-> 自动推导->

-> Param1 | Param2

因此,在调用时,需要人为地告诉jvm我们要调用的方法参数是啥:

app.lambdaMethod((Param1) (String info) -> {

System.out.println(info);

});

3.9 深入理解lambda表达式

Lambda表达式在jvm谨慎解析在私有静态方法和匿名内部类型,通过实现接口的匿名内部类型中接口方法调用静态实现方法,完成Lambda表达式的执行。

先准备一个App.java类:

// 函数式接口

interface IMarkUp {

void markUp(String msg);

}

public class App {

public static void main(String [] args) {

IMarkUp mu = (message) -> System.out.println(message);

mu.markUp("lambda!");

}

}

使用javac 编译下,再使用javap查看:

$ javac App.java

$ javap -p App.class

Compiled from "App.java"

public class App {

public App();

public static void main(java.lang.String[]);

private static void lambda$main$0(java.lang.String);

}

可以看到,自动生成了私有静态方法private static void lambda$main$0(java.lang.String),Lambda在实际运行时,也是生成了一个私有静态方法:

private static void lambda$main$0(String message) {

System.out.println(message);

}

为了查看编译过程,我们使用参数-Djdk.internal.lambda.dumpProxyClasses重新处理:

$ java -Djdk.internal.lambda.dumpProxyClasses App

lambda!

运行后,发现多生成了一个类:App$$Lambda$1.class,使用javap -p App$$Lambda$1查看类的信息:

$ javap -p App$$Lambda$1

final class App$$Lambda$1 implements IMarkUp {

private App$$Lambda$1();

public void markUp(java.lang.String);

}

这里的markUp(java.lang.String)方法实际调用的是 lambda$main$0:

public void markUp(String message) {

App.lambda$main$0(message);

}

总结下lambda表达式的底层执行过程:

在编译时,会自动生成私有静态方法 private static void lambda$main$0(java.lang.String)

在编译时,会自动生成实现类:final class App$$Lambda$1 implements IMarkUp

调用mu.markUp("lambda!"),实际上进行的操作是new App$$Lambda$1().markUp("lambda!")

代码如下:

interface IMarkUp {

void markUp(String msg);

}

public class App {

public static void main(String [] args) {

IMarkUp mu = (message) -> System.out.println(message);

mu.markUp("lambda!");

// 3. 实际调用: new App$$Lambda$1().markUp("lambda!");

}

// 1. 自动生成的私有静态方法

/*

private static void lambda$main$0(String message) {

System.out.println(message);

}

*/

// 2. 自动生成的内部类

/*

final class App$$Lambda$1 implements IMarkUp {

private App$$Lambda$1() {

}

public void markUp(String message) {

App.lambda$main$0(message);

}

}

*/

}

4. Lambda表达式在集合中的运用

4.1 方法引用

方法引用是结合Lambda表达式的一种语法特性,主要分为静态方法引用、实例方法引用和构造方法引用。

先准备一个POJO:

@Data

@AllArgsConstructor

@NoArgsConstructor

class Person {

private String name;

private String gender;

private int age;

// 静态方法引用

public static int compareByAge(Person p1, Person p2) {

return p1.getAge() - p2.getAge();

}

}

再准备一些数据:

List list = new ArrayList();

list.add(new Person("shuke", "男", 29));

list.add(new Person("tom", "男", 16));

list.add(new Person("jerry", "男", 20));

list.add(new Person("beita", "女", 30));

1. 静态方法引用

匿名内部类实现排序:

Collections.sort(list, new Comparator() {

@Override

public int compare(Person o1, Person o2) {

return o1.getAge() - o2.getAge();

}

});

lambda表达式实现排序

Collections.sort(list, (p1, p2) -> p1.getAge() - p2.getAge());

方法引用实现排序

// 使用::操作符引用Person类的静态方法compareByAge()

Collections.sort(list, Person::compareByAge);

2. 实例方法引用

添加一个类,准备实例方法:

class PersonUtil {

// 实例方法引用

public int comprareByName(Person p1, Person p2) {

return p1.getName().hashCode() - p2.getName().hashCode();

}

}

实例方法引用:

PersonUtil pu = new PersonUtil();

Collections.sort(list, pu::comprareByName);

3. 构造方法引用

准备一个函数式接口:

interface IPerson {

Person getPerson(String name, String gender, int age);

}

使用方式如下:

/*

// 匿名内部类方式

IPerson p1 = new IPerson() {

@Override

public Person getPerson(String name, String gender, int age) {

return new Person(name, gender, age);

}

}

// lambda表达式方式

IPerson p1 = (name, gender, age) -> new Person(name, gender, age);

*/

// 绑定构造方法,实际调用的构造方法是 Person(String, String, int)

IPerson p1 = Person::new;

// 调用接口方法

Person person = p1.getPerson("tom", "男", 18);

4.2 Stream概述

首先准备数据:

List list = new ArrayList();

list.add("tom");

list.add("jerry");

list.add("shuke");

list.add("beita");

list.add("damu");

现在有这样的处理要求:找出升序大于5的有效账号

第一种方式:增强for遍历

List lista = new ArrayList();

for (String s : list) {

if (s.length() > 3) {

lista.add(s);

}

}

System.out.println(lista);

第二种方式:Iterator遍历

List listb = new ArrayList<>();

Iterator it = list.iterator();

while(it.hasNext()) {

String s = it.next();

if(s.length() > 3) {

listb.add(s);

}

}

System.out.println(listb);

第三种方式:使用 stream 实现

List listc = list.stream().filter(s->s.length()>3).collect(Collectors.toList());

System.out.println(listc);

4.3 Stream常见操作API介绍

4.3.1 聚合操作

4.3.2 stream的处理流程

数据源

数据转换

获取结果

4.3.3 获取Stream对象

从集合或者数组中获取[**]

Collection.stream(),如accounts.stream()

Collection.parallelStream()

Arrays.stream(T t)

BufferReader

BufferReader.lines()-> stream()

静态工厂

java.util.stream.IntStream.range()..

java.nio.file.Files.walk()..

自定构建

java.util.Spliterator

更多的方式..

Random.ints()

Pattern.splitAsStream()..

4.3.4 中间操作API{intermediate}

操作结果是一个Stream,中间操作可以有一个或者多个连续的中间操作,需要注意的是,中间操作只记录操作方式,不做具体执行,直到结束操作发生时,才做数据的最终执行。

中间操作:就是业务逻辑处理。

中间操作过程:

无状态:数据处理时,不受前置中间操作的影响,如:map/filter/peek/parallel/sequential/unordered

有状态:数据处理时,受到前置中间操作的影响,如:distinct/sorted/limit/skip

4.3.5 终结操作|结束操作{Terminal}

需要注意的是,一个Stream对象,只能有一个Terminal操作,这个操作一旦发生,就会真实处理数据,生成对应的处理结果。

终结操作又可区分为非短路操作和短路操作,

非短路操作:当前的Stream对象必须处理完集合中所有 数据,才能得到处理结果,如:forEach/forEachOrdered/toArray/reduce/collect/min/max/count/iterator

短路操作:当前的Stream对象在处理过程中,一旦满足某个条件,就可以得到结果,如:anyMatch/allMatch/noneMatch/findFirst/findAny等,Short-circuiting,无限大的Stream-> 有限大的Stream。

4.4 Stream操作集合数中的数据-上

4.4.1 获取stream

多个数据

Stream stream = Stream.of("admin", "tom", "damu");

数组

String [] strArrays = new String[] {"xueqi", "biyao"};

Stream stream2 = Arrays.stream(strArrays);

列表

List list = new ArrayList<>();

list.add("少林");

list.add("武当");

list.add("青城");

list.add("崆峒");

list.add("峨眉");

Stream stream3 = list.stream();

集合

Set set = new HashSet<>();

set.add("少林罗汉拳");

set.add("武当长拳");

set.add("青城剑法");

Stream stream4 = set.stream();

Map

Map map = new HashMap<>();

map.put("tom", 1000);

map.put("jerry", 1200);

map.put("shuke", 1000);

Stream stream5 = map.entrySet().stream();

4.4.2 Stream对象对于基本数据类型的功能封装

// int / long / double

IntStream.of(new int[] {10, 20, 30}).forEach(System.out::println);

// range方法:[1, 5),左闭右开

IntStream.range(1, 5).forEach(System.out::println);

// rangeClosed:[1, 5],左半右闭

IntStream.rangeClosed(1, 5).forEach(System.out::println);

4.4.3 Stream对象 --> 转换得到指定的数据类型

// 数组

Object [] objx = stream.toArray(String[]::new);

// 字符串

String str = stream.collect(Collectors.joining()).toString();

// 列表

List listx = (List) stream.collect(Collectors.toList());

// 集合

Set setx = (Set) stream.collect(Collectors.toSet());

// Map

Map mapx = (Map) stream.collect(Collectors.toMap(x->x, y->"value:"+y));

4.5 Stream操作集合数中的数据-下

1. Stream中间操作

List accountList = new ArrayList<>();

accountList.add("xongjiang");

accountList.add("lujunyi");

accountList.add("wuyong");

accountList.add("linchong");

accountList.add("luzhishen");

accountList.add("likui");

accountList.add("wusong");

// map() 中间操作,map()方法接收一个Functional接口

accountList = accountList.stream().map(x->"梁山好汉:" + x).collect(Collectors.toList());

// filter() 添加过滤条件,过滤符合条件的用户

accountList = accountList.stream().filter(x-> x.length() > 5).collect(Collectors.toList());

// forEach 增强型循环

accountList.forEach(x-> System.out.println("forEach->" + x));

// peek() 中间操作,迭代数据完成数据的依次处理过程

accountList.stream()

.peek(x -> System.out.println("peek 1: " + x))

.peek(x -> System.out.println("peek 2:" + x))

.forEach(System.out::println);

2. Stream中对于数字运算的支持

List intList = new ArrayList<>();

intList.add(20);

intList.add(19);

intList.add(7);

intList.add(8);

intList.add(86);

intList.add(11);

intList.add(3);

intList.add(20);

// skip() 中间操作,有状态,跳过部分数据

intList.stream().skip(3).forEach(System.out::println);

// limit() 中间操作,有状态,限制输出数据量

intList.stream().skip(3).limit(2).forEach(System.out::println);

// distinct() 中间操作,有状态,剔除重复的数据

intList.stream().distinct().forEach(System.out::println);

// sorted() 中间操作,有状态,排序

// max() 获取最大值

Optional optional = intList.stream().max((x, y)-> x-y);

System.out.println(optional.get());

// min() 获取最小值

// reduce() 合并处理数据

Optional optional2 = intList.stream().reduce((sum, x)-> sum + x);

System.out.println(optional2.get());

5. Lambda表达式在实际生产中的应用

5.1 Lambda表达式重构项目

可以使用Lambda表达式简化项目中的代码。

5.2 Lambda表达式和Stream性能问题

我们主要从两个方面进行性能比较:基本数据类型与复杂数据类型。

5.2.1 基本数据类型的性能比较

package java8;

import java.util.ArrayList;

import java.util.Iterator;

import java.util.List;

import java.util.Optional;

import java.util.Random;

/**

* {这里添加描述}

*

* @author funcy

* @date 2020-01-18 8:57 下午

*/

public class Test02 {

public static void main(String[] args) {

Random random = new Random();

// 1. 基本数据类型:整数

List integerList = new ArrayList();

for (int i = 0; i < 1000000; i++) {

integerList.add(random.nextInt(Integer.MAX_VALUE));

}

// 1) stream

testStream(integerList);

// 2) parallelStream

testParallelStream(integerList);

// 3) 普通for

testForLoop(integerList);

// 4) 增强型for

testStrongForLoop(integerList);

// 5) 迭代器

testIterator(integerList);

}

public static void testStream(List list) {

long start = System.currentTimeMillis();

Optional optional = list.stream().max(Integer::compare);

System.out.println(optional.get());

long end = System.currentTimeMillis();

System.out.println("testStream:" + (end - start) + "ms");

}

public static void testParallelStream(List list) {

long start = System.currentTimeMillis();

Optional optional = list.parallelStream().max(Integer::compare);

System.out.println(optional.get());

long end = System.currentTimeMillis();

System.out.println("testParallelStream:" + (end - start) + "ms");

}

public static void testForLoop(List list) {

long start = System.currentTimeMillis();

int max = Integer.MIN_VALUE;

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

int current = list.get(i);

if (current > max) {

max = current;

}

}

System.out.println(max);

long end = System.currentTimeMillis();

System.out.println("testForLoop:" + (end - start) + "ms");

}

public static void testStrongForLoop(List list) {

long start = System.currentTimeMillis();

int max = Integer.MIN_VALUE;

for (Integer integer : list) {

if (integer > max) {

max = integer;

}

}

System.out.println(max);

long end = System.currentTimeMillis();

System.out.println("testStrongForLoop:" + (end - start) + "ms");

}

public static void testIterator(List list) {

long start = System.currentTimeMillis();

Iterator it = list.iterator();

int max = it.next();

while (it.hasNext()) {

int current = it.next();

if (current > max) {

max = current;

}

}

System.out.println(max);

long end = System.currentTimeMillis();

System.out.println("testIterator:" + (end - start) + "ms");

}

}

运行结果如下:

2147480897

testStream:88ms

2147480897

testParallelStream:28ms

2147480897

testForLoop:9ms

2147480897

testStrongForLoop:11ms

2147480897

testIterator:15ms

5.2.2 复杂数据类型的性能

package java8;

import java.util.ArrayList;

import java.util.Iterator;

import java.util.List;

import java.util.Optional;

import java.util.Random;

/**

* {这里添加描述}

*

* @author funcy

* @date 2020-01-18 9:11 下午

*/

public class Test03 {

public static void main(String[] args) {

Random random = new Random();

List productList = new ArrayList<>();

for(int i = 0; i < 1000000; i++) {

productList.add(new Product("pro" + i, i, random.nextInt(Integer.MAX_VALUE)));

}

// 调用执行

testProductStream(productList);

testProductParallelStream(productList);

testProductForloop(productList);

testProductStrongForloop(productList);

testProductIterator(productList);

}

public static void testProductStream(List list) {

long start = System.currentTimeMillis();

Optional optional = list.stream().max((p1, p2)-> p1.hot - p2.hot);

System.out.println(optional.get());

long end = System.currentTimeMillis();

System.out.println("testProductStream:" + (end - start) + "ms");

}

public static void testProductParallelStream(List list) {

long start = System.currentTimeMillis();

Optional optional = list.stream().max((p1, p2)-> p1.hot - p2.hot);

System.out.println(optional.get());

long end = System.currentTimeMillis();

System.out.println("testProductParallelStream:" + (end - start) + "ms");

}

public static void testProductForloop(List list) {

long start = System.currentTimeMillis();

Product maxHot = list.get(0);

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

Product current = list.get(i);

if (current.hot > maxHot.hot) {

maxHot = current;

}

}

System.out.println(maxHot);

long end = System.currentTimeMillis();

System.out.println("testProductForloop:" + (end - start) + "ms");

}

public static void testProductStrongForloop(List list) {

long start = System.currentTimeMillis();

Product maxHot = list.get(0);

for (Product product : list) {

if(product.hot > maxHot.hot) {

maxHot = product;

}

}

System.out.println(maxHot);

long end = System.currentTimeMillis();

System.out.println("testProductStrongForloop:" + (end - start) + "ms");

}

public static void testProductIterator(List list) {

long start = System.currentTimeMillis();

Iterator it = list.iterator();

Product maxHot = it.next();

while(it.hasNext()) {

Product current = it.next();

if (current.hot > maxHot.hot) {

maxHot = current;

}

}

System.out.println(maxHot);

long end = System.currentTimeMillis();

System.out.println("testProductIterator:" + (end - start) + "ms");

}

}

class Product {

String name; // 名称

Integer stock; // 库存

Integer hot; // 热度

public Product(String name, Integer stock, Integer hot) {

this.name = name;

this.stock = stock;

this.hot = hot;

}

}

运行结果:

java8.Product@5f184fc6

testProductStream:63ms

java8.Product@5f184fc6

testProductParallelStream:15ms

java8.Product@5f184fc6

testProductForloop:16ms

java8.Product@5f184fc6

testProductStrongForloop:16ms

java8.Product@5f184fc6

testProductIterator:17ms

5.2.3 结论

jvm相关人员也对stream进行了一系列测,结果如下:

可以看到,随着核心数增加,并行Stream带来的性能提升是非常明显的。

最终,我们可以得到这样一个结论:对于简单数据的迭代处理,可以直接通过外部迭代进行操作,如果在性能上有一定的要求,可以使用并行stream进行操作;对于复杂对象的处理操作,stream的串行操作在性能上已经和普通的迭代相差无几,甚至超过了普通的迭代方式,完全可以用简洁的stream的语法来替换普通的迭代操作,如果在性能上有要求,可以直接选择并行stream操作以提升性能,并行stream在多核条件下,更能发挥其性能优势。

5.3 线程安全问题

这一节我们来看看并行stream(parallelStream)的线程安全:

package java8;

import java.util.ArrayList;

import java.util.List;

import java.util.stream.Collectors;

/**

* {这里添加描述}

*

* @author funcy

* @date 2020-01-18 9:30 下午

*/

public class Test04 {

public static void main(String[] args) {

// 整数列表

List lists = new ArrayList();

// 增加数据

for (int i = 0; i < 1000; i++){

lists.add(i);

}

// 串行Stream

List list2 = new ArrayList<>();

lists.stream().forEach(x->list2.add(x));

System.out.println(lists.size());

System.out.println(list2.size());

// 并行Stream

List list3 = new ArrayList<>();

lists.parallelStream().forEach(x-> list3.add(x));

System.out.println(list3.size());

// stream的collect操作

List list4 = lists.parallelStream().collect(Collectors.toList());

System.out.println(list4.size());

}

}

运行结果如下:

1000

1000

994

1000

可以看到,lists.parallelStream().forEach(x-> list3.add(x)) 会引发线程安全问题,而lists.parallelStream().collect(Collectors.toList())不会引起线程安全问题。

关于stream的collect操作,官方文档有云:当并行 执行时,可以实例化,填充和合并多个中间结果,以便保持可变结构的隔离。因此,即使与非线程安全的数据结构(例如ArrayList)并行执行,并行还原也不需要额外的同步。

结论:并行stream的线程安全问题,在业务处理的过程中,主要通过自定义编码添加线程锁的方式,或者使用stream api中提供的线程安全的终端操作来完成执行过程。不过,在更多的场景中,如果我们遇到多线程问题,会直接使用线程安全的集合来规范数据源。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值