一、函数式编程
1、函数式编程是一种方法论:能够熟练使用流 API和lambda表达式和流相关思想,就可以说自己会函数式编程了。
2、命令式编程关注 怎么做,函数式编程关注 做什么(我要实现什么样的功能而不用管实现的细节)
二、Lambda表达式:
1、Lambda表达式返回的是一个指定接口的对象实例。接口里只能有一个要实现的方法(要实现的方法并不是指接口里只能有一个方法)。jdk8里引入注解@FunctionalInterface,标明此接口为函数接口。jdk8里还新增了已实现的方法(如图)
2、函数接口:
Function<Integer,String>:定义一个输入是int类型,输出是String的函数。使用函数接口,可以不用定义那么多函数接口,还可以实现链式编程。
链式操作举例
class MyMoney{
private final int money;
public MyMoney(int money){
this.money = money;
}
public void getMoney(Function<Integer,String> func){
System.out.println("我的存款是:"+func.apply(this.money));
}
}
public class MyMoneyDemo {
public static void main(String[] args) {
MyMoney me = new MyMoney(999999);
Function<Integer, String> function = i -> new DecimalFormat("#,###").format(i);
me.getMoney(function.andThen(s -> "人民币"+ s));
}
}
public class FunctionDemo {
public static void main(String[] args) {
//断言接口
IntPredicate result = i -> i>0 ;
System.out.println(result.test(-9));
//消费者接口
Consumer<String> consumer = s -> System.out.println(s);
consumer.accept("输入的数据");
}
Lambda的本质是一个匿名函数
s -> System.out.println(s);
函数的参数 函数的执行体
3、Lambda方法引用:
静态方法的引用:
class Dog{
private String name = "哮天犬";
public static void bak(Dog dog){
System.out.println(dog+"叫了");
}
@Override
public String toString() {
return this.name;
}
}
public class FunctionDemo {
public static void main(String[] args) {
//静态方法
Consumer<Dog> consumer = Dog::bak;
Dog dog = new Dog();
consumer.accept(dog);
}
}
非静态方法的引用:
class Dog{
private String name = "哮天犬";
private int food = 50;
public static void bak(Dog dog){
System.out.println(dog+"叫了");
}
public int eat(int num){
System.out.println("吃了"+num +"狗粮");
this.food -= num;
return this.food;
}
@Override
public String toString() {
return this.name;
}
}
public class FunctionDemo {
public static void main(String[] args) {
//静态方法
// Consumer<Dog> consumer = Dog::bak;
Dog dog = new Dog();
// consumer.accept(dog);
//1.非静态,使用对象实例的方法引用
Function<Integer,Integer> function = dog::eat;
System.out.println("还剩下:"+function.apply(2)+"斤");
}
<Integer,Integer>方法的输入和输出类型一致时
// Function<Integer,Integer> function = dog::eat;
// UnaryOperator<Integer> function = dog::eat;//一元函数接口
IntUnaryOperator function = dog::eat;
System.out.println("还剩下:"+function.applyAsInt(2)+"斤");
修改非静态的参数:
不报错的原因:jdk默认会把当前实例传入非静态函数,参数名是this,位置是第一个。
所以当我们用类名对非静态方法做方法引用的时候,参数名为两个。
以上两种都可以
//2.使用类名引用方法
Dog dog = new Dog();
BiFunction<Dog,Integer,Integer> biFunction = Dog::eat;
System.out.println("还剩下:"+biFunction.apply(dog,6)+"斤");
没有参数的构造函数方法的引用:
Supplier<Dog> dogSupplier = Dog::new;
System.out.println("创造了一个新的对象:"+dogSupplier.get());
有参数的构造函数方法的引用:
class Dog{
private String name = "哮天犬";
private int food = 50;
public Dog(){
}
public Dog(String name){
this.name = name;
}
Function<String,Dog> stringDogFunction = Dog::new;
System.out.println("创造了一个新的对象:"+stringDogFunction.apply("旺旺"));
4、
Lambda返回了一个指定接口的对象
lambda的类型推断:
解决:强转
Jdk1.8之前,内部类要引用外部的变量,外部的变量需要命名为final
传参传的是值而不是引用
原因:传值图解:
传引用图解:
5、级联表达式和柯里化:
级联表达式:有多个箭头的lambda表达式
6、Stream流编程
流的创建:
public static void main(String[] args) {
List<String> list = new ArrayList<>();
//并行流
list.stream();
list.parallelStream();
//从数组创建
Arrays.stream(new int[]{2,3,4});
//创建一个数字流
IntStream.of(1,2,3);
IntStream.rangeClosed(1,10);
//使用random创建一个无限流
new Random().ints().limit(10);
Random random = new Random();
Stream.generate(() -> random.nextInt()).limit(20);
}
无状态:我当前的操作和其他元素的前后状态无关
有状态:我当前的结果需要依赖其他的元素(例如排序)
返回的都是String
Map:得到某个对象的属性。例如mapToInt(输入一个字符串得到它的长度)
public static void main(String[] args) {
//打印出每个单词的长度
String str= "my name is hhdh";
// Stream.of(str.split(" ")).filter(s -> s.length()>2).map(s -> s.length()).forEach(System.out::println);
//flatMap a ->B属性(集合)最终得到所有的A元素里面所有的B集合的属性
//返回的是IntStream/LongStrem 并不是Stream的子类,所以需要自动装箱
//打印出来的是所有的字符
Stream.of(str.split(" ")).flatMap(s -> s.chars().boxed()).forEach(
i -> System.out.println((char)i.intValue())
);
//peek用于debug是个中间操作,foEach是个中间操作
//my my name name is is hhdh hhdh
Stream.of(str.split(" ")).peek(System.out::println).forEach(System.out::println);
//limit的使用
New Random().ints().filter(s -> s>=1000&&s<=10000).limit(10).forEach(System.out::println);
}
(char)i.intValue():不加char 打印出的是数字不是字符。i.intValue():Integer需要转换为int
public static void main(String[] args) {
String str= "my name is hhdh";
str.chars().parallel().forEach(i -> System.out.print((char)i));
//有序
//my name is hhdh
str.chars().parallel().forEachOrdered(i -> System.out.print((char)i));
//收集到list
//[my, name, is, hhdh]
List<String> list = Stream.of(str.split(" ")).collect(Collectors.toList());
System.out.println(list);
//使用reduce拼接字符串
//my|name|is|hhdh
Optional<String> letter = Stream.of(str.split(" ")).reduce((s1, s2) -> s1 +"|" +s2);
//返回值为空时不报错
System.out.println(letter.orElse(""));
//|my|name|is|hhdh
String reduce = Stream.of(str.split(" ")).reduce("",(s1, s2) -> s1 +"|" +s2);
System.out.println(reduce);
//单词的总长度
Integer length = Stream.of(str.split(" ")).map(s -> s.length()).reduce(0,(s1,s2) -> s1+s2);
System.out.println(length);
//长度最长的单词
//name
Optional<String> max = Stream.of(str.split(" ")).max((s1,s2) -> s1.length() - s2.length());
System.out.println(max.get());
//短路操作
//-1330528590
OptionalInt first = new Random().ints().findFirst();
System.out.println(first.getAsInt());
}
并行流
public class StreamDemo3 {
public static void main(String[] args) {
//一行一个的打印
IntStream.range(1,100).peek(StreamDemo3::debug).count();
//一次两行的打印
IntStream.range(1,100).parallel().peek(StreamDemo3::debug).count();
//先并行再串行
//多次调用parallel/sequential,以最后一次为准(截图)
IntStream.range(1,100).parallel().peek(StreamDemo3::debug2).sequential()
.peek(StreamDemo3::debug).count();
System.setProperty("java.util.concurrent.ForkJoinPool.common.parallelism()","20");
IntStream.range(1,1000).parallel().peek(StreamDemo3::debug).count(); 未成功
//使用自己的线程池,不适用默认的,防止任务被阻塞
ForkJoinPool pool = new ForkJoinPool(8);
pool.submit(() -> IntStream.range(1,100).parallel().peek(StreamDemo3::debug).count());
pool.shutdown();
//自己的线程是守护线程的关系,因为main函数主线程退出了,所以守护线程也退出了
synchronized (pool){
try {
pool.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
public static void debug(int i){
System.out.println(Thread.currentThread().getName()+"debug" + i);
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
public static void debug2(int i){
System.err.println("debug" + i);
try {
TimeUnit.SECONDS.sleep(3);
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
收集器:
使用MapUtils引入依赖:
<dependency>
<groupId>commons-collections</groupId>
<artifactId>commons-collections</artifactId>
<version>3.1</version>
</dependency>
package com.example.springwebfluxtest;
import com.sun.xml.internal.ws.policy.PolicyMapUtil;
import org.apache.commons.collections.MapUtils;
import java.lang.reflect.Array;
import java.util.*;
import java.util.stream.Collectors;
class Student{
private String name;
private int age;
private String address;
public Student() {
}
public Student(String name, int age, String address) {
this.name = name;
this.age = age;
this.address = address;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
public int getAge() {
return age;
}
public void setAge(int age) {
this.age = age;
}
public String getAddress() {
return address;
}
public void setAddress(String address) {
this.address = address;
}
@Override
public String toString() {
return "Student{" +
"name='" + name + '\'' +
", age=" + age +
", address='" + address + '\'' +
'}';
}
}
public class CollectorDemo {
public static void main(String[] args) {
List<Student> students = Arrays.asList(
new Student("小王",12,"北京"),
new Student("小红",12,"北京"),
new Student("小ming",14,"北京")
);
// List<Integer> ages = students.stream().map(s -> s.getAge()).collect(Collectors.toList());
//不会多生成一个类似lambda$0的函数
List<Integer> ages = students.stream()
.map(Student::getAge).collect(Collectors.toList());
//去重复[12, 13, 14]
Set<Integer> ages2 = students.stream()
.map(Student::getAge).collect(Collectors.toSet());
//转换为指定类型的集合
Set<Integer> ages3 = students.stream()
.map(Student::getAge).collect(Collectors.toCollection(TreeSet::new));
System.out.println(ages2);
//统计信息
//IntSummaryStatistics{count=3, sum=39, min=12, average=13.000000, max=14}
IntSummaryStatistics statistics = students.stream()
.collect(Collectors.summarizingInt(Student::getAge));
System.out.println(statistics);
//分块
//列表分块 =
//{
// false = [Student{name='小ming', age=14, address='北京'}]
// true = [Student{name='小王', age=12, address='北京'}, Student{name='小红', age=12, address='北京'}]
//}
Map<Boolean,List<Student>> map = students.stream().collect(Collectors.partitioningBy(s -> s.getAge() == 12));
MapUtils.verbosePrint(System.out,"列表分块",map);
//分组
//列表分组 =
//{
// 12 = [Student{name='小王', age=12, address='北京'}, Student{name='小红', age=12, address='北京'}]
// 14 = [Student{name='小ming', age=14, address='北京'}]
//}
Map<Integer,List<Student>> integerListMap = students.stream().collect(Collectors.groupingBy(Student::getAge));
MapUtils.verbosePrint(System.out,"列表分组",integerListMap);
//列表分组统计 =
//{
// 12 = 2
// 14 = 1
//}
Map<Integer,Long> count = students.stream()
.collect(Collectors.groupingBy(Student::getAge,Collectors.counting()));
MapUtils.verbosePrint(System.out,"列表分组统计",count);
}
}
JDK1.9 Reactive Stream
背压
Reactive Stream 调节发布、订阅者直接的关系
同步servlet和异步servlet针对的是后台服务器而言,前台只有同步
同步servlet阻塞的是Tomcat的servlet线程,开启异步Servlet,可以把耗时的任务放在一个独立的线程池里,servlet线程可以快速返回去处理其他的任务。
异步servlet是怎样工作的:
1、开启异步上下文
2、把业务代码放在一个独立的线程池进行
3、调用complete通知结束
@RestController
@Slf4j
public class FluxController {
@RequestMapping("/1")
public String hello(){
log.info("get start");
String result = createStr();
log.info("get end");
return result;
}
private String createStr() {
try {
TimeUnit.SECONDS.sleep(5);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "sone String";
}
@RequestMapping("/2")
public Mono<String> hello2(){
log.info("get start2");
Mono<String> result =Mono.fromSupplier(() -> createStr());
log.info("get end2");
return result;
}
}
访问localhost:8080/1
2018-12-26 16:35:38.206 INFO 9252 --- [ctor-http-nio-2] c.e.springwebfluxtest.FluxController : get start
2018-12-26 16:35:43.207 INFO 9252 --- [ctor-http-nio-2] c.e.springwebfluxtest.FluxController : get end
访问localhost:8080/2
2018-12-26 16:36:35.237 INFO 9252 --- [ctor-http-nio-2] c.e.springwebfluxtest.FluxController : get start2
2018-12-26 16:36:35.240 INFO 9252 --- [ctor-http-nio-2] c.e.springwebfluxtest.FluxController : get end2
@RequestMapping(value = "/3",produces =MediaType.TEXT_EVENT_STREAM_VALUE)
public Flux<String> flux(){
Flux<String> result = Flux.fromStream(IntStream.range(1,5).mapToObj(i -> {
try {
TimeUnit.SECONDS.sleep(1);
} catch (InterruptedException e) {
e.printStackTrace();
}
return "flux"+i;
}));
return result;
}
数据像流一样,一个一个输出