史上最全Lambda表达式介绍

欢迎进入Lambda世界

一、 Lambda表达式的基础语法

java8引入了一个新的操作符 “->” ,该操作符称为箭头操作符或Lambda操作符
左侧:Lambda表达式的参数列表
右侧:Lambda表达式中所需要执行的功能,即Lambda体
语法格式:
格式一:无参数,无返回值
() -> System.out.printLn(“hello world”);

Runnable  r1 = () -> System.out.println("hello world");
r1.run;

格式二:一个参数,并且无返回值。

(x) -> System.out.printLn(x);

格式三:如果只有一个参数,小括号可以不写

x -> System.out.printLn(x);

格式四:有两个以上参数,并且有返回值
如果Lambda体有多条语句,则必须使用大括号

Comparator<Integer> com =    (x,y) -> {
	 System.out.println("请求参数:x=“ + x +",y= "+y);
	 return Integer.compare(x,y);
 }

格式五:若Lambda体中只有一个条语句,则return和大括号都可以不写

Comparator<Integer> com =    (x,y) ->  Integer.compare(x,y);

格式六:Lambda表达式的参数列表的数据类型可以省略不写,因为JVM编译器通过上下文推断出数据类型,即”类型推断
总结:左右遇一括号省,左侧推断类型省。
解释:左边遇到一个参数括号省,右边遇到一条语句大括号和return省
左侧类型为Interger,推断出右侧的x和y必为Integer,所有类型可以省,如下代码:

    x -> System.out.printLn(x); //左右遇一括号省
   Comparator<Integer> com =    (x,y) ->  Integer.compare(x,y);//左侧推断类型省

二、 Lambda表达式需要"函数式接口"的支持

函数式接口:接口中只有一个抽象方法的接口,称为函数是接口,可以使用注解**@FunctionalInterface**来修饰,如果多个抽象方法则会报错。

java8内置的四大核心函数式接口

1. Consumer : 消费型接口
              void  accept(T t);
public void test1(){
    testConsumer(100,(m) -> System.out.println("购物消费" +m +" 元"));
}
public void testConsumer(double money,Consumer<Double> con){
     con.accept(money);
}
2. Supplier : 供给型接口
              T   get();
public void test2(){
    getNumList(10, () -> (int) (Math.random() *100));
    for(Integer num : numList){
 	   System.out.prinln(num);
      }
}
//需求:产生指定个数的参数,并放入集合
public List<Integer> getNumList(int num,Supplier<Integer> sup){
	 List<Integer> list = new ArrayList();
	 for(int i = 0 ; i< num; i++){
	    Integer n = sup.get();
	    list.add(n);
	  }
	  return list;
}
3. Function< T,R > : 函数型接口
              R   apply(T t);
  public void test3(){
        strHandler("hello world", (str) -> str.trim());
 }
		//用于处理字符串
 public String strHandler(String str,Functing<String , String> fun){
     	return fun.apply(str);
 }
4. Predicate : 断言型接口
              boolean  test(T t);
public void test4(){
 List<String> list = Arrays.asList("hello","www","Lambda","ok","world");
List<String> strList  =   filterStr(list,(s) -> s.length() > 3);//Lambda体只有一条语句return可以不写
	for(String s : strList)   {
	     System.out.println(s);
	 }
   
}
//将满足条件的字符串添加到集合中
public List<String> filterStr(List<String> list , Predicate<String> pre){
        List<String> strList = new ArrayList<>();
         for(String str : list){
			if(pre.test(str)){
			 strList.add(str);
			}
		}
		return strList;
 }
5.其他函数式接口
函数式接口参数类型返回类型用途使用
BiFunction<T,U,R>T,UR对类型为T,U参数参数应用操作,返回R类型的结果,包含方法为 R apply(R t,U u);BiFunction的使用
UnaryOperator (Function子接口,一元运算函数式接口)TT对类型为T的对象进行一元运算,并返回T类型的结果,包含方法为 T apply(T t);UnaryOperator的使用
BinaryOperator (BiFunction子接口)T,TT对类型为T的对象进行二元运算,并返回T类型的结果,包含方法为:T apply(T t1,T t2);
BiConsumer<T,U>T,Uvoid对类型为T,U参数应用操作,包含方法为void accept(T t,U u);
ToIntFunction< T >intvoid计算int值的函数
ToLongFunction< T >longvoid计算long值的函数
ToDoubleFunction< T >doublevoid计算double值的函数
IntFunction< R >intR参数为int类型的函数
LongFunction< R >longR参数为long类型的函数
DoubleFunction< R >doubleR参数为double类型的函数

三、方法引用和构造器引用

1、方法引用:

若Lambda体中的内容有方法已经实现了,那么我们可以使用**方法引用**(可以理解为方法引用是Lambda表达式的另外一种表现形式)
主要有三种语法格式:
  1. 对象::实例方法名
  2. 类::静态方法名
  3. 类::实例方法名
使用注意事项:

1:Lamdba体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型一致

2:如果函数式接口的第一个参数是方法的调用者,第二个参数是调用方法的参数时,则可以使用**类名::实例方法**
使用案例
1、对象::实例方法名
//demo1:
Consumer<String> con = (x)-> System.out.println(x);
//上面的println方法已经实现了我们功能,可以使用方法引用的方式来代替Lambda表达式
//替换如下
PrintStream ps = System.out;
Consumer<String> con1 = ps::println;
Consumer<String> con2 = System.out::println;
//Consumer的accept方法的参数为一个,返回值类型为void;
//println的参数为一个,返回值类型为void;
//所有可以使用方法引用来代替Lambda表达式

//demo2:
Employee emp = new Employee();
Supplier<String> sup = () -> emp.getName();
//替换如下
Supplier<String> sup = emp::getName;
System.out.println(sup.get());

注:使用方法引用时,方法引用的参数类型和返回值需要和函数式接口方法的参数和返回值类型保持一致。 如上代码

2、类::静态方法名
Comparator<Integer> com = (x,y) -> Integer.compare(x,y);
//上面的 Integer.compare方法已经实现了我们功能,可以使用方法引用的方式来代替Lambda表达式
//替换如下:
Comparator<Integer> com1 = Integer::compare;
3、类::实例方法
BiPredicate<String,String> bp = (x,y) -> x.equalse(y);
//替换如下:
BiPreicate<String,String> bp1 = String::equals;

注:如果函数式接口的第一个参数是方法的调用者,第二个参数是调用方法的参数时,则可以使用类名::实例方法,如上代码。

2、构造器引用

1、格式:
ClassName::new
2、使用注意事项

使用构造器引用,使用的哪个构造器呢?
答:根据函数式接口方法的参数自动匹配对应类的构造器。即需要调用的构造器参数列表要与函数式接口中抽象方法的参数列表保持一致。

3、使用案例
Supplier<Employee> sup = () -> new Employee();
//构造器引用替换如下:
Supplier<Employee> sup =Employee::new;

//使用一个参数的函数式接口,则调用Employee的一个参数的构造参数
Function<Integer,Employee> fun = (x) -> new Employee(x);
Function<Integer,Employee> fun2 = Employee::new;
Employee emp = fun2.apply(23);
System.out.println(emp);

//使用Employee的两个参数的构造器。
BiFunction<String,Integer,Employee> bf = Employee::new;
Employee emp2 = bf.apply("张三",23);

注:Supplier的方法是无参的,所有Employee::new使用的是无参构造器来创建对象

3、数组引用(与构造器引用类似)

1、格式

Type::new

2、注意事项
3、使用案例
Function<Integer,String[]> fun = (x) -> new String[x];
String[] strs = fun.apply(10);
System.out.println(strs.length);
//数组引用替换如下:
Function<Integer,String[]> fun2 = String[]::new
String[] strs2 = fun2.apply(20);
System.out.println(strs2.length);

四、强大的Stream API

1、概念

java8中两个最为重要的改变,一个是Lambda表达式,另一个就是Stream API(java.util.stream.*);
Stream 是java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行复杂的查找、过滤和映射数据等操作。使用Stream API对集合数据进行操作,就类似于使用SQL执行的数据库查询,也可以使用Stream API来并行执行操作。简而言之,Stream API提供了一种高效且易于使用的处理数据的方式。
流(Stream)到底是什么呢?
是数据渠道,用于操作数据源(集合,数组等)所生成的元素序列。“集合讲的是数据,流讲的是计算”

2、注意事项

  1. Stream自己不会存储元素
  2. Stream不会改变源对象。相反,他们会返回一个持有结果的新Stream
  3. Stream操作是延迟执行的,这意味着他们会等到需要结果的时候才执行。

3、Stream的操作三个步骤

  1. 创建Stream
    一个数据源(如:集合、数组),获取一个流
  2. 中间操作
    一个中间操作链,对数据源的数据进行处理
  3. 终止操作(终端操作)
    一个终止操作,执行中间操作链,并产生结果。
    stream操作三步骤
3.1创建Stream
//创建Stream
public void test1(){
   //1 可以通过Collection系列集合提供的Stream() 或parallelStream()
  //parallelStream 并行流
  List<String> list = Lists.newArrayList();
  Stream<String> stream1 = list.stream();

  //2通过Arrays中的静态方法Stream()获取数组流
  Employee[] emps =new Employee[10];
  Stream<Stream> stream2 = Arrays.stream(emps);

  //3 通过Stream类中的静态方法of();
  Stream<String> stream3 = Stream.of("aa","bb","cc");

  //4创建无限流 
  //迭代 ,从0开始,一直加2,无限叠加循环。
Stream<Integer> sream4 =  Stream.iterate(0,(x) -> x+2);
stream4.forEach()System.out::println);
//中间操作,获取前10个
stream4.limit(10).forEach()System.out::println);

