== lambda表达式 ==

lambda表达式的使用方式

************************************************************************************************************************
java8新特性之一,流式处理数据
流式操作不仅可以操作集合,也可以操作数据和文件,一般可以转化为流的基本可以使用流来处理数据;流式操作基本上可以分为三个部分:流转化,中间操作,终端操作。
************************************************************************************************************************

lambda操作List,Map和Array>>>

lambda操作list前提是将List转化为Stream接口,除非通过最后步骤的收集器之类的结尾操作指定具体的类型,否则都是会返回Stream接口。
// 创建MdProIndexExt集合
List<MdProIndexExt> bdVos = Lists.newArrayList();

// 将List转化为Stream,返回Stream,这是lambda操作List的前提条件
Stream<MdProIndexExt> stream = bdVos.stream();


 

另一种将集合转为流,使用praallelStream(),支持并行处理数据,使用时考虑多线程安全使用

Stream<ReTrafficDetailExt> parallelStream = trafficDetailExtList.parallelStream();

 

返回String,Stream.distinct()去重,Collectors.joining(",")以","分割拼接字符串

List<MdProIndexExt> bdVos = Lists.newArrayList();
String ids = bdVos.stream().map(MdProIndexExt::getProBatchId).distinct().collect(Collectors.joining(","));
System.out.println(ids);  // 输出:"张三,李四,王五"

 

lambda的map操作,也是返回Stream;其中原本List指定的类型是MdProIndexExt类,经过map转化以后为String类型,这是因为getProBatchId方法返回的是String类型,其中双冒号“::”是lambda的操作简写,前面是类名,后面是方法名
Stream<String> streamToString = stream.map(MdProIndexExt::getProBatchId);  等价于  Stream<String> streamToString = stream.map(p -> p.getProBatchId());

 

返回Set
Set<String> setString = streamToString.collect(Collectors.toSet());

返回map,其中t.getRouteId()对应map的key,p.getRouteName()对应map的value,如果key有重复则会报错,使用(k1, k2) -> k1处理,最终返回Map具体的类型也可以指定,这里是HashMap,key和value不能为空,后面两个参数可以为空
Map<String, String> mapStream = stream.collect(Collectors.toMap(t -> t.getRouteId(), p -> p.getRouteName()getRouteId(), (k1, k2) -> k1, HashMap::new)); 

 

 forEach处理List集合

List<MdProIndexDetailExt> detList = Lists.newArrayList();
detList.forEach(p -> p.setRowState(RowStateEnum.DELETED.getValue()));

 

forEach处理Map集合
Map<String, String> mapStream = stream.collect(Collectors.toMap(t -> t.getRouteId(), p -> p.getRouteName()getRouteId(), (k1, k2) -> k1, HashMap::new)); 
mapStream.forEach((key, value) -> System.out.println(key + "=" + value));

 

 List分组,key为getRouteName()的返回值,意思是将多个对象的routeName属性值相同则分为一组

List<MdProIndexExt> bdVos = Lists.newArrayList();

// 第二个参数不写则默认表示对象的List集合
Map<String, List<MdProIndexExt>> listToMap = bdVos.stream().collect(Collectors.groupingBy(MdProIndexExt::getRouteName));  

 

Collectors.counting()意思是将多个routeId属性值相同对象按照routeId分组后计数每个组的个数
Map<String, Long> listToMap = bdVos.stream().collect(Collectors.groupingBy(MdProIndexExt::getRouteId,Collectors.counting()));


Collectors.summingInt()求和,返回结果是每组所有对象的index属性相加,每组的key对应求和的结果
Map<String, Integer> listToMap = bdVos.stream().collect(Collectors.groupingBy(MdProIndexExt::getRouteId, Collectors.summingInt(MdProIndexExt::getIndex)));

多重分组
Map<String, Map<String, List<ReTrafficDetailExt>>> mapInMpaInList = trafficDetailExtList.stream().collect(
Collectors.groupingBy(ReTrafficDetailExt::getObservationDate,
Collectors.groupingBy(ReTrafficDetailExt::getDrivingDirectionCode)));

分区,将需要的数据与不需要的数据进行分区
// 将交通量中路线名称不为空的数据与其他数据分组两组,key为true这组则是想要的结果
Map<Boolean, List<ReTrafficDetailExt>> listInMap = trafficDetailExtList.stream().collect(Collectors.partitioningBy(p -> StringUtil.isNotEmpty(p.getRouteName())));

 

