美文网首页
Java8 之 Lambda 表达式与函数式接口

Java8 之 Lambda 表达式与函数式接口

作者: 小道萧兮 | 来源:发表于2020-01-10 11:29 被阅读0次

一、Lambda 表达式

Lambda 表达式,也可称为闭包,它是 Java 8 发布的最重要新特性。Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。使用 Lambda 表达式可以使代码变的更加简洁紧凑。

基本语法:
(parameters) -> expression 或 (parameters) -> { statements; }

例如:

// 无参数,返回值为 5  
() -> 5  
  
// 接收一个参数(数字类型),返回其2倍值  
x -> 2 * x  
  
// 接受2个参数(数字),并返回他们的差
(x, y) -> x – y

// 接收2个int型整数,返回他们的和
(int x, int y) -> x + y

// 接受一个 String 对象,在控制台打印,不返回任何值
(String s) -> System.out.print(s)

特点:

  • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
  • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。

下面举几个例子,先准备数据:

public class Person {
    private String name;
    private Integer age;

    public Person(String name, Integer age) {
        this.name = name;
        this.age = age;
    }
    public List<Person> getPersonList() {
        return Arrays.asList(new Person("zhang", 20),new Person("liu", 21), new Person("wang", 19),new Person("li", 22));
    }
}

1、forEach 遍历集合
使用 forEach 方法,直接通过一行代码即可完成对集合的遍历:

List<Person> personList = getPersonList();
personList.forEach(e -> System.out.println(e));

/**
* ========结果========
* Person{name='zhang', age=20}
* Person{name='liu', age=21}
* Person{name='wang', age=19}
* Person{name='li', age=22}
*/

2、filter 对集合进行过滤

// 过滤 age > 20 的对象
Stream<Person> personStream = personList.stream().filter(e -> e.getAge() > 20);
// stream 转 list
List<Person> collect = personStream.collect(Collectors.toList());
// print 打印
collect.forEach(e -> System.out.println(e));

当需要通过 多个过滤条件对集合进行过滤时,可以通过调用多次 filter 通过传入不同的 Predicate 对象来进行过滤

Predicate<Person> ilter1 = e -> e.getAge() > 19;
Predicate<Person> filter2 = e -> e.getAge() < 22;
// 过滤 age > 19 并且 age < 22 的对象
personList.stream().filter(filter1).filter(filter2).forEach(System.out::println);

/**
* ========结果========
* Person{name='zhang', age=20}
* Person{name='liu', age=21}
*/

也可以通过 Predicate 对象的 andor 方法,对多个Predicate 对象进行过滤。

Predicate<Person> filter1 = e -> e.getAge() > 20;
Predicate<Person> filter2 = e -> e.getName().startsWith("l");
// 过滤 age < 20 或者 name 以 l 开头的对象
personList.stream().filter(filter1.or(filter2)).forEach(System.out::println);

/**
* ========结果========
* Person{name='wang', age=19}
* Person{name='li', age=22}
*/

3、limit 限制结果集的数据量
limit 可以控制结果集返回的数据条数

// age > 19 的数据有3条,通过limit只返回2条
personList.stream().limit(2).filter(e -> e.getAge() > 19).forEach(System.out::println);

/**
* ========结果========
* Person{name='zhang', age=20}
* Person{name='liu', age=21}
*/

4、sorted 排序
通过 sorted,可以按自定义的规则,对数据进行排序

// 按照 age 排序
personList.stream().sorted((e1, e2) -> e1.getAge() - e2.getAge()).forEach(System.out::println);
System.out.println("---------------");
// 按照 name 排序
personList.stream().sorted((e1, e2) -> e1.getName().compareTo(e2.getName())).forEach(System.out::println);

/**
* ========结果========
* Person{name='wang', age=19}
* Person{name='zhang', age=20}
* Person{name='liu', age=21}
* Person{name='li', age=22}
* ---------------
* Person{name='li', age=22}
* Person{name='liu', age=21}
* Person{name='wang', age=19}
* Person{name='zhang', age=20}
*/