//5、 生成6个随机数,也可以生成无限流。
Stream.generate(() -> Math.random()).limit(6).forEach(System.out::println);
}
3.2 Steam的中间操作

多个中间操作可以连接起来形成一个流水线,除非流水线上触发终止操作,否则中间操作不会执行任何处理! 而在终止操作时一次性全部处理,称为"惰性求值"。

1、筛选与切片
方法描述
filter(Predicate p)接收Lambda,从流中排除某些元素
distinct()筛选,通过流所生成元素的hashCode()和equals()去除重复元素
limit(long maxSize)截断流,使其元素不超过给定数量
skip(long n)跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit(n)互补
筛选与切片的使用案例

1、 filter

Stream<Employee> s = employees.stream()
						.filter((e) ->e.getAge()>35)//中间操作,不会有任何结果
						.forEach(System.out::println);//终止操作,一次性执行全部内容,即”惰性求值“
//内部迭代由Stream API完成

//外部迭代:我们自己写
Iterator<Employees> it = employees.iterator();
while(it.hasNext()){
   System.out.println(it.next());
}

2、limit

Stream<Employee> s = employees.stream()
						.filter((e) ->e.getAge()>35)//中间操作,不会有任何结果
						.limit(2) //中间操作,截断流,只执行2次迭代。后续的迭代操作不再执行。
						.forEach(System.out::println);//终止操作,一次性执行全部
						

