java初学者笔记24 —— lambda表达式与流处理

lambda表达式与流处理

lambda表达式简介

lambda表达式可以用非常少的代码实现抽象方法。lambda表达式不能独立运行,因此必须实现函数式接口,并且返回一个函数式接口对象。

// 语法格式如下
()->结果表达式                             // 无参方法
参数 -> 结果表达式                          // 只有一个参数方法
(参数1,参数2,……,参数n) -> 结果表达式         // 实现多个参数方法

lambda表达式也可以实现复杂方法,右侧表达式换成代码块就可以了

() -> { 代码块 }
参数 -> { 代码块 }
(参数1,参数2,……,参数n) ->{ 代码块 }

lambda表达式的语法非常抽象,但有着很强大的自动化功能,如自动识别泛型、自动数据类型转换等。

总结:

​ () -> { 代码块 }

这个方法 按照 这样的代码来实现

lambda表达式实现函数接口

函数式接口

函数式接口指的是仅包含一个抽象方法的接口,接口中的方法简单明了地说明了接口的用途。

// 例子
interface MyInterface{
    void method();
}

lambda表达式实现无参抽象方法

很多函数式接口的抽放方法是无参的,如run()

// 使用lambda表达式实现打招呼接口
interface SayHiInterface{
    String say();
}
public class NoParamterDemo {
    public static void main(String[] args) {
        // lambda 表达式实现打招呼接口,返回抽象方法结果
        SayHiInterface pi = ()->"你好啊,这是lambda表达式";
        System.out.println(pi.say());
    }
}

lambda表达式实现有参抽象方法

// 使用lambda表达式实现加法计算
interface AdditionInterface{
    int add(int a,int b);
}
public class Paramter {
    public static void main(String[] args) {
        // lambda表达式实现加法接口,返回相加的值
        AdditionInterface np = (x,y) -> x + y;
        int result = np.add(15,26);
        System.out.println("相加的结果为" + result);
    }
}

lambda表达式使用代码块

// 使用lambda表达式为考试成绩分类
interface CheckGrade{
    String check(int grade);
}
public class GradeDemo {
    public static void main(String[] args) {
        CheckGrade g = (n) -> {
            if(n >= 90 &&n <= 100)
                return "成绩为优";
            else if(n >= 80 && n <= 90)
                return "成绩为良";
            else if(n >= 60 && n <= 80)
                return "成绩为中";
            else
                return "成绩为差";

        };
        System.out.println(g.check(89));
    }
}

lambda表达式调用外部变量

lambda表达式除了可以调用定义好的参数,还可以调用表达式以外的变量。

注意这些变量的值有些可以被修改,有些却不能被修改。例如:lambda表达式不能修改局部变量的值,可以修改外部成员变量的值。

lambda表达式子无法更改局部变量

// 使用lambda表达式修改局部变量(不行)
interface VariableInterface1 {
    void method();
}
public class VariableDemo1 {
    public static void main(String[] args) {
        int value = 100;
        VariableInterface1 v = () -> {
// 程序报错
//            int num  = value - 90;
//            value = 12;
        };
    }
}

lambda表达式可以修改成员变量

// 使用lambda表达式修改类成员变量
interface VariableInterface2{
    void method();
}
public class VariableDemo2 {
    int value = 100;
    public void action(){
        VariableInterface2 v = () -> {
            value = -12;
        };
        System.out.println("运行接口方法之前value=" + value);
        v.method();
        System.out.println("运行接口方法之后value=" + value);
    }

    public static void main(String[] args) {
        VariableDemo2 demo = new VariableDemo2();
        demo.action();
    }
}

lambda表达式与异常处理

很多接口的抽象方法为了保证程序的安全性,会在定义时就抛出异常。但是在lamdba表达式中没有抛出异常的语法,因为lambda表达式会默认抛出抽象方法原有的异常。

