Welcome everyone

Lambda & Stream 使用入门详解

java 汪明鑫 106浏览 0评论

lambda表达式

Lambda 允许把函数作为一个方法的参数

一切皆为函数,函数可以作为另一个函数的输入/输出,形成表达式链

使用 Lambda 表达式可以使代码变的更加简洁紧凑

推动 Java 8 发布的最重要新特性

 

 

【直观体验】

// 1. 不需要参数,返回值为 5  
() -> 5  
  
// 2. 接收一个参数(数字类型),返回其2倍的值  
x -> 2 * x  
  
// 3. 接受2个参数(数字),并返回他们的差值  
(x, y) -> x – y  
  
// 4. 接收2个int型整数,返回他们的和  
(int x, int y) -> x + y  
  
// 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)  
(String s) -> System.out.print(s)

 

【Demo1】

/**
 * @author: wangmingxin03
 * @date: 2020-07-02
 */
public class Main {
    public static void main(String[] args) throws Throwable {
        Supplier<Integer> integerSupplier = () -> 5;
        Integer i = integerSupplier.get();
        System.out.println(i);

        Weigher<Integer, Integer> kvWeigher = (x, y) -> x + y + 6;
        int weigh = kvWeigher.weigh(1, 3);
        System.out.println(weigh);

        ThrowableConsumer<String, Throwable> stringThrowableThrowableConsumer =
                (String x) -> System.out.println(x);
        stringThrowableThrowableConsumer.accept("xinye");
    }
}

 

【Demo2】

/**
 * @author: wangmingxin03
 * @date: 2020-07-02
 */
public class Main {
    public static void main(String[] args) {
        Plus plus = (x,y) -> x+y;
        int add = plus.plus(1, 2);
        System.out.println(add);
    }
}

@FunctionalInterface
interface Plus {
    int plus(int x,int y);
}

 

【Demo3】

/**
 * @author: wangmingxin03
 * @date: 2020-07-03
 */
public class Test3 {
    public static void main(String[] args) {
        int num = 1;
        Converter<Integer, String> s = (param) -> System.out.println(String.valueOf(param + num));
        s.convert(2); 
    }
}

interface Converter<T1, T2> {
    void convert(int i);
}

输出3 没什么问题

我们如果在下面对num  重新赋值呢?

在 Lambda 表达式当中不允许声明一个与局部变量同名的参数或者局部变量

 

 

Lambda方法引用

方法引用可以看作仅仅调用特定方法方法的 lambda 的一种快捷的方式

public void functionRef() {
    Apple one = new Apple(20);
    Apple two = new Apple(10);
    List<Apple> appleList = Arrays.asList(one, two);

    appleList.sort((a1, a2)->a1.getWeight().compareTo(a2.getWeight()));
    appleList.sort(Comparator.comparing(Apple::getWeight));
}

Apple::getWeight 就是方法引用

 

 

lambda 内置函数

【接口java.util.function.Predicate】

核心方法

/**
     * Evaluates this predicate on the given argument.
     *
     * @param t the input argument
     * @return {@code true} if the input argument matches the predicate,
     * otherwise {@code false}
     */
    boolean test(T t);

 

//过滤大于等于20岁的人
        Predicate<Integer> agePredicate = (age) -> age >= 20;
        List<Person> oldPeople = people.stream()
                .filter(person -> agePredicate.test(person.getAge()))
                .collect(Collectors.toList());
        System.out.println(oldPeople);

输出: [Person{name=’张三’, age=20}, Person{name=’李四’, age=21}]

 

 

【接口java.util.function.Supplier】

提供者

@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}
Supplier<Person> getInstance = () -> new Person("xx", 18);
System.out.println(getInstance.get());

输出:  Person{name=’xx’, age=18}

 

 

【接口java.util.function.Consumer】

  /**
     * Performs this operation on the given argument.
     *
     * @param t the input argument
     */
    void accept(T t);

 

Consumer  消费

accept 接受一个参数做处理

 Consumer<Person> personConsumer =
                (Person p) -> System.out.println("幸运儿 " + p.getName());
        personConsumer.accept(p1);

输出:   幸运儿 张三

 

 

【接口java.util.function.Function】

/**
     * Applies this function to the given argument.
     *
     * @param t the function argument
     * @return the function result
     */
    R apply(T t);

给一个类型的对象转为另一个灵性的对象,这个是我们最常用的

 

Function<Person, String> nickNameFunction =
                (Person p) -> p.getName() + "_帅气";
        List<String> nickNameList =
                people.stream().map(nickNameFunction).collect(Collectors.toList());
        System.out.println(nickNameList);

输出: [张三帅气, 李四帅气, 王五_帅气]

 

Stream的定义与理解

A sequence of elements supporting sequential and parallel aggregate operations.

1. Stream是元素的集合,这点让Stream看起来用些类似Iterator

2. 可以支持顺序和并行的对原Stream进行汇聚的操作

Stream类图

 

Stream的基本步骤

 

  • 创建Stream   一个数据源 如集合
  • 转换Stream(可以有多次转换)    中间操作链
  • 对Stream进行聚合    终端操作

 

构建流

  • 由值创建流
Stream<Integer> integerStream = Stream.of(1,2,3,5);
integerStream.forEach(e->System.out.println(e));

 

  • 由数组创建流
int []nums = {};
Arrays.stream(nums)

 

  • 由文件生成流

Files.lines

  • 由函数生成流

无限流(没有固定大小的流)

Stream.iterate()