3、skip

 Stream<Employee> s = employees.stream()
						.filter((e) ->e.getAge()>35)//中间操作,不会有任何结果
						.skip(2) //中间操作,跳过前两个元素,取之后两个
						.forEach(System.out::println);//终止操作,一次性执行全部

4、distinct 去重

   Stream<Employee> s = employees.stream()
						.filter((e) ->e.getAge()>35)//中间操作,不会有任何结果
						.skip(2) //中间操作,跳过前两个元素,取之后两个
						.distinct() //中间操作,去重(需要重写hashCode和equals方法)
						.forEach(System.out::println);//终止操作,一次性执行全部
2、映射

map它接收Lambda,将元素转换成其他形式或提取信息,接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素。
flatMap它接收一个函数作为参数,将流中的每个值都转换成另一个流,然后把所有的流连成一个流。

方法描述
map(Function f)接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
mapToDouble(ToDoubleFunction f )接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的DoubleStream
mapToInt(ToLongFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的IntStream
mapToLong(ToLongFunction f)接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的LongStream
flatMap(Function f)接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连成一个流
映射的使用案例

1、map

public void test(){
 List<String> list = Arrays.aslist("aa","bb","cc","dd","ee");
 list.stream().stream()
 					.map((str) -> str.toUpperCase())
 					.forEach(System.out::println)
	System.out.println("------------分割线----------")
   employees.stream()
   					.map(Employee::getName)
   					.forEach(System.out::println);
	 System.out.println("------------分割线----------")
  Stream<Stream<Character>> streams = 
  			list.stream().map(ClassName::filterCharacter);
  			
 		streams.forEach((sm) -> {
				sm.forEach(System.out::println);
		})
}
//操作字符串的方法
 publib Stream<Character> filterCharacter(String str){
			List<Character> list = new ArrayList<>();
			for(Character ch : str.toCharArray){
				 list.add(ch);			
     		}
     		return list.stream();
}

2、flatMap

//上面代码的替换,使用flatMap
Stream<Character> sm =    list.stream()
											    .flatMap(ClassName::filterCharacter)
											    .forEach(System.out::println);


map和flatMap区别
//上面两个方法map和flatMap对比
map和flatMap都返回一个流。
eg:对于一个List,map是将list里面每个数据都生成一个流(有几个元素就有几个流),然后进行操作
而flatMap是将所有的元素生成一个流进行操作的。
map内部是接收多少个流,则有多少个流,要分开操作--->{ {流1(元素1,元素2,元素3)},{流2(元素4,元素5...)}, ... {流n(元素n)}}
flatMap内部是将所有的流元素拿出来,放在一个流中--->{  一个流 元素1,元素2,元素3,元素4,元素5  …… 元素n }
类似于add和addAll;
add添加的是整个对象,如果是集合,就把集合添加到集合中 
List list1 = new ArrayList();
List list2 = new ArrayList();
List list3 = new ArrayList();
List listStr = new ArrayList();
listStr.add("aa");
listStr.add("bb");
list2.add(11);
list3.add(11);
list2.add(22);
list3.add(22);

list2.add(listStr);
list3.add(listStr);
//输出: list2 =  [11, 22, [aa,bb] ]
//输出: list3 =  [11, 22, aa, bb]
addAll添加的是元素,如果添加集合,则将集合的元素添加到原理的集合中。
3、排序

sorted(): 自然排序(Comparable)
sorted(Comparator com): 定制排序(Comparator)

方法描述
sorted()产生一个新流,其中按自然顺序排序
sorted(Comparator comp)产生一个新流,其中按比较器顺序排序
排序的使用案例
 List<String> list = Arrays.aslist("aa","bb","cc","dd","ee");
 list.stream().sorted()
 			.forEach(System.out::println);
 System.out.println("------------分割线----------")
 employees.stream()
			 .sorted((e1,e2)->{
 					if(e1.getAge().equals(e2.getAge())){
							return e1.getName().compareTo(e2.getName));
						}else{
							return e1.getAge().compareTo(e2.getAge());
						}
				}).forEach(System.out::println);