返回最大值的对象,有可能不存在,最终结果是key->MdProIndexExt,其中MdProIndexExt是每一组中行号(index)最大的对应的对象
Map<String, Optional<MdProIndexExt>> listToMap = bdVos.stream().collect(Collectors.groupingBy(MdProIndexExt::getRouteId, Collectors.maxBy(Comparator.comparing(MdProIndexExt::getIndex)))); 

 

 Stream.filter(),过滤数据,参数为true(符合实际要求)时则收集否则丢弃,这里是返回List集合

// 过滤掉路线名称为空的对象,保留路线不为空的数据
List<MdProIndexExt> ListInFliter = bdVos.stream().filter(p -> StringUtil.isNotEmpty(p.getRouteName())).collect(Collectors.toList());

 

Stream.findAny()随机返回一个对象;Stream.findFirst()返回第一个对象,使用Optional来接

// 随机返回一个对象
Optional<MdProIndexExt> findAny = bdVos.stream().filter(p -> StringUtil.isNotEmpty(p.getRouteName())).findAny();

// 返回第一个对象
Optional<MdProIndexExt> findFirst = bdVos.stream().filter(p -> StringUtil.isNotEmpty(p.getRouteName())).findFirst();

 

Optional.isPresent()返回boolean,判断对象是否存在
// 随机返回一个对象
Optional<MdProIndexExt> findAny = bdVos.stream().filter(p -> StringUtil.isNotEmpty(p.getRouteName())).findAny();

// 判断随机返回的对象存不存在,若存在findFirst.get()获取当前对象
if(findAny.isPresent()){
    System.out.println(findFirst.get().getRouteName());
}

 

Optional.ifPresent();无返回值,判断对象是否存在

// 返回第一个对象
Optional<MdProIndexExt> findFirst = bdVos.stream().filter(p -> StringUtil.isNotEmpty(p.getRouteName())).findFirst();

// 如果路线存在,则输出路线名称
findFirst.ifPresent(p -> System.out.println(p.getRouteName()));

 

Stream.allMatch(),判断条件是否满足所有对象
// 判断是否满足所有路线的路线名称都不为空,若是则返回true,否则返回false
boolean
isAllMatch = bdVos.stream().allMatch(p -> StringUtil.isNotEmpty(p.getRouteName()));

 

Stream.anyMatch()判断条件否满足一个对象;Stream.noneMatch()判断条件是否都不满足所有对象
// 判断是否存在一条路线的路线名称不为空,若存在则返回true,若所有路线的路线名称都为空着返回false
boolean isAllMatch = bdVos.stream().anyMatch(p -> StringUtil.isNotEmpty(p.getRouteName()));

// 如果所有路线的路线名称都为空则返回true,如果有一条路线的路线名称不为空则返回false,noneMatch()与anyMatch()相对
boolean isAllMatch = bdVos.stream().noneMatch(p -> StringUtil.isEmpty(p.getRouteName()));

 

lambda的计算,可以通过map转为特定的类型(int,long)之后再计算,也可以通过收集器统一计算(Collectors.summingInt(),Collectors.summinLong())

int min = trafficDetailExtList.stream().mapToInt(p -> DateUtil.getYear(stringToDate(p.getObservationDate()))).sum();  // 求和
OptionalDouble avg = trafficDetailExtList.stream().mapToInt(p -> DateUtil.getYear(stringToDate(p.getObservationDate()))).average();  // 求平均数
double sum = trafficDetailExtList.stream().collect(Collectors.summingDouble(p -> p.getEndStake().doubleValue()));  // 求和
double avg = trafficDetailExtList.stream().collect(Collectors.averagingDouble(p -> p.getEndStake().doubleValue()));  // 求平均数

 

lambda排序:lambda提供了另外一种sort排序方法:List.sort(),参数是Comparator 接口

// 传入两个参数p1,p2,命名随意看个人爱好,然后对这两个参数进行操作,可以多步操作但是返回值必须是int型
bdVos.sort((p1, p2) -> p1.getRowState().compareTo(p2.getRowState()));

 

lambda排序:另一个比较简洁的方式,Comparator实现方式简写,是上面传入两个参数(p1,p2)方式的简写,但是要求返回值是int型

bdVos.sort(Comparator.comparing(ReTrafficDetailExt::getRowState));

 

多个排序条件