5、max、min 获取结果中 某个值最大最小的的对象
maxmin 可以按指定的条件,获取到最大、最小的对象,当集合里有多个满足条件的最大最小值时,只会返回一个对象。

// 获取 age 最大的对象
Person person = personList.stream().max(Comparator.comparing(Person::getAge)).get();
System.out.println(person); // Person{name='li', age=22}

// 获取数组中最大/小的数字
int a = Stream.of(2,1,4,5,3).max(Integer::compare).get(); // 5
int b = Stream.of(2,1,4,5,3).min(Integer::compare).get(); // 1

6、map 集合转换
map 对集合中的每个元素进行遍历,并且可以对遍历的元素进行操作,转化为其他对象。

// 例1:把 Integer 数组转为 String 数组
List<Integer> num = Arrays.asList(19, 20, 21);
List<String> dollar = num.stream().map(e -> "$" + e).collect(Collectors.toList());
dollar.forEach(System.out::println);
/**
* ========结果========
* $19
* $20
* $21
*/

// 例2:由数组构建对象
List<String> strings = Arrays.asList("zhang 20", "liu 21", "wang 19", "li 22");
// 先把数组中每个元素用空格切分,然后将切分后的第0个元素传入构造方法的name,第1个元素传入构造方法的age
strings.stream().map(e -> e.split(" ")).map(e -> new Person(e[0], Integer.valueOf(e[1]))).forEach(System.out::println);
/**
* ========结果========
* Person{name='zhang', age=20}
* Person{name='liu', age=21}
* Person{name='wang', age=19}
* Person{name='li', age=22}
*/

7、reduce 聚合函数
reduce 也是对集合中所有值进行操作,但它是将所有值,按照传入的处理逻辑,将结果处理合并为一个

如:将集合中的所有整数相加,并返回其总和

// 将所有元素加起来求和
List<Integer> nums = Arrays.asList(2, 5, 3, 4, 7);
int num1 = nums.stream().reduce((e1, e2) -> e1 + e2); // 21
        
// 带初始值的计算
int num2 = nums.stream().reduce(10, (e1, e2) -> e1 + e2); // 31

8、summaryStatistics 计算集合元素的最大、最小、平均等值

IntSummaryStatistics statistics= personList.stream().mapToInt(e -> e.getAge()).summaryStatistics();
System.out.println("最大值: " + statistics.getMax()); // 22
System.out.println("最小值: " + statistics.getMin()); // 19
System.out.println("平均值: " + statistics.getAverage()); // 20.5
System.out.println("总和: " + statistics.getSum()); // 82
System.out.println("个数: " + statistics.getCount()); // 4

二、函数式接口

函数式接口(Functional Interface)是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口

函数式接口可以被隐式转换为 Lambda 表达式。最常见的函数式接口可能就是 Runnable 接口了

@FunctionalInterface
public interface Runnable {
    public abstract void run();
}

特点:

  • 接口有且仅有一个抽象方法
  • 允许定义静态方法
  • 允许定义默认方法
  • 允许 java.lang.Object 中的 public 方法
  • @FunctionalInterface 注解不是必须的,如果一个接口符合"函数式接口"定义,那么加不加该注解都没有影响。加上该注解能够更好地让编译器进行检查。如果编写的不是函数式接口,但是加上了@FunctionInterface 那么编译器会报错

例如,启动一条线程,旧写法如下:

new Thread((new Runnable() {
    @Override
    public void run() {
        Thread thread = Thread.currentThread();
        System.out.println("线程: " + thread.getName());
    }
})).start();

但是可以使用 lambda 表达式和函数式接口组合方式来简化代码:

new Thread((() -> {
    Thread thread = Thread.currentThread();
    System.out.println("线程: " + thread.getName());
})).start();

三、单词统计

说了这么多,来点实战的例子。

在 Spark 中的入门例子就是单词统计了。这个例子很好的说明了 Lambda 表达式与函数式接口有多么方便。

首先,新建一个 Maven 项目,在 pom 文件中添加 Spark 依赖。

<dependency>
    <groupId>org.apache.spark</groupId>
    <artifactId>spark-core_2.12</artifactId>
    <version>2.4.4</version>
