Java lambda&Stream


1. lambda

Lambada 简介:
Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。

Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。使用 Lambda 表达式可以使代码变的更加简洁紧凑

记住:函数作为参数传递进方法中
两个东西:函数参数、方法

示例:

匿名类写法
new Thread(new Runnable(){
	@Override
	public void run(){
	System.out.println("hello");
	}	
}).start();

Lambada写法
new Thread(()->System.out.println("hello")).start();

上述例子:
方法:Thread().start();
函数参数:()->System.out.println(“hello”)
这个函数参数实际上是Runnable中的run函数

编译器会将 “System.out.println(“hello”)” 编译成Runnable.run 的执行指令。
可代码中我们并没有指明Run方法,这是因为 run 方法是Runnable接口的唯一方法,也就是说如果Runable有多个方法是不能使用Lambada表达示的,这种支持Lambada的接口统称函数式接口。

函数参数的写法:

() -> {}

    ():接口方法的括号,接口方法如果有参数,也需要写参数。若只有一个参数时,括号可以省略。
    -> : 分割左右部分。
    {} : 要实现的方法体。只有一行代码时,可以不加括号,可以不写return。

1.1 函数式接口

必须是 函数式接口 才可以使用lambada 表达示 ,函数式接口笼统的讲就是只有一个抽像方法接口就是函数式接口,其详细特征如下:

  • 接口中只有一个抽像方法 会被编译器自动认识成函数式接口
  • 有多个方法,但是Object类提供的方法和default方法除外

1.2 表达式编写方法

expression/ɪkˈspreʃn/:单条语句表达式
statement:语句块
reference:方法引用

package com.lrm.web;

public class Test {
    public static void main(String[] args) {
        //参数声明
        creat((a,b)->a+b);

        //单行语句块:必须要有结果
        creat((a,b)->a+b);

        //多行语句块
        creat((a,b)->{
            System.out.println("1");
            return a+b;
        });

        //静态引用
        creat(Test::staticMethod);//引用Test类下的static_me方法
        //非静态引用
        creat(new Test()::notStatic);
        //工具类方法引用
        creat(String::concat);
    }

    private String notStatic(String name,String message){
        return name+message;
    }

    private static String staticMethod(String name, String message){
        return name+message;
    }

    private static void creat(myInter myInter){
        myInter.build("11","22");
    }

    public interface myInter{
        String build(String name, String message);
    }

}

2. Stream

2.1 Stream介绍

java8 中的stream 与InputStream和OutputStream是完全不同的概念, stream 是用于对集合迭代器的增强,使之完成 能够完成更高效的聚合操作(过滤、排序、统计分组)或者大批量数据操作。此外与stream 与lambda 表达示结合后编码效率与大大提高,并且可读性更强。

示例展示:

// 获取所有红色苹果的总重量
appleStore.stream().filter(a -> "red".equals(a.getColor()))
.mapToInt(w -> w.getWeight()).sum()


// 基于颜色统计平均重量
appleStore.stream().collect(Collectors.groupingBy(a -> a.getColor(),
        Collectors.averagingInt(a -> a.getWeight()))).forEach((k, v) -> {
    System.out.println(k + ":" + v);
});

stream 产生背景
‘获取所有红色苹果的总重量’,如果用SQL其实非常好实现,为什么不在直接关系数据库来实现呢?

//获取所有红色苹果的总重量
select sum(a.weight) from apple as a  where a.color='red';
// 基于颜色分组统计重量
select a.color,sum(a.weight) from apple as a group by color;

遍历在传统的javaEE 项目中数据源比较单而且集中,像这类的需求都我们可能通过关系数据库中进行获取计算。但现在的互联网项目数据源成多样化有:关系数据库、NoSQL、Redis、mongodb、ElasticSearch、Cloud Server 等。这时就需我们从各数据源中汇聚数据并进行统计。这在Stream出现之前只能过遍历实现 非常繁琐。

Stream可以解决多数据源的数据操作相关问题

场景:跨库join的问题

查询一个店铺的订单信息,需要用到订单表与会员表 在传统数据库单一例中 可以通过jon 关联轻松实现,但在分布场景中 这两张表分别存储在于 交易库 和会员库 两个实例中,join不能用。只能在服务端实现其流程如下:

查询订单表数据
找出订单中所有会员的ID
根据会员ID查询会员表信息
将订单数据与会员数据进行合并
这用传统迭代方法非常繁琐,而这正是stream 所擅长的。示例代码如下:

// 获取所有会员ID 并去重
List<Integer> ids = orders.stream().map(o -> o.getMemberId()).distinct().collect(Collectors.toList());
//  合并会员信息 至订单信息
orders.stream().forEach(o -> {
    Member member = members.stream().filter(m -> m.getId() == o.getMemberId()).findAny().get();
    o.setMemberName(member.getName());
});

2.2 流的三种操作

2.2.1 生成流

  1. Stream.of("1","2","3");
    
  2. 处理容器
    Collecton.stream();
    
  3. 处理数组
    Arrays.stream()
    
    

2.2.2 中间操作

非静态、返回Stream的方法,都是中间操作(惰性操作)
惰性操作:如果没有终值操作,中间操作不会被执行

2.2.3 终值操作

非中间操作的都是终值操作,只能有一个且为最后一个,Stream的结果为终值方法的返回值

2.3 流的执行顺序

