Lambda表达式与Stream

Lambda表达式

1.出现的背景

java是面向对象的语言,除了部分简单数据类型,java中的一切都是对象,即使是数组也是一种对象,每个类创建的实例都是对象.在java中定义的方法不可能完全独立,也不可能将方法作为一个参数或作为返回值给其他方法来使用.

在java 8以前,我们要想把某些功能传递给某一个方法,总是需要去写一个(匿名)内部类.

public class Car {

    private int num;
    private String name;

    public Car(int num, String name) {
        this.num = num;
        this.name = name;
    }

    public int getNum() {
        return num;
    }

    @Override
    public String toString() {
        return "Car{" +
                "num=" + num +
                ", name='" + name + '\'' +
                '}';
    }
}
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;

public class Test {

    public static void main(String[] args) {

        Car car0 = new Car(100, "宝马0");
        Car car1 = new Car(101, "宝马1");
        Car car2 = new Car(102, "宝马2");
        Car car3 = new Car(103, "宝马3");
        Car car4 = new Car(104, "宝马4");

        ArrayList<Car> arrayList = new ArrayList<>();
        Collections.addAll(arrayList, car2,car4,car1,car3,car0);

        //将比较功能传递给sort方法
        Collections.sort(arrayList, new Comparator<Car>() {
            @Override
            public int compare(Car o1, Car o2) {
                return o1.getNum()-o2.getNum();
            }
        });

        //按照升序输出集合
        System.out.println(arrayList);

    }
}

在上面的例子里,为了对集合进行排序,我们为Comparator接口创建了它的一个匿名内部类对象,重写接口中的方法,来实现排序的功能.

简而言之,在java里将普通的方法像参数一样传值并不简单,为此,java 8增加了一个语言级的新特性—Lambda表达式.

2.Lambda简介

Lambda表达式是一个匿名函数,我们可以把Lambda表达式理解为一段可以传递的代码(将代码像数据一样可以传递),使用它可以写出更简洁,更灵活的代码.作为一种更紧凑的代码风格,使java语言的表达能力得到提升.

Lambda表达式的本质是一个"语法糖’',由编译期推断并帮你转换包装为常规的代码,因此你可以使用更少的代码来实现同样的功能.

3.Lambda表达式的结构

Lambda表达式可以有零个,一个或者多个参数.

可以显式声明参数的类型,也可以由编译器自动从上下文推断参数的类型.例如(int a,int b)与(a,b)相同.参数用小括号括起来,用逗号分割.空括号表示一组空的参数.

当且仅有一个参数的时候,如果不显式指明类型,则不必使用小括号.例如a -> return a*a.

Lambda表达式的正文可以包含零条,一条或多条语句.如果只有一条语句,那么大括号可以不用写(推荐不论什么情况都加上花括号),表达式的返回值类型要与匿名函数的返回类型相同.

java中的Lambda表达式通常用**(argument) -> {body}**语法书写.

比如我们上面的代码可以更改一下:

//因为Comparator接口被@FunctionalInterface标注(功能函数接口),因此可以使用Lambda表达式表示
        Collections.sort(arrayList, ((o1, o2) ->{
            return o1.getNum()-o2.getNum();
        }));

这样就更为简单快捷了.我们自己创建的接口也是可以使用Lambda表达式来进行简化语句的.

public interface Demo {

    /*
        要是这个接口能够使用Lambda表达式来表示,接口中只允许有一个抽象方法
     */

    //无参且无返回值的方法
    void show();
}
public class Test {

    public static void test(Demo demo){
        //如果我们调用了test方法,test方法调用了接口中的show方法,需要重写这个方法
        demo.show();
    }

    public static void main(String[] args) {

        Test.test(new Demo() {
            @Override
            public void show() {
                System.out.println("重写了接口中的show方法");
            }
        });

    }
}

我们必须确定我们的接口中只有一个抽象方法才能使用,如果有两个抽象方法,我们使用Lambda表达式的时候会报错.即使两个方法的参数不相同也不可以.

Test.test(()->{
            System.out.println("重写的show方法");
        });

4.功能接口FunctionalInterface

**Lambda表达式只支持函数式接口.也就是只有一个抽象方法的接口.功能接口是java 8中的新增功能,他们只允许一个抽象方法.这些接口也称为单抽象方法接口.java 8也引入了一个注释,即@FunctionalInterface,**当你的接口违反了它的契约(也就是说不是仅有一个抽象方法),他可以用于编译期级错误.

一个最简单的,我们之前提到过的线程,如果只是一个简单的功能,那我们没有必要直接去写一个线程.直接使用Lambda表达式即可:

public class ThreadDemo {

    public static void main(String[] args) {

        new Thread(()->{
            for (int i = 0; i < 10; i++) {
                System.out.println(i);
            }
        }).start();

    }
}

Stream

1.什么是Stream

Stream是java 8的新特性,它允许你以声明式的方式处理数据集合,不需要我们自己写for循环进行遍历等操作.可以把他看做是遍历数据集的高级迭代器.此外与stream与lambda结合后编码效率可以大大提高,并且可读性也更强.

要清楚的是这里的stream与我们之前IO讲到的InputStream和OutputStream是完全不同的两个概念.

那到底什么是流.简单的定义,就是**“从支持数据处理操作的源生成的元素序列.”**

元素列表:和集合一样,流也提供了一个接口,访问特定元素类型的一组有序值.

数据源:获取数据的源,比如集合.