// 使用lambda表达式实现防沉迷接口
import java.util.Scanner;
interface AntiaddictInterface{                        // 防沉迷接口
    boolean check(int age) throws UnderAgeException;  // 抽象检查方法,抛出用户未成年人异常
}
class UnderAgeException extends Exception{            // 自定义未成年人异常
    public UnderAgeException(String message){         // 有参构造方法
        super(message);                               // 调用原有父类构造方法
    }
}
public class ThrowExceptionDemo {                     // 测试类
    public static void main(String[] args) {
        // lambda表达式创建AntiaddictInterface对象,默认抛出原有异常
        AntiaddictInterface ai = (a) -> {
            if(a < 18){
                throw new UnderAgeException("未满18岁,开启防沉迷模式!");
            }else{
                return true;
            }
        };
        Scanner sc = new Scanner(System.in);           // 创建控制塔扫描器
        System.out.println("请输入年龄:");              // 控制台提示
        int age = sc.nextInt();                       // 获取用户输入年龄

        try {                                          // 因为接口方法抛出异常,所以此处必须捕捉异常
            if(ai.check(age)){                         // 验证年龄
                System.out.println("欢迎进入xx世界");
            }
        } catch (UnderAgeException e) {
            System.err.println(e);                     // 控制台打印异常报告
        }
        sc.close();                                    // 关闭扫描器
    }
}

方法的引用

引用静态方法

语法如下

类名::静态方法名

例子
// 使用lambda表达式引用静态方法
interface StaticMethodInterface{
    int method(int a,int b);
}
public class StaticMethodDemo {
    static int add(int x,int y){
        return x + y;
    }

    public static void main(String[] args) {
        StaticMethodInterface sm = StaticMethodDemo::add;
        int result = sm.method(15,16);
        System.out.println("接口方法结果:" + result);
    }
}

引用成员方法

语法如下

类名::静态方法名

例子
// 使用lambda表达式引用成员方法
import java.text.SimpleDateFormat;
import java.util.Date;
interface InstanceMethodInterface{
    String method(Date date);
}
public class InstanceMethodDemo {
    public String format(Date date){
        // 创建日期格式化对象,并指定日期格式
        SimpleDateFormat sdf = new SimpleDateFormat("yyyy-MM-dd");
        return sdf.format(date);
    }

    public static void main(String[] args) {
        InstanceMethodDemo demo = new InstanceMethodDemo();
        InstanceMethodInterface im;
        // 有错?
        //im = new demo::format;

        Date date = new Date();
        System.out.println("默认格式:" + date);
        //System.out.println("接口输出的格式:" + im.method(date));
    }
}

引用带泛型的方法

泛型是java开发经常使用到的功能,“::”操作符支持引用带泛型的方法。除方法外,“::”操作符也支持引用带泛型的类

例子
// 使用lambda表达式引用带泛型的方法
import java.util.HashSet;
interface ParadigmInterface<T>{
    int method(T[] t);
}
public class ParadigmDemo {
    // 静态方法,使用泛型参数,在方法名之前定义泛型。此方法用于查找数组中的重复元素个数
    static public <T> int repeatCoount(T[] t){
        int arrayLength = t.length;
        java.util.HashSet<T>set = new HashSet<>();
        for(T tmp:t){
            set.add(tmp);
        }
        return arrayLength - set.size();
    }

    public static void main(String[] args) {
        Integer a[] = {1,1,2,3,1,5,6,1,8,8};
        String s[] = {"王","李","赵","陈","李","孙","张"};
        // 有错
        // 创建接口对象,Integer作为泛型,引入ParadigmDemo类的静态方法,方法名药定义泛型
        //ParadigmInterface<Integer> p1 = new ParadigmDemo::<Integer> repeatCoount;
        // 创建接口对象,String作为泛型,引入ParadigmDemo类的静态方法
        // 方法名若不定义泛型,则默认使用接口已定义好的泛型
        ParadigmInterface<String> p2 = ParadigmDemo::repeatCoount;
        System.out.println("字符串数组重复元素个数:" + p2.method(s));
    }
}

引用构造方法

引用无参数构造方法

语法如下

类名::new

例子
// 使用lamdba表达式引用无参构造方法
interface ConstructorsInterface1{
    ConstructorsInterface1 action();
}
public class ConstructorsDemo1 {
    public ConstructorsDemo1(){
        System.out.println("调用无参数的构造方法");
    }
    public ConstructorsDemo1(int i){
        System.out.println("调用有参数的构造方法");
    }

    public static void main(String[] args) {
// 报错
//        ConstructorsInterface1 a = ConstructorsDemo1::new;
//        ConstructorsDemo1 b = a.action();
    }
}

引用有参数的构造方法