3.3 终止操作

终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,比如:list,Integer,甚至是void。

1、查找与匹配
方法描述
allMatch(Predicate p )检查是否匹配所有元素
anyMatch(Predicate p)检查是否至少匹配一个元素
noneMatch(Predicate p )检查是否没有匹配到所有元素
findFirst()返回第一个元素
findAny()返回当前流中的任意元素
count返回流中元素的个数
max返回流中最大值
min返回流中最小值
查找与匹配的使用案例
1、allMatch
	boolean b1 = employees.stream()
							.allMatch((e) -> e.getStatus().equals(Status.BUSY));
      System.out.println("是否全部匹配所有元素" + b1);
 2、anyMatch
	boolean b2 = employees.stream()
							.anyMatch((e) -> e.getStatus.equals(Status.BUSY));
       System.out.println(”是否有一个匹配的元素“ +b2);
3、 noneMatch
	boolean b3 = employees.stream()
							.noneMatch((e) -> e.getStatus.equals(Status.BUSY));
       System.out.println("是否没有匹配到所有元素" +b3);     
4、 findFirst  
      Optional<Employee>  op = employees.stream()
    						.sort( (e1,e2) -> Double.compare(e1.getSalary(), e2.getSalary()))
							.findFirst((e) -> e.getStatus.equals(Status.BUSY));
		op.orElse(new Employee);//如果Opentional里面的值为空,则使用替代的对象,避免空指针异常
       System.out.println("获取第一个元素" +op.get());     
5、findAny
		Opentional<Employee> op = employees.stream()
       					.filter((e) -> e.getStatus().equals(Satus.FREE))
				       .findAny();
//.stream() 获取一个并行流,从上往下找,找到第一个。
//.parallelStream() 获取一个并行流,多个线程同时找,谁找到就用谁。
6、count
Long count = employees.stream().count();
System.out.println(count);
7、max
 Opentional<Employee> op1 = employees.stream().max((e1,e2) -> Double.compare(e1.getSarlary(),e2.getSarlary));
 System.out.println(op1.get());