List<VcUser> listUser = new ArrayList<VcUser>();
// 先根据真实姓名排序,再根据用户名排序,也提供了排序重载方式:(p1,p2) -> p1.compareTo(P2) listUser.sort(Comparator.comparing(VcUser::getRealName,
(p1, p2) -> p1.compareTo(p2)).thenComparing(VcUser::getUserName));

 

lambda处理最值方式:目前我使用到有3种情况:通过Map映射转化类型;通过收集器Collector集合;直接使用最值方法(max,min)

1.使用映射方式Map转为int类型,返回值OptionalInt

// 还有mapToLong();mapToDuble(),取决于方法返回值类型,最后面min()获取最小值,max()最大值,average()平均值,summing()求和
OptionalInt min = trafficDetailExtList.stream().mapToInt(p -> DateUtil.getYear(stringToDate(p.getObservationDate()))).min();

// min.getAsInt()获取到具体的值
min.ifPresent(p -> {System.out.println(min.getAsInt());});

 

2.通过Collector处理处理最值,返回值为Optional类型

// Collectors.maxBy()获取最大值
Optional<ReTrafficDetailExt> min = trafficDetailExtList.stream().collect(Collectors.minBy(Comparator.comparing(ReTrafficDetailExt::getYearAxleloadNum)));

 

3.通过直接使用max()方法

Optional<ReTrafficDetailExt> max = trafficDetailExtList.stream().max(Comparator.comparing(p -> DateUtil.getYear(stringToDate(p.getObservationDate()))));

 

跳过前面元素:Strream.skip()

@Test
public void skip(){
List<Integer> listNum = Stream.of(1,2,3,4,5,6).skip(2).collect(Collectors.toList());
listNum.forEach(System.out::println);
}
// 输出:3
     4
     5
     6

 

截取前几个元素:Stream.limit()

@Test
public void limit(){
    List<Integer> listNum = Stream.of(1,2,3,4,5,6).limit(2).collect(Collectors.toList());
    listNum.forEach(System.out::println);
}
// 输出:1
     2

 

归约操作:Stream.reduce()