Stream.generate()

无限流一般需要limit来显示限定大小

 

Stream.iterate(0, n -> n + 2)
        .limit(10)
        .forEach(System.out::println);

 

Stream.generate(Math::random)
                .limit(5)
                .forEach(System.out::println);

 

转换Stream

转换Stream其实就是把一个Stream通过某些行为转换成一个新的Stream

【filter】

过滤

【map】

映射

【limit】

对一个Stream进行截断操作,获取其前N个元素,如果原Stream中包含的元素个数小于N,那就获取其所有的元素;

【skip】

返回一个丢弃原Stream的前N个元素后剩下元素组成的新Stream,如果原Stream中包含的元素个数小于N,那么返回空Stream;

 

最常用的就是filter、map了

 

原始类型流特化

解决装箱造成的复杂性

IntStream

LongStream

 

package pers.wmx.springbootfreemarkerdemo.java8;

import java.util.ArrayList;
import java.util.List;
import java.util.stream.IntStream;

/**
 * @author: wangmingxin03
 * @date: 2020-07-03
 */
public class Test4 {

    public static void main(String[] args) {
        List<Person> people = new ArrayList<>();

        Person p1 = new Person();
        p1.setName("张三");
        p1.setAge(20);
        Person p2 = new Person();
        p2.setName("李四");
        p2.setAge(21);
        Person p3 = new Person();
        p3.setName("王五");
        p3.setAge(18);

        people.add(p1);
        people.add(p2);
        people.add(p3);

        IntStream ageStream = people.stream().mapToInt(Person::getAge);

        //求合
        System.out.println(ageStream.sum());
    }

}


class Person {
    private String name;

    private int age;

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }
}

输出: 59

 

 IntStream ageStream = people.stream().mapToInt(Person::getAge);

        //求合
        System.out.println(ageStream.sum());

        //再次求合
        System.out.println(ageStream.sum());

我们基于得到的流再求一次合

 

运行报异常

Exception in thread “main” java.lang.IllegalStateException: stream has already been operated upon or closed

说明流不能被重复使用

如果还想如用,需再从原集合获取流

 

 

获取最大值和最小值

 int maxAge = people.stream()
                .mapToInt(Person::getAge).max().orElse(0);
  System.out.println(maxAge);

  int minAge = people.stream()
                .mapToInt(Person::getAge).min().orElse(0);
  System.out.println(minAge);

 

流的扁平化

flatMap

 

//又来了一批年龄大的people list
        List<Person> people1 = new ArrayList<>();

        Person p4 = new Person();
        p1.setName("赵六");
        p1.setAge(40);
        Person p5 = new Person();
        p2.setName("王二麻子");
        p2.setAge(99);
        Person p6 = new Person();
        p3.setName("赵二瘸子");
        p3.setAge(200);

        people1.add(p4);
        people1.add(p5);
        people1.add(p6);

        List<List<Person>> personList = new ArrayList<>();
        personList.add(people);
        personList.add(people1);

        //想把这两个list合成一个
        List<Person> finalPersonList = personList.stream()
                .flatMap(List::stream)
                .collect(Collectors.toList());
        System.out.println(finalPersonList);

输出:

[Person{name=’张三’, age=20}, Person{name=’李四’, age=21}, Person{name=’王五’, age=18}, Person{name=’赵六’, age=40}, Person{name=’王二麻子’, age=99}, Person{name=’赵二瘸子’, age=200}]

 

 

 

flatMap 方法让你把一个流中的每个值都换成另一个流,然后把所有的流连接起来成为一个流。

 

查找与匹配

也称短路操作

allMatch  anyMatch  noneMatch  findFirst  findAny

较简单,通常用来查找符合条件的元素

 

比如 anyMatch 流中是否有一个元素能匹配给定的谓词

//是否有(存在)大于100岁的
        boolean flag = finalPersonList.stream()
                .anyMatch(person -> person.getAge() > 100);
        System.out.println(flag);

输出: true

 

用流收集数据

用流收集数据

前面已经用过了

.collect(Collectors.toList());

把一系列的中间操作聚合,即终端操作会消耗流

上面有个例子有体现,流是不能重复使用的

其实所有的收集器都来自于 Collectors.reducing

其他的用法都是衍生出来的,更加方便我们使用流

 

//求流中的元素个数

long peopleCount1= finalPersonList.stream().collect(Collectors.counting());
long peopleCount2= finalPersonList.stream().count();
System.out.println(peopleCount1);
System.out.println(peopleCount2);

 

最后再简单介绍一个分组,也是我们经常用的

//按年龄分组
        Map<Boolean, List<Person>> personMap =
                finalPersonList.stream().collect(Collectors.groupingBy(p -> p.getAge() > 50));
        List<Person> oldPersonList = personMap.get(true);
        List<Person> youngPersonList = personMap.get(false);
        System.out.println(youngPersonList);
        System.out.println(oldPersonList);

输出:

[Person{name=’张三’, age=20}, Person{name=’李四’, age=21}, Person{name=’王五’, age=18}, Person{name=’赵六’, age=40}]
[Person{name=’王二麻子’, age=99}, Person{name=’赵二瘸子’, age=200}]

 

我们还可以根据对象的某一属性来进行分组,比如Person再加一个属性爱好,

根据爱好来分成多组

 

 

转载请注明:汪明鑫的个人博客 » Lambda & Stream 使用入门详解

喜欢 (1)

说点什么

您将是第一位评论人!

提醒
avatar
wpDiscuz