</dependency>

不使用 Lambda 表达式时,代码如下:

public static void main(String[] args) {
    // 创建Spark配置对象
    SparkConf conf = new SparkConf().setAppName("name").setMaster("local");
    // 通过conf创建上下文
    JavaSparkContext sc = new JavaSparkContext(conf);
    // 加载文件
    JavaRDD<String> rdd1 = sc.textFile("/Users/terry-jri/Desktop/test2.txt");
    // 压扁
    JavaRDD<String> rdd2 = rdd1.flatMap(new FlatMapFunction<String, String>() {
        @Override
        public Iterator<String> call(String s) {
            String[] arr = s.split(" ");
            List<String> list = new ArrayList<>(Arrays.asList(arr));
            return list.iterator();
        }
    });
    // 映射
    JavaPairRDD<String, Integer> rdd3 = rdd2.mapToPair(new PairFunction<String, String, Integer>() {
        @Override
        public Tuple2<String, Integer> call(String s) {
            return new Tuple2<>(s, 1);
        }
    });
    // 聚合
    JavaPairRDD<String, Integer> rdd4 = rdd3.reduceByKey(new Function2<Integer, Integer, Integer>() {
        @Override
        public Integer call(Integer v1, Integer v2) {
            return v1 + v2;
        }
    });
    // 收集,打印输出
    for (Object o : rdd4.collect()) {
        System.out.println(o);
    }
}

使用 Lambda 表达式如下:

public static void main(String[] args) {
    // 创建Spark配置对象
    SparkConf conf = new SparkConf().setAppName("name").setMaster("local");
    // 通过conf创建上下文
    JavaSparkContext sc = new JavaSparkContext(conf);

    JavaPairRDD<String, Integer> rdd = sc.textFile("/Users/terry-jri/Desktop/test2.txt") // 加载文件
            .flatMap(line -> Arrays.asList(line.split(" ")).iterator()) // 压扁
            .mapToPair(s -> new Tuple2<>(s, 1)) // 映射
            .reduceByKey((v1, v2) -> (v1 + v2)); // 聚合

    rdd.collect().forEach(System.out::println); // 收集并打印
}

相关文章

  • Lambda表达式总结

    Lambda表达式总结使用范例以及例子Lambda表达式基础语法函数式接口Lambda练习Java8四大内置函数式...

  • Lambda表达式

    Lambda表达式与函数式接口紧密相关,函数式接口介绍 Lambda表达式的基本语法 Java Lambda表达式...

  • Java Lambda表达式

    标签(空格分隔):java Lambda表达式是java8的重要更新,Lambda表达式的目标类型必须是函数式接口...

  • Java8中你可能不知道的一些地方之函数式接口实战

    什么时候可以使用 Lambda?通常 Lambda 表达式是用在函数式接口上使用的。从 Java8 开始引入了函数...

  • Lambda表达式

    Lambda表达式是Java8的一个新特性,是函数式接口的一种体现。所谓函数式接口(functional inte...

  • Java8一些变态的特性

    引言 Java8里面增加了许多新特性。。。嗯,此处废话!!! Lambda表达式 函数式接口 要使用Lambda表...

  • JDK1.8-Lambda表达式

    一、Lambda表达式及函数式接口介绍 1. 描述 Lambda表达式是Java8中最重要的新功能之一。使用Lam...

  • Java8 函数式编程初识之函数式接口

    在Java8 函数式编程初识之Lambda表达式中讲了如果一个接口中仅有一个待实现的方法则它可以用Lambda代替...

  • Java 函数式接口说明

    整理一下 Java8 之后的重要概念 函数式接口的说明和团队分享,这是函数式编程和 Lambda 表达式的基础。虽...

  • JDK8新特性 - 函数式编程

    一、JDK8之自定义函数式编程 1.使用Lambda表达式,自定义lambda接口编程 定义⼀个函数式接口 需要标...

网友评论

      本文标题:Java8 之 Lambda 表达式与函数式接口

      本文链接:https://www.haomeiwen.com/subject/raeuactx.html