@Test
public void reduce(){
   // reduce()=>param1:初始化参数的初始值,其中p1为初始化参数,p2为集合元素,计算过程是p1=p1*p2;最终结果赋值给p1并返回p1,p1的初始值为1 Integer listNum = Stream.of(1, 2, 3, 4, 5, 6).reduce(1, (p1, p2) -> p1 * p2); System.out.println(listNum); }
// 输出:720

 

了解Optional类,上面的一些操作也会返回Optional类型的值

Optional允许对象为空的容器,可使用ifPresent()或者ifPresent()判断对象是否为null,如下使用of()方法创建Optional

Optional<String> optionalString = Optional.of("hello world");
if (optionalString.isPresent()) {
System.out.println(optionalString.get());
}
optionalString.ifPresent(p-> System.out.println(optionalString.get()));

 

使用ofNullable()创建

Optional<String> optionalString = Optional.ofNullable("hello world");

 

ofNullable()与of()区别在于ofNullable()允许对象为null,of()不允许对象为null,get()是获取optional中的对象

Optional<String> optionalString = Optional.of(null);
if (optionalString.isPresent()) {
System.out.println(optionalString.get());
}
// 这里会报空指针错误
Optional<String> string = Optional.ofNullable(null);
if (string .isPresent()) {
    System.out.println(optionalString.get());
} else {    
  System.out.println("Optional对象为null");
}
// 输出:Optional对象为null

 

Optional的map()方法,将对象转化为其它类型,如果结果为null则返回空对象的optional,否则返回对应的内容

Optional<String> optionalString = Optional.of("hello world");
System.out.println(optionalString.map(String::toUpperCase).orElse("other"));  等价于  System.out.println(optionalString.map(p -> p.toUpperCase()).orElse("other"));
// 输出:HELLO WORLD
Optional<String> optionalString = Optional.empty();
System.out.println(optionalString.map(String::toUpperCase).orElse("other"));
// 输出:other

 

optional的orElse()方法,是判断optional的对象如果为null则返回orElse()方法的返回值,其返回值的类型必须与optional的泛型类型一致

Optional<String> optionalString = Optional.empty();
System.out.println(optionalString.orElse("other"))
// 输出:other

 

optional的orElseGet()方法与orElse()用法差不多,只不过orElseGet()的参数是Supplier接口,可以传入一个方法,看例子

private String doSomething(){
        return "hello new world";
    }
Optional<String> optionalString = Optional.empty();
System.out.println(optionalString.orElseGet(() -> doSomething()));
// 输出:hello new world

 

lambda还可以支持接口匿名简写,但是只能支持函数式接口(只能有一个抽象方法),先定义一个接口
package test;

public interface MyLambda {
  void doSomething();
}

 

函数式接口中可以包含静态方法和默认方法,可以增加注解@FunctionalInterface  

package test;

@FunctionalInterface public interface MyLambda {
  // 只能包含一个抽象方法 void doSomething();

  // 可以包含静态方法 static void staticFunction() { System.out.println("----->static function"); }
  // 可以包含默认方法 default void defaultFunction(){ System.out.println("--------->default function"); } }

 

函数式接口测试效果

package test;

import org.junit.Test;

public class ControllerTest{

    @Test
    public void testId(){
        testLambda(() -> System.out.println("--------->lambda function")); // 实现MyLambda接口,并重写doSomething()方法
    }

    private void testLambda(MyLambda lambda) {
        lambda.doSomething();
        System.out.println("----------->testLambda function");
    }

}
// 输出:
--------->lambda function

      ----------->testLambda function

 

流合并Stream.flatMap() 

@org.junit.Test
public void controllerTest() throws Exception {
    VcUser user1 = new VcUser();
    user1.setUserName("demo1");
    user1.setRealName("zhangsan");
    
    VcUser user2 = new VcUser();
    user2.setUserName("demo2");
    user2.setRealName("lisi");
    
    List<VcUser> list = new ArrayList<>();
    list.add(user1);
    list.add(user2);

    List<String> dd = list.stream().flatMap(p -> Stream.of(p.getUserName(), p.getRealName())).collect(Collectors.toList());
    dd.forEach(System.out::println);
}

 

 java8四大函数接口

——Function(函数):接受一个参数,返回一个值

private int addOne(int i) {
    return i + 1;
}
private int opera(int i, Function<Integer, Integer> function) {
    return function.apply(i);
}
@org.junit.Test
public void controllerTest(){
  // Function参数可以是表达式,但是必须返回一个值 int y = opera(1, p -> addOne(p)); System.out.println("y = " + y);
  int x = opera(4, p -> p + 1); // int x = opera(4, p -> {int x = 6; retrun x + p;} => 输出:10
  System.out.println("x = " + x);
}
// 输出:y = 2
     x = 5

 

——Consumer(消费者):接收一个参数,没有返回值

private void consumer(VcUser vcUser, Consumer<VcUser> consumer) {
    consumer.accept(vcUser);
}

 

@org.junit.Test
public void controllerTest() {
    VcUser vcUser = new VcUser();
    vcUser.setRealName("demo");

  // Consumer参数可以是表达式,但是不能有返回值 consumer(vcUser, p -> System.out.println("user = " +
p.getRealName())); }
// 输出:user = demo

 

 ——Supplier(提供者):不接收参数,返回一个值

private VcUser supplier(Supplier<VcUser> supplier) {
    return supplier.get();
}

 

@org.junit.Test
public void controllerTest() {
  // Supplier参数不接收参数,其中参数可以是表达式,但是必须有返回值 VcUser user = supplier(() -> { VcUser vcUser2 = new VcUser(); vcUser2.setRealName("demo"); return vcUser2; }); System.out.println("realName = " +
user.getRealName()); }
// 输出:realName = demo

 

——Predicate(谓语接口):接收一个参数,返回一个Boolean类型值,是一个特殊的Function(相当于Function<T,Boolean>)
private Boolean predicate(Integer i, Predicate<Integer> predicate) {
    return predicate.test(i);
}

 