例子
// 使用lambda表达式引用有参数的构造方法
interface ConstructorInterface2{
    ConstructorInterface2 action(int i);
}
public class ConstructorsDemo2 {
    public ConstructorsDemo2(){
        System.out.println("调用无参构造方法");
    }
    public ConstructorsDemo2(int i){
        System.out.println("调用有参数的构造方法,参数为:" + i);
    }

    public static void main(String[] args) {
// 报错
//        ConstructorInterface2 a = ConstructorsDemo2::new;
//        ConstructorsDemo2 b = a.action(123);
    }
}

引用数组构造方法

语法如下

类名[]::new

例子
// 使用lambda表达式引用数组的构造方法
interface ArraysConsInterface<T>{
    // 抽象方法返回对象数组,方法参数决定数组个数
    T action(int n);
}
public class ArraysConsDemo {
    public static void main(String[] args) {
        // 引用数组构造方法
        ArraysConsInterface<ArraysConsDemo[]> a = ArraysConsDemo[]::new;
        ArraysConsDemo array[] = a.action(3);
        array[0] = new ArraysConsDemo();
        array[1] = new ArraysConsDemo();
        array[2] = new ArraysConsDemo();
        // 如果调用或给array[3]赋值,代码则会抛出下标越界异常
    }
}

Fuction接口

java.utilfunction包已经提供了很多预定义函数式接口,就是没有实现任何功能,仅用来封装lambda表达式对象。该包中最常用的接口是Funtion<T,R>接口。

方法功能说明方法返回值
apply(T t)抽象方法。按照被子类实现的逻辑,执行函数。参数为被操作泛型对象R
addThen(Function<? super R,?extends V>after)先执行apply(t)方法,将执行结果作为本方法的参数,再按照after函数逻辑继续执行(T t) ->after.apply(apply(t))
compose(Function<? super V,?extends T>before)先按照before函数逻辑操作接口的被操作对象t,再将执行结果作为apply()方法的参数(V v) -> apply(before.apply(v))
static identity()此方法是静态方法。返回一个Function对象,此时对象apply方法只会返回参数值t -> t
例子
// 使用lambda表达式拼接IP地址
import java.util.function.Function;

public class FunctionDemo {
    // 创建Function接口对象,参数类型是integer[],返回值类型是String
    Function<Integer[],String> function = (n) -> {
        StringBuilder str = new StringBuilder();
        for(Integer num : n){                   // 遍历输出ip地址
            str.append(num);
            str.append('.');
        }
        str.deleteCharAt(str.length() - 1);
        return str.toString();
    };

    public static void main(String[] args) {
        Integer[] ip = {192,168,1,1};
        FunctionDemo demo = new FunctionDemo();
        System.out.println(demo.function.apply(ip));
    }
}

流处理

流处理语句有点类似数据库的SQL语句,可以执行非常复杂的过滤、映射、查找和收集功能,并且代码很少。唯一缺点是代码可读性不高。

例子
// 创建员工类,并按照表创建初始化数据,并将其全部放入ArrayList列表中
import java.util.ArrayList;
import java.util.List;
public class Employee {
    private String name;
    private int age;
    private double salary;
    private String sex;
    private String dept;
    // 构造方法

    public Employee(String name, int age, double salary, String sex, String dept) {
        this.name = name;
        this.age = age;
        this.salary = salary;
        this.sex = sex;
        this.dept = dept;
    }
    // 重写toString方法,方便打印员工信息
    @Override
    public String toString() {
        return "name=" + name + ",age=" + age + ",salary=" + salary + ",sex=" + sex + ",dept=" + dept;
    }
    // 员工get()方法

    public String getName() {
        return name;
    }

    public int getAge() {
        return age;
    }

    public double getSalary() {
        return salary;
    }

    public String getSex() {
        return sex;
    }

    public String getDept() {
        return dept;
    }
    static List<Employee> getEmpList(){
        List<Employee>list = new ArrayList<Employee>();
        list.add(new Employee("老张",40,9000,"男","运营部"));
        list.add(new Employee("小刘",24,5000,"女","开发部"));
        list.add(new Employee("大刚",32,7500,"男","销售部"));
        list.add(new Employee("翠花",28,5500,"女","销售部"));
        list.add(new Employee("小马",21,3000,"男","开发部"));
        list.add(new Employee("老王",35,6000,"女","人事部"));
        list.add(new Employee("小王",21,3000,"女","人事部"));
        return list;
    }
}

