Java系列笔记第九章:Stream流与方法引用

第九章:Stream流与方法引用

1. Stream流

1.1 Stream简介

JDK 1.8开始引入。

流不一定是IO流。得益于lambda编程带来的函数式编程,引入Stream的概念,用于解决集合类的弊端,简化操作。

比如List的遍历,只能用for循环。我们关注于做什么(循环体), 而不是怎么做(for循环语法)。

示例:

我们有一个List,先筛选出姓张的人,再选出名字长度是3的人,再输出。

  • 使用list.stream()方法转换为Stream流。
  • 流有filter方法,传进去一个Predicate接口的lambda表达式进行筛选。
  • 流有forEach接口,传进去消费者接口,将数据输出。
list.stream()
    .filter((s -> s.startsWith("张")))
    .filter(s -> s.length() == 3)
    .forEach(System.out::println);

拼接流式模型:建立一个生产线,按照生产线来生产商品。每个操作都是一个流,从一个流的模型可以转换为另一个流的模型。集合的数据并没有被实时操作,只有最终执行时才会开始执行,这也是lambda延迟执行的特点。

Stream并不会存储元素,而是按需计算。

数据来源可以是集合或者数组。


Stream有两个基础特征:

  • Pipelining: 中间操作都会返回流对象本身,这样多个操作可以串联成一个管道。这样就可以对操作进行优化,比如延迟执行短路

  • 内部迭代: Stream可以直接调内部迭代的方法。


  • filter
  • map
  • skip
  • count
  • forEach

1.2 获取流

  1. 所有的Collection集合均可通过stream()方法获取流。

  2. Stream接口的静态方法of也可以。

    • static <T> Stream<T> of(T...values)
  3. Map<K, V>获取流的方法:

    • 先获取Map.Entry<K, V>,存入Set里,再转换为流。
  4. 数组转换为流:

    • Stream.of(一个数组)。
    • Stream.of(1, 2, 3, 4, 5)。

1.3 流的常用方法

流有两类方法:

  • 延迟方法

    • 返回值是Stream流对象,所以可以链式调用。
  • 终结方法

    • count()和forEach()方法是终结方法。
1.3.1 forEach方法

该方法接收一个Consumer接口,会将每一个元素交给接口去处理。

调用之后就会将流终结。

一般用来遍历流。

list.stream().forEach((str) -> System.out.println(str));

优化为方法引用:list.stream().forEach(System.out::println);

1.3.2 filter方法

这是个过滤方法,传递进来一个Predicate接口,对传进来的数据进行判断。如果满足条件,这个元素将被放进流,否则元素会被舍弃。

1.3.3 map方法

这是个映射方法,传递进来一个Function接口,将当前流中的数据转换为另一种类型的数据。

list.stream().map(s -> Integer.parseInt(s));

优化为方法引用:list.stream().map(Integer::parseInt);

1.3.4 count方法

返回一个long类型的数据,统计Stream中元素的个数。

调用之后就会将流终结。

1.3.5 limit方法

截取前n个元素,n是long类型。如果n大于流中元素的长度,就会返回全部元素组成的流。

这是个延迟方法,只是对流中的元素进行截取,返回一个新的流对象。

list.stream().limit(2); 返回list中前两个元素组成的流。

1.3.5 skip方法

跳过前n个元素。这是个延迟方法,只是对流中的元素进行截取,返回一个新的流对象。如果n大于流中元素的个数,就会返回一个空的流。

1.3.6 contract方法

组合两个流。Stream流的静态方法,可以将两个流合并成一个流。
Stream.concat(stream1, stream2);

1.4 Stream注意事项

Stream流属于管道流,只能被使用一次。第一个流调用完毕方法后,数据就会转到下一个Stream上。此时第一个Stream流已被使用完毕,不能再使用。

2. 方法引用

2.1 简介

我们传递进去的lambda表达式是一种解决方案:拿什么参数、做什么操作。如果其他地方已经存在了这样的方案,就不用重写重复的逻辑。


示例:

接口定义:

package MethodReference;
@FunctionalInterface
public interface PrintAble {
    void print(String str);
}

测试类:

package MethodReference;

public class PrintAbleDemo {
    public static void fun(String str, PrintAble p) {
        p.print(str);
    }

    public static void main(String[] args) {
        String s = "aaa";
        fun(s, str -> System.out.println(str));
    }
}

分析:

str -> System.out.println(str)这个lambda表达式的目的是把str传给System.out对象,调用println方法打印。这个对象和方法都是已经存在的。

所以我们可以直接使用System.out::println来输出字符串。参数被省略。

2.2 语义分析

注意: 传递的参数一定要是方法引用中可以接收的类型,否则会抛异常。

2.3 通过对象名引用成员方法

如果某个类的一个方法实现了我们想要的操作,就可以使用这个类对象来调用这个方法,作为方法引用。

接口定义:

package MethodReference.ObjectMethodReference;
@FunctionalInterface
public interface PrintAble {
    void print(String str);
}

类定义:

package MethodReference.ObjectMethodReference;

public class MethodReferObj {
    public void toUpper(String str){
        System.out.println(str.toUpperCase());
    }
}

测试:

package MethodReference.ObjectMethodReference;

public class Demo {
    public static void fun(PrintAble p){
        p.print("Hello");
    }

    public static void main(String[] args) {
        MethodReferObj obj = new MethodReferObj();
        fun(obj::toUpper);
    }
}

2.4 通过类名引用静态成员方法

类存在,静态成员方法存在,就可以不新建对象,直接用类::成员方法来实现方法引用。

2.5 通过super引用父类成员方法

package MethodReference.superDemo;

public class Man extends Human{
    @Override
    public void sayHello(){
        System.out.println("我是子类方法");
    }

    public void method(Greeting g){
        g.meeting();
    }

    public void show(){
        method(super::sayHello);
    }
}

存在继承关系、父类有sayHello方法。

2.6 通过this引用本类的方法

package MethodReference.superDemo;

public class Man extends Human{
    @Override
    public void sayHello(){
        System.out.println("我是子类方法");
    }

    public void method(Greeting g){
        g.meeting();
    }

    public void show(){
        method(this::sayHello);
    }
}

就是把2.5中的super改为this。

2.7 类的构造器引用

package MethodReference.ConstructReferDemo;

public class Demo {
    public static void printName(String s, PersonBuilder p){
        Person build = p.build(s);
        System.out.println(build.getName());
    }

    public static void main(String[] args) {
        String name = "张三";
        
        //传统lambda表达式
        printName(name,(s)->new Person(s));
        
        //方法引用,类名::new
        printName(name,Person::new);
    }
}

2.8 数组的构造器引用

int[]::new

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

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值