数据处理操作:流更偏向于数据处理和计算.比如filter,map,find,sort等.

简单来说,我们通过一个数据集合的stream方法获取一个流,然后对这个流进行一系列的流操作.最后再构成我们需要的数据集合.

2.如何获取流

要利用stream对数据集合进行操作,第一步一定是获取流.我们之前学过的数据集合暂时来说只有两个(数组和集合).

import java.util.ArrayList;
import java.util.Arrays;
import java.util.stream.Stream;

//获取流的方法
public class Demo {

    public static void main(String[] args) {

        //使用Collection接口下的Stream()
        //Collection接口定义了Stream方法,意味着List集合与Set集合都实现了这个方法
        ArrayList<Integer> arrayList = new ArrayList<>();
        Stream<Integer> stream = arrayList.stream();

        //使用Arrays类中的Stream方法,将数组转换为流
        Integer[] a = new Integer[3];
        Stream<Integer> stream1 = Arrays.stream(a);

        //我们也可以直接把元素当做参数传入方法中
        //of方法的参数是可变长度参数
        Stream<Integer> stream2 = Stream.of(1,2,3,6,5,8);

    }
}

这里一定要注意.我们的数组里面应该存储的是引用类型的数据.

3.如何对流进行操作

我们对流进行操作都是声明式的,我们只要说明想完成什么(筛选,排序,取值),而不是说明如何实现一个操作(利用for循环或者迭代器遍历),我们将这些操作连接起来,达到一种流水线式的效果.

流操作可以分为两类:**中间操作和终端操作.**终端操作的作用是将流关闭,构建新的数据集合对象(也可以不构建).

import java.util.Arrays;

public class Demo1 {

    public static void main(String[] args) {

        Integer[] integers = {9,5,1,3,7,8,2,6,4};

        Arrays.stream(integers)//获取流
              .distinct()//去重
              .filter((integer) -> {return integer < 6;})//对流进行过滤,只需要小于6的元素,返回一个流
              .sorted((a,b)->{return a-b;})//对流进行排序,返回一个流
              .limit(4)//只需要取出4个元素
              .skip(1)//跳过第一个元素
              .forEach((integer) -> {
                  System.out.println(integer);
              });//对这个流遍历输出

    }
}

上面就是简单的对数组这种数据集合进行流操作.不需要我们写for循环来实现这些,我们只需要声明即可.

我们看看常用的中间操作还有终端操作都有哪些

常用的中间操作:

filter();//过滤流中的某些元素

sorted();//自然排序,流中的元素须实现Comparable接口

distinct();//去除重复元素

limit(n);//获取n个元素

skip(n);//跳过n个元素,配合limit可以实现分页

map();//将其映射成为一个新的元素

常用的终端操作:

forEach();//遍历流中的元素

toArray();//将流中的元素传入一个数组

collect();//将流中的元素传入一个集合

Min();//返回流中元素的最小值

Max();//返回流中元素的最大值

count();//返回流中元素的总个数

reduce();//所有元素求和

anyMatch();//接收一个Predicate函数,只要流中有一个元素满足条件返回true,否则返回false

allMatch();//接收一个Predicate函数,流中所有元素满足条件返回才true,否则返回false

findFirst();//返回流中的第一个元素

对于对流的操作的一些小问题,我们通过几个代码示例可以轻松搞定.

import java.util.Arrays;

public class Demo2 {

    public static void main(String[] args) {

        Integer[] integers = {9,5,1,3,7,8,2,6,4};

        Integer integer = Arrays.stream(integers)
                                .distinct()
                                .max((a, b) -> { return a - b; })  //max方法返回的是一个Optional对象
                                .get();//调用get方法来得到我们想要的最大值
        System.out.println(integer.intValue());

    }
}
import java.util.Arrays;

public class Demo3 {

    public static void main(String[] args) {

        Integer[] integers = {9,5,1,3,7,8,2,6,4};

        Arrays.stream(integers)
              .reduce((a,b)->{return a+b;})
              .get();//依旧需要调用get方法

        boolean b = Arrays.stream(integers)
                          .anyMatch((integer)->{return integer > 10;});
        System.out.println(b);
        
    }
}

当然这些就是比较简单的,我们通过看源码或者查阅API结合Lambda表达式是完全可以掌握的.

import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.Map;
import java.util.stream.Collectors;

public class TestCar {

    public static void main(String[] args) {

        Car car1 = new Car(101, "蓝色", "宝马1");
        Car car2 = new Car(102, "黑色", "宝马2");
        Car car3 = new Car(103, "白色", "宝马3");
        Car car4 = new Car(104, "绿色", "宝马4");
        Car car5 = new Car(105, "红色", "宝马5");

        ArrayList<Car> arrayList = new ArrayList<>();
        Collections.addAll(arrayList,car2,car4,car3,car5,car1);

        Map<Integer,String> map =
        arrayList.stream()
                 .sorted((c1,c2)->{return c1.getNum()-c2.getNum();})
                 //利用车的号码以及颜色构成一个键值对,将所有对象封装为一个map集合
                 .collect(Collectors.toMap(Car::getNum,Car::getColor));
        System.out.println(map);

        //注意我们中间操作的map与集合的map并不一样,这个map是一个映射的意思
        Object[] nums = arrayList.stream()
                              .map(Car::getName)
                              .toArray();
        //这个数组存储了所有car对象的num信息
        System.out.println(Arrays.toString(nums));
    }
}
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值