Stream接口介绍

流处理接口都在java.util.stream包下。BaseStream接口是最基础的接口,但是最常用的是BaseStream接口的一个子接口——Stream接口是泛型接口,所以流中操作的元素可以使任何类的对象。

Stream类接口的常用方法

方法返回值功能描述类型
count()long返回流中元素个数终端操作
distinct()Stream去除流中重复元素中间操作
filter(Predicte<?super T> predicate)Stream返回一个满足指定条件的流中间操作
forEach(Consumer<? super T> action)void遍历流中的每一个元素,执行action动作终端操作
limit(long maxSize)Stream获取流中的每一个元素,执行action动作中间操作
map(Function<? super T,? extends R>mapper)Stream对流中的元素调用mapper方法,产生包含这些元素的一个新的LongStream流终端操作
mapToDouble(ToDoubleFunction<? super T>mapper)DoubleStream对流中的元素调用mapper方法,产生包含这些元素的一个新的DoubleStream流中间操作
mapToInt(ToIntFunction<? super T>mapper)IntStream对流中的元素调用mapper方法,产生包含这些元素的一个新的IntStream流中间操作
mapToLong(ToLongFunction<? super T>mapper)LongStream对流中的元素调用mapper方法,产生包含这些元素的一个新的LongStream流中间操作
max(Comparator<? super T>mapper)Optional根据指定比较器规则,获取流中最大元素终端操作
min(Comparator<? super T>mapper)Optional根据指定比较器规则,获取流中最小元素终端操作
skip(long n)Stream去除元素中n个元素中间操作
sorted()Stream将流中元素排序终端操作
sorted(Comparator<? super T> comparator)Stream将流中的元素按照指定比较器规则进行排序终端操作

Collection接口

新增两个可以获取流对象的方法

语法如下

Stream<E> stream();

Stream<E> parallelstream();

所有集合类都是Collection接口的子类

List<Integer> list = new ArrayList<Integer>();  // 创建集合
Stream<Integer> s = list.stream();              // 获取集合流对象

Optional类

Optional类像是一个容器,可以保存任何对象。Optional类使用final修饰,所以不能有任何子类。Optional类是带有泛型的类,所以该类可以保存任何对象的值。

Optional类提供的常用方法

方法返回类型功能描述
empty()Optional静态方法。返回一个表示空值的Optional实例
filter()Optional如果Optional实例的value是有值的,并且该值与给定的条件匹配,则返回包含这个值的Optional实例,否则返回一个表示空值的Optional实例,否则返回一个表示空值的Optional实例
get()T如果Optional实例的value有值,则返回值为任意,否则排除NoSuchElementException
of(T value)Optional静态方法。返回一个value值等于参数值的Optional实例
ofNullable(T other)Optional返回一个value值等于参数值的非null的Optional实例
orElse(T other)T如果Optional实例的value是有值的,则返回value值,否则返回参数值

例子

// 使用Optional类创建“空”对象
import java.util.Optional;
public class OptionalDemo {
    public static void main(String[] args) {
        Optional<String> strValue = Optional.of("hello");  // 创建有值的对象
        boolean haveValueFlag = strValue.isPresent();            // 判断对象中的值是否为空
        System.out.println("strValue对象是否有值:" + haveValueFlag);
        if(haveValueFlag){
            String str = strValue.get();
            System.out.println("strValue对象的值是:" + str);
        }
        Optional<String> noValue = Optional.empty();             // 创建空值对象
        boolean noValueFlag = noValue.isPresent();               // 判断对象的值是否为空
        System.out.println("noValue对象是否有值:" + noValueFlag);
        if(noValueFlag){
            String str = noValue.get();
            System.out.println("noValue对象的值是:" + str);
        }else{
            String str = noValue.orElse("使用默认值");
            System.out.println("noValue对象的值是:" + str);
        }
    }
}

Collectors类

Collectors类为搜集器类,该类实现了java.util.collector接口,可以将Stream流对象进行各种各样的封装、归集、分组等操作。同时,Collectors类还提供了很多实用的数据加工方法,如数据统计计算等。