public void test(){
	appleStore.stream(){
		.peek(a->System.out.println(a.getColor()))//打印颜色
		.peek(a->System.out.println(a.getOrigin()))//打印产地
		.toArray();
}

上述代码相当于

public void test(){
	for(Apple apple : appleStore){
		System.out.println(a.getColor());//打印颜色
		System.out.println(a.getOrigin());//打印产地
}

因此执行顺序为:
以对象为基本单位,依次操作

2.4 IDEA可视化Stream

在这里插入图片描述
debug的时候点击:

在这里插入图片描述

2.5 知识汇总(※)

示意图
在这里插入图片描述
操作特性

  1. 不存储数据
  2. 不改变数据源
  3. 不可重复使用(对一个流进行操作之后,要么生成新的流继续操作,要么终值操作)

流的操作类型
stream 所有操作组合在一起即变成了管道,管道中有以下两种操作:

  • 中间操作(intermediate /,ɪntə’miːdɪət/): 调用中间操作方法会返回一个新的流。通过连续执行多个操作倒便就组成了Stream中的执行管道(pipeline)。需要注意的是这些管道被添加后并不会真正执行,只有等到调用终值操作之后才会执行。
  • 终值操作(terminal /'tɜːmɪn(ə)l/): 在调用该方法后,将执行之前所有的中间操作,获返回结果结束对流的使用
    流的执行顺序说明:其每个元素挨着作为参数去调用中间操作及终值操作,而不是遍历完一个方法,在遍历下一个方法。

流的并形操作
调用Stream.parallel() 方法可以将流基于多个线程并行执行

流的生成
Collection#stream
Arrays#stream
Stream#Stream
Stream#generate

Stream 中的常用API及场景

方法描述操作类型
filter接收一个Boolean表达示来过滤元素中间操作
map将流中元素 1:1 映谢成另外一个元素中间操作
mapToInt将流中元素映谢成int,mapToLong、mapToDouble操作类似目的减少 装箱拆箱带来的损耗中间操作
flatMap如map时返回的是一个List, 将会进一步拆分。详见flatMap示例中间操作
forEach遍历流中所有元素终值操作
sorted排序中间操作
peek遍历流中所有元素 ,如forEach不同在于不会结束流中间操作
toArray将流中元素转换成一个数组返回终值操作
reduce归约合并操作中间操作
collect采集数据,返回一个新的结果 参数说明 Supplier<R>: 采集需要返回的结果BiConsumer<R, ? super T>:传递结果与元素进行合并。BiConsumer<R, R>:在并发执行的时候 结果合并操作。详见 collec示例终值操作
distinct基于equal 表达示去重中间操作
max通过比较函数 返回最大值终值操作
anyMatch流中是否有任一元素满足表达示终值操作
allMatch流中所有元素满足表达示返回true终值操作
noneMatch与allMatch 相反,都不满足的情况下返回 true终值操作
findFirst找出流中第一个元素终值操作
of生成流生成流操作
iterate基于迭代生成流生成流操作
generate基于迭代生成流,与iterate 不同的是不 后一元素的生成,不依懒前一元素生成流操作
concat合并两个相同类型的类生成流操作

示例


 @Test
    public void filterTest() {
        appleStore.stream().filter(a -> a.getColor().equals("red")).forEach(a -> {
            System.out.println(a.getColor());
        });
    }

    @Test
    public void mapTest() {
        appleStore.stream().map(a -> a.getOrigin()).forEach(System.out::println);
    }

    @Test
    public void flatMapTest() throws IOException {
        Stream<String> lines = Files.lines(new File("G:\\git\\tuling-java8\\src\\main\\java\\com\\tuling\\java8\\stream\\bean\\Order.java").toPath());
        lines.flatMap(a -> Arrays.stream(a.split(" "))).forEach(System.out::println);
    }

    @Test
    public void sortedTest() {
        appleStore.stream().sorted((a, b) -> a.getWeight() - b.getWeight())
                .map(a -> a.getWeight()).forEach(System.out::println);
    }

    @Test
    public void peekTest() {
        appleStore.stream().peek(a -> {
            System.out.println(a.getId());
        }).map(a -> a.getOrigin())
                .peek(System.out::println).forEach(a -> {
        });
    }

    @Test
    public void reduceTest() {
        // 找出最重的那个苹果
        appleStore.stream().reduce((a, b) -> a.getWeight() > b.getWeight() ? a : b)
                .ifPresent(a -> {
                    System.out.println(a.getWeight());
                });
    }

    @Test
    public void collectTest() {
        // 将结果转换成id作为key map<Integer,Apple>
        HashMap<Integer, Apple> map = appleStore.stream().collect(HashMap::new, (m, a) -> m.put(a.getId(), a), (m1, m2) -> m1.putAll(m2));
        map.forEach((k, v) -> {
            System.out.println(k);
            System.out.println(v);
        });
        // Map<String,List<Apple>>
        //  基于颜色分组, 并获取其平均重量


    }

Collectors 中的常用API及场景

方法描述
toList转换成list
toMap转换成map
groupingBy统计分组
averagingInt求平均值
summingInt求总值
maxBy获取最大值

Collectors 使用例子

 // 获得所有颜色苹果的平均重量
    @Test
public void groupByTest() {
        Collector<Apple, ?, Map<String, Double>> groupCollect =
                Collectors.groupingBy((Apple a) -> a.getColor(), Collectors.averagingInt((Apple a) -> a.getWeight()));
        appleStore.stream().collect(groupCollect).forEach((k, v) -> {
            System.out.println(k + ":" + v);
        });
    }

流的关闭机制
一般情况使用完流之后不需要调用close 方法进行关闭,除非是使用channel FileInputStream 这类的操作需要关闭,可调用 java.util.stream.BaseStream#onClose() 添加关闭监听.

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值