@org.junit.Test
public void controllerTest() {
// Predicate接口接收一个参数,并返回boolean类型的返回值,predicate参数可以是表达式 boolean is = predicate(24, p -> { int i = 1; return p % 2 == i; }); System.out.println("is = " + is); }
// 输出:is = false

 

其它的接口都是上面四中接口的变种,一般是在限制参数类型,数量,具体如下信息:

 

——参数类型限制的接口

IntPredicate,LongPredicate, DoublePredicate,这几个接口,都是在基于Predicate接口的,不同的就是
他们的泛型类型分别变成了Integer,Long,Double,IntConsumer,LongConsumer, DoubleConsumer比如这几
个,对应的就是Consumer,Consumer,Consumer,其余的是一样的道理,就不再举例子了。

 

——返回值类型

和上面类似,只是命名的规则上多了一个To,例如IntToDoubleFunction,IntToLongFunction, 很明显就是对应的
Funtion;参数限制与返回值限制的命名唯一不同就是To,简单来说,前面不带To的都是参数类型限制,带To的是返回值类
型限制,对于没有参数的函数接口,那显而易见只可能是对返回值作限制。例如LongFunction就相当于Function<Long,R>
而多了一个To的ToLongFunction就相当于Function<T,Long>,也就是对返回值类型作了限制。

 

——数量限制接口

有些接口需要接受两名参数,此类接口的所有名字前面都是附加上Bi,是Binary的缩写,开头也介绍过这个单词了,是二元的
意思,例如BiPredicate,BiFcuntion等等,而由于java没有多返回值的设定,所以Bi指的都是参数为两个。

 

——Operator接口

此类接口只有2个分别是UnaryOperator 一元操作符接口,与BinaryOperator二元操作符接口,这类接口属于Function接口的简写,
他们只有一个泛型参数,意思是Funtion的参数与返回值类型相同,一般多用于操作计算,计算 a + b的BiFcuntion如果限制条件为
Integer的话 往往要这么写BiFunction<integer,integer,integer> 前2个泛型代表参数,最后一个代表返回值,看起来似乎是
有点繁重了,这个时候就可以用BinaryOperator来代替了。

 

接口函数如下

 

 

java尾递归,在java编程中,如果使用递归操作数据,容易出现栈溢出的情况,这是因为每次调用下一轮递归的时候在栈都需要保存之前的变量,在没有到底之前,那些中间变量会一直保存着,因此每一次递归都需要开辟一个新的栈空间。使用java的lambda表达式可优化此问题。原理利用java8的惰性求值,使用一个变量,每次递归时都保存上一轮的结果,到了最后轮时,直接取出结果即可。

——设计一个函数式接口,用来控制递归的状态

public interface Tail<T> {

  // 用来连接每次一次递归的栈帧,返回下一个栈帧 Tail<T> apply();
  // 判断递归是否结束,默认递归还没有结束,具体判断可在工具类中重写 default boolean isFinished() { return false; }
  // 递归结束时获得最终值,这里默认是还没有结束,抛出异常,具体返回值在工具类中重写 default T getResult() throws BusinessException { throw new BusinessException("递归还没有结束..."); }
  // java8的及早求值,执行完一系列的递归后,因为栈帧只有一个,所以使用findFirst()获取最终的栈帧 default T invoke() throws BusinessException { return Stream.iterate(this, Tail::apply) .filter(Tail::isFinished) .findFirst() .get() .getResult(); } }

 

——尾递归的工具类

public class TailInvoke {

    public static <T> Tail<T> callBack(Tail<T> next) {
        return next;
    }

    private static <T> Tail<T> done(T value) {
        return new Tail<T>() {
            @Override
            public Tail<T> apply() {
                try {
                    throw new BusinessException("递归已经结束...");
                } catch (BusinessException e) {
                    e.printStackTrace();
                }
                return null;
            }

            @Override
            public boolean isFinished() {
                return true;
            }

            @Override
            public T getResult() {
                return value;
            }
        };
    }
}

 

——例子:1 + 2 + 3 + ... + 100

public static Tail<Long> calculate(final long factory, final long next) {
if (1 == next) {
return TailInvoke.done(factory + next);
} else {
return TailInvoke.callBack(() -> calculate(factory + next, next - 1)); // 调用invoke()时都重写了apply()方法,并将上一次的结果值保存在factory中,每次都将factory传到下一递归中
}
}
@org.junit.Test
public void controllerTest() throws Exception {
    long y = TailInvoke.calculate(0, 100).invoke();
    System.out.println("y = " + y);
}
// 输出:5050

 

 

 

 

使用经验:
1、lambda中的双冒号“::”操作都可以用箭头“->”代替,如User::getName 等价于 user->user.getName();
2、使用箭头操作相对灵活一点,可以改变返回值类型,如user.getName().getBytes()返回的是byte[];
3、"->"后面可以跟着“{}”代码块,如user->{return user.getName();};
4、从外面传到lambda方法体时,必须是final型,否则编译不过去;

 



 
 

 

posted @ 2018-01-16 22:33  antlord  阅读(1267)  评论(0编辑  收藏  举报