方法功能描述
averageDouble(ToDoubleFunction<? super T>mapper)计算流元素的平均值
averageInt(ToIntFunction<? super T>mapper)计算流元素的平均值
averageLong(ToLongFunction<? super T>mapper)计算流元素的平均值
counting()统计元素个数
maxBy(Comparator<? super T>comparator)返回符合条件的最大值元素
minBy(Comparator<? super T>comparator)返回符合条件的最小值元素
summarizingInt(ToIntFunction<? super T>mapper)返回流元素的和
joining()按照顺序将元素连接成一个String类型数据
joining(CharSequence delimiter)按照顺序将元素连接成一个String类型数据,并指定元素之间的间隔符
toList()将流中元素封装成一个List集合
toMap(Function<? super T,?extends K>keyMapper,Function<?super T,?extends U>valueMapper)将流中元素封装成Map集合
toSet()将流中元素封装成Set集合
groupingBy(Function<? super T, ?extends K>classifier)根据分类函数对元素进行分组,并将结果封装成一个Map集合
groupingBy(Function<? super T, ?extends K>classifier,Collector<?super T,A,D>downstream)根据分类函数对元素进行分组,并将结果封装成一个Map集合。一个参数为一级分组条件,第二个参数为耳机分组条件

数据过滤

数据过滤见名知意就是在杂乱的数据中筛选出需要的数据,类似SQL语句中的WHERE关键字,就出一定的条件。将符合条件的数据过滤并展示出来。

filter()方法

该方法可以将lambda表达式作为参数,然后按照lambda表达式的逻辑过滤流中的元素。过滤出想要的流元素后,还需使用Stream提供的collect()方法按照指定方法重新封装。

例子
// 输出1~10中所有的奇数
import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class FilterOddDemo {
    static void printeach(String message, List list){
        System.out.println(message);
        list.stream().forEach(n->{
            System.out.print(n + " ");
        });
        System.out.println();
    }

    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        for(int i = 1;i < 10;i ++){
            list.add(i);
        }
        printeach("集合原有元素:",list);
        Stream<Integer> stream = list.stream();
        // 将集合中的所有奇数过滤出来,把过滤结果冲更新赋值给流对象
        stream = stream.filter(n -> n % 2 == 1);
        List<Integer> result = stream.collect(Collectors.toList());
        printeach("过滤之后的集合元素",result);
    }
}

distinct()方法

distinct()方法是Stream接口提供的过滤方法。可以去除流中的重复元素,效果类似于SQL语句中的DISTINCT关键字一样。

例子
// 去除List集合中的重复数字
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class DistinctDemo {
    static void printeach(String message, List list){
        System.out.println(message);
        list.stream().forEach(n -> {
            System.out.print(n + " ");
        });
        System.out.println();
    }

    public static void main(String[] args) {
        List<Integer> list = new ArrayList<>();
        list.add(1);
        list.add(2);
        list.add(2);
        list.add(3);
        list.add(3);
        printeach("去重前:",list);
        Stream<Integer> stream = list.stream();
        stream = stream.distinct();
        List<Integer> result = stream.collect(Collectors.toList());
        printeach("去重后:" , result);

    }
}

limit()方法

limit()方法是Stream接口提供的方法,该方法可以获取流中的前n个元素

语法如下

stream = stream.limit(int n);

skip()方法

skip()方法是Stream接口提供的方法,该方法可以忽略流中的前n个元素

语法如下

stream = stream.skip(int n);

数据映射

数据的映射和过滤概念不同:过滤是在流中找到符合条件的元素,映射是在流中获得具体的数据。

用原有集合的一些信息覆盖原集合

数据查找

这里的数据查找并不是在流中获取数据,而是判断是否有符合条件的数据,查找的结果是一个boolean值或者Object对象。

allMatch()方法

allMatch()方法是Stream接口提供的方法,该方法会判断流中的元素是否全部符合某一条件,返回结果是boolean值。如果全部符合则返回true,否则返回false。

语法如下

boolean bool = stream.allMatch(x -> y > z)

anymatch()方法

同上allMatch()方法,不同在于一旦有符合条件的返回true,全部没有返回false、

noneMatch()方法

逻辑和allMatch()方法正好相反

findFirst()方法

该方法会返回符合条件的第一个元素

数据收集

理解为高级的“数据映射”和“数据查找”

一般有两种应用场景:数据统计和数据分组

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值