8、min
Optional<Double> op2 = employees.stream().map(Employee::getSarlary).min(Double::compare);
System.out.println(op2.get());		       
2、 归约
方法描述
reduce(T identity, BinaryOperator accumulator)可以将流中的元素反复结合起来,得到一个值,返回T
reduce(BinaryOperator b)可以将流中元素反复结合起来,得到一个值,返回Optional< T >

备注:map和reduce的连接通常称为map-reduce模式,因Google用它来进行网络搜索而出名

归约的使用案例
List<Integer> list = Arrays.aslist(1,2,3,4,5,6,7,8,9);
Integer sum =list.stream().reduce(0,(x,y) -> x+y); 
//第一步 : 0+1=1
//第二步:   1+2 =3
//第三步:  3+3 =6
//第四步:  3+4 =10  ……
System.out.println(sum); //45

Optional<Double> op = employees.stream()
								.map(Employee::getSarlary)
								.reduce(Double::sum);
	System.out.println(op.get());
3、收集
方法描述
collect(Collector c)将流转换成其他形式,接受一个Collector接口的实现,用于给Stream中元素做汇总的方法

注: Collector接口中方法的实现决定了如何对流执行收集操作(如收集到List,Set,Map),但是Collectors实用类提供了很多静态方法,可以方便地创建常见收集器实例,具体方法与实例如下表:

收集的使用案例(分组,最大值,最小值等)
       //收集到list中
        List<String> list = employees.stream()
                .map(Employee::getName)
                .collection(Collectors.toList());
        list.forEach(System.out::println);
        //收集到set中
        List<String> set = employees.stream()
                .map(Employee::getName)
                .collection(Collectors.toSet());
        set.forEach(System.out::println);
        //收集到hash或者linkedHashSet中
        HashSet<String> hs = employees.stream()
                .map(Employee::getName)
                .collection(Collectors.toCollection(HashSet::new));
        HashSet<String> lhs = employees.stream()
                .map(Employee::getName)
                .collection(Collectors.toCollection(LinkedHashSet::new));
        set.forEach(System.out::println);
        lhs.forEach(System.out::println);
        //总数		
        Long count = employees.stream().collection(Collectors.counting());
        //平均值
        Long count = employees.stream()
                .collection(Collectors.averagingDouble(Employee::getSarlary));
        //工资总和
        Double sum = employees.stream()
                .collect(Collectors.summingDouble(Employee::getSalary))
        //最大值
        Optional<Employee> max = employees.stream()
                .collect(Collectors.maxBy((e1, e2) -> Double.compare(e1.getSarlary, e2.getSarlary())));
        //最小值
        Optional<Double> min = employees.stream()
                .map(Employee::getSalary)
                .collect(Collectors.minBy(Double::compare));
        //分组
        Map<Status, List<Employee>> map = employees.stream()
                .collect(Collectors.groupingBy(Employee::getStatus));
        System.out.println(map);
        //多级分组
        Map<Status, Map<String, Employee>>>map = employees.stream()
                .collect(Collectors.groupBy(Employee::getStatus), Collectors.groupBy((e) -> {
                    if (((Employee) e).getAge() <= 35) {
                        return ”青年“
                    } else if (((Employee) e).getAge() <= 50) {
                        return ”中年“;
                    } else {
                        return "老年";
                    }
                }));
        //分区
        Map<Boolean, List<Employee>> map = employees.stream().collect(Collectors.partitioningBy((e) -> e.getSalary() > 8000));
        System.out.println(map);
        //各种获取方式
        DoubleSummaryStatistics dss = employees.stream()
                .collect(Collector.summarizingDouble(Employee::getSalary));
        System.out.println(dss.getSum());
        System.out.println(dss.getAverage());
        System.out.println(dss.getMax());
        //字符串拼接
        String str1 = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.joining());
        System.out.println(str);// "张三李四王五赵六";
        //字符串拼接,分割
        String str2 = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.joining(”,“));
        System.out.println(str2);// "张三,李四,王五,赵六"
        //字符串拼接,分割&字符串前后加符号
        String str3 = employees.stream()
                .map(Employee::getName)
                .collect(Collectors.joining(",", "===", "==="));
        System.out.println(str3);// "===张三,李四,王五,赵六===";

stream可以任意并行流和串行流进行切换:
list.stream().sequential();//串行流
list.stream().parallel();//并行流
todo 持续更新
预告片:Opentional 与 新时间LocalDateTime

  • 0
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

三丶竹

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值