day13【Stream流、方法引用】JDK8型新特征:获取流、Stream流常用方法、方法引用 (对Lambda表达式进行优化)

本文深入探讨了Java 8中的Stream流及其如何简化集合和数组操作,强调了流式思想和工厂流水线的概念。内容涵盖Stream流的获取、常用方法如forEach、filter、map、count等,并介绍了方法引用的优化作用,包括对象名、类名、super和this引用成员方法,以及构造器和数组构造器引用。通过实例展示了如何用Stream流取代传统的for循环,提高代码的可读性和效率。
摘要由CSDN通过智能技术生成

目录

第一章 Stream流

io流:主要用于进行读写

Stream流:对集合和数组进行简化操作

1.循环遍历的弊端

2.循环和遍历的区别

3.Stream流取代for循环

for循环:对集合元素进行过滤

Stream流:JDK1.8新特征,关注“做什么”,而不是“怎么做”

1.2 流式思想概述

1.3 获取流 :两种。stream &&stream.of()

Stream 接口的静态方法 of 可以获取数组对应的流。数组

所有的 Collection 集合都可以通过 stream 默认方法获取流; 集合

根据Map获取流

1.4 常用方法

延迟方法:返回值类型仍然是 Stream 接

终结方法:返回值类型不再是 Stream 接口自身类型的方法(count,forEach)

1.逐一处理:forEach (终结方法)

复习Consumer接口

基本使用:

2.过滤:fifilter

复习Predicate接口

 boolean test(T t)

 基本使用

3.映射:map  

 复习Function接口

基本使用

4.统计个数:count (终结方法)

 基本使用

5.取用前几个:limit  

 基本使用

 6.跳过前几个:skip

  基本使用

7.组合:concat  

基本使用  

第二章 方法引用  (对Lambda表达式进行优化)

2.1 冗余的Lambda场景

 2.2 问题分析

2.3 用方法引用改进代码

2.4 方法引用符

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

通过对象名引用方法,

使用前提是对象名已经存在,成员方法也已经存在

就可以使用对象名引用成员方法

2.6 通过类名称引用静态方法

Lambda表达式: n -> Math.abs(n)

方法引用: Math::abs

2.7 通过super引用成员方法

Lambda表达式: () -> super.sayHello()

方法引用: super::sayHello

2.8 通过this引用成员方法  

this::成员方法

Lambda表达式: () -> this.buyHouse()

方法引用: this::buyHouse

2.9 类的构造器引用

Lambda表达式: name -> new Person(name)

方法引用: Person::new

 2.10 数组的构造器引用

Lambda表达式: length -> new int[length]

方法引用: int[]::new


第一章 Stream

io流:主要用于进行读写

Stream流:对集合和数组进行简化操作

将集合和数组转换为Stream流再用Stream流对集合和数组进行简化操作,用于解决已有集合类库既有的弊端。

1.循环遍历的弊端

Java 8 Lambda 让我们可以更加专注于 做什么 What ),而不是 怎么做 How

我们可以发现

for 循环的语法就是 怎么做
for 循环的循环体才是 做什么

2.循环和遍历的区别

为什么使用循环?因为要进行遍历。
但循环是遍历的唯一方式吗?遍历是指每一个元素逐一进行处理, 而并不是从第一个到最后一个顺次处理的循环 。前者是目的,后者是方式。

3.Stream流取代for循环

先代码对比写

for循环:对集合元素进行过滤

1. 首先筛选所有姓张的人;

2. 然后筛选名字有三个字的人;
3. 最后进行对结果进行打印输出。
/*
    使用传统的方式,遍历集合,对集合中的数据进行过滤
 */
public class Demo01List {
    public static void main(String[] args) {
        //创建一个List集合,存储姓名
        List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");

        //对list集合中的元素进行过滤,只要以张开头的元素,存储到一个新的集合中
        List<String> listA = new ArrayList<>();
        for(String s : list){
            if(s.startsWith("张")){
                listA.add(s);
            }
        }

        //对listA集合进行过滤,只要姓名长度为3的人,存储到一个新集合中
        List<String> listB = new ArrayList<>();
        for (String s : listA) {
            if(s.length()==3){
                listB.add(s);
            }
        }

        //遍历listB集合
        for (String s : listB) {
            System.out.println(s);
        }
    }
}

Stream流:JDK1.8新特征,关注“做什么”,而不是“怎么做”

/*
    使用Stream流的方式,遍历集合,对集合中的数据进行过滤
    Stream流是JDK1.8之后出现的
    关注的是做什么,而不是怎么做
 */
public class Demo02Stream {
    public static void main(String[] args) {
        //创建一个List集合,存储姓名
        List<String> list = new ArrayList<>();
        list.add("张无忌");
        list.add("周芷若");
        list.add("赵敏");
        list.add("张强");
        list.add("张三丰");

        //对list集合中的元素进行过滤,只要以张开头的元素,存储到一个新的集合中
        //对listA集合进行过滤,只要姓名长度为3的人,存储到一个新集合中
        //遍历listB集合
        list.stream()
                .filter(name->name.startsWith("张"))
            .filter(name->name.length()==3)
            .forEach(name-> System.out.println(name));
}
}

1.2 流式思想概述

 整体来看,流式思想类似于工厂车间的生产流水线

当需要对多个元素进行操作(特别是多步操作)的时候,考虑到性能及便利性,我们应该首先拼好一个 模型 步骤方案,然后再按照方案去执行它。

 

这张图中展示了过滤、映射、跳过、计数等多步操作,这是一种集合元素的处理方案,而方案就是一种 函数模型 。图中的每一个方框都是一个
这里的 filter map skip 都是在对函数模型进行操作,集合元素并没有真正被处理。而这得益于 Lambda 的延迟执行特性。
  • Pipelining: 中间操作都会返回流对象本身。
  • 内部迭代: 流可以直接调用遍历方法。

1.3 获取流 :两种。stream &&stream.of()

  • Stream 接口的静态方法 of 可以获取数组对应的流。数组

public class Demo01GetStream {
    public static void main(String[] args) {

        //把数组转换为Stream流
        Stream<Integer> stream6 = Stream.of(1, 2, 3, 4, 5);
        //可变参数可以传递数组
        Integer[] arr = {1,2,3,4,5};
        Stream<Integer> stream7 = Stream.of(arr);
        String[] arr2 = {"a","bb","ccc"};
        Stream<String> stream8 = Stream.of(arr2);
    }
}
  • 所有的 Collection 集合都可以通过 stream 默认方法获取流; 集合

public class Demo01GetStream {
    public static void main(String[] args) {
        //把集合转换为Stream流
        List<String> list = new ArrayList<>();
        Stream<String> stream1 = list.stream();

        Set<String> set = new HashSet<>();
        Stream<String> stream2 = set.stream();

    }
}
  • 根据Map获取流

public class Demo01GetStream {
    public static void main(String[] args) {

        Map<String,String> map = new HashMap<>();
        //获取键,存储到一个Set集合中
        Set<String> keySet = map.keySet();
        Stream<String> stream3 = keySet.stream();

        //获取值,存储到一个Collection集合中
        Collection<String> values = map.values();
        Stream<String> stream4 = values.stream();

        //获取键值对(键与值的映射关系 entrySet)
        Set<Map.Entry<String, String>> entries = map.entrySet();
        Stream<Map.Entry<String, String>> stream5 = entries.stream();

    }
}


1.4 常用方法

  • 延迟方法:返回值类型仍然是 Stream

  • 终结方法:返回值类型不再是 Stream 接口自身类型的方法(count,forEach)

1.逐一处理:forEach (终结方法)

void forEach(Consumer<? super T> action)

复习Consumer接口

java . util . function . Consumer < T > 接口是一个消费型接口。
Consumer 接口中包含抽象方法 void accept ( T t ) ,意为消费一个指定泛型的数据

基本使用:

public class Demo02Stream_forEach {
    public static void main(String[] args) {
        //获取一个Stream流
        Stream<String> stream = Stream.of("张三", "李四", "王五", "赵六", "田七");
        //使用Stream流中的方法forEach对Stream流中的数据进行遍历
        /*stream.forEach((String name)->{
            System.out.println(name);
        });*/

        stream.forEach(name->System.out.println(name));
    }
}

2.过滤:fifilter

可以通过 filter 方法将一个流转换成另一个子集流。方法签名:

Stream < T > filter ( Predicate <? super T > predicate );

复习Predicate接口

 boolean test(T t)

 基本使用

import java . util . stream . Stream ;
public class Demo07StreamFilter {
        public static void main ( String [] args ) {
                Stream < String > original = Stream . of ( " 张无忌 " , " 张三丰 " , " 周芷若 " );
                Stream < String > result = original . filter ( s ‐> s . startsWith ( " " ));
        }
}

3.映射:map  

< R > Stream < R > map ( Function <? super T , ? extends R > mapper );

 复习Function接口

R apply ( T t );

基本使用

import java . util . stream . Stream ;
public class Demo08StreamMap {
        public static void main ( String [] args ) {
                Stream < String > original = Stream . of ( "10" , "12" , "18" );
                Stream < Integer > result = original . map ( (String str )‐> Integer . parseInt ( str ));
        }
}

4.统计个数:count (终结方法)

long count ();

 基本使用

import java . util . stream . Stream ;
public class Demo09StreamCount {
        public static void main ( String [] args ) {
                Stream < String > original = Stream . of ( " 张无忌 " , " 张三丰 " , " 周芷若 " );
                Stream < String > result = original . filter ( s ‐> s . startsWith ( " " ));
                System . out . println ( result . count ()); // 2
        }
}

5.取用前几个:limit  

Stream < T > limit ( long maxSize );

 基本使用

 import java.util.stream.Stream;

public class Demo10StreamLimit {
        public static void main ( String [] args ) {
                Stream < String > original = Stream . of ( " 张无忌 " , " 张三丰 " , " 周芷若 " );
                Stream < String > result = original . limit ( 2 );
                System . out . println ( result . count ()); // 2
        }
}

 6.跳过前几个:skip

Stream<T> skip(long n);

  基本使用

import java . util . stream . Stream ;
public class Demo11StreamSkip {
        public static void main ( String [] args ) {
                Stream < String > original = Stream . of ( " 张无忌 " , " 张三丰 " , " 周芷若 " );
                Stream < String > result = original . skip ( 2 );
                System . out . println ( result . count ()); // 1
        }
}

7.组合:concat  

static < T > Stream < T > concat ( Stream <? extends T > a , Stream <? extends T > b )

基本使用  

import java . util . stream . Stream ;
public class Demo12StreamConcat {
        public static void main ( String [] args ) {
                Stream < String > streamA = Stream . of ( " 张无忌 " );
                Stream < String > streamB = Stream . of ( " 张翠山 " );
                Stream < String > result = Stream . concat ( streamA , streamB );
        }
}

第二章 方法引用  (对Lambda表达式进行优化)

2.1 冗余的Lambda场景

@FunctionalInterface
public interface Printable {
        void print ( String str );
}
Printable 接口当中唯一的抽象方法 print 接收一个字符串参数,目的就是为了打印显示它。那么通过 Lambda来使用它的代码很简单:
public class Demo01PrintSimple {
        private static void printString ( Printable data ) {
                data . print ( "Hello, World!" );
        }
public static void main ( String [] args ) {
                printString ( s ‐> System . out . println ( s ));
        }
}

 2.2 问题分析

有现成的打印方法,我们可以直接调用

System.out
println(String)

2.3 用方法引用改进代码

public class Demo02PrintRef {
        private static void printString ( Printable data ) {
                data . print ( "Hello, World!" );
}
public static void main ( String [] args ) {
        printString ( System . out :: println );
        }
}

 请注意其中的双冒号 :: 写法,这被称为方法引用,而双冒号是一种新的语法

2.4 方法引用符

:: 为引用运算符。而它所在的表达式被称为 方法引用 如果 Lambda 要表达的函数方案已经存在于某个方 法的实现中,那么则可以通过双冒号来引用该方法作为 Lambda 的替代者。
  • Lambda表达式写法: s -> System.out.println(s);
  • 方法引用写法: System.out::println
第一种语义是指:拿到参数之后经 Lambda 之手,继而传递给 System.out.println 方法去处理。
第二种等效写法的语义是指:直接让 System.out 中的 println 方法来取代 Lambda 。两种写法的执行效果完全一样,而第二种方法引用的写法复用了已有方案,更加简洁。
注:Lambda 中 传递的参数 一定是方法引用中 的那个方法可以接收的类型,否则会抛出异常

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

这是最常见的一种用法,如果一个类中已经存在了一个成员方法:
public class MethodRefObject {
        public void printUpperCase ( String str ) {
                System . out . println ( str . toUpperCase ());
        }
}

 

函数式接口仍然定义为:

@FunctionalInterface
public interface Printable {
        void print ( String str );
}

通过对象名引用方法,

使用前提是对象名已经存在,成员方法也已经存在

就可以使用对象名引用成员方法

 public static void main(String[] args) {
        //调用printString方法,方法的参数Printable是一个函数式接口,所以可以传递Lambda表达式
        printString((s)->{
            //创建MethodRerObject对象
            MethodRerObject obj = new MethodRerObject();
            //调用MethodRerObject对象中的成员方法printUpperCaseString,把字符串按照大写输出
            obj.printUpperCaseString(s);
        });

        /*
            使用方法引用优化Lambda
            对象是已经存在的MethodRerObject
            成员方法也是已经存在的printUpperCaseString
            所以我们可以使用对象名引用成员方法
         */
        //创建MethodRerObject对象
        MethodRerObject obj = new MethodRerObject();
        printString(obj::printUpperCaseString);
    }

2.6 通过类名称引用静态方法

@FunctionalInterface
public interface Calcable {
        int calc ( int num );
}
Lambda 表达式:
public class Demo05Lambda {
        private static void method ( int num , Calcable lambda ) {
                System . out . println ( lambda . calc ( num ));
}
public static void main ( String [] args ) {
        method ( 10 , n ‐> Math . abs ( n ));
        }
}

 

更好写法是:

public class Demo05Lambda {
        private static void method ( int num , Calcable lambda ) {
                System . out . println ( lambda . calc ( num ));
}
public static void main ( String [] args ) {
       method ( 10 , Math :: abs );
        }
}

 

在这个例子中,下面两种写法是等效的:
  • Lambda表达式: n -> Math.abs(n)

  • 方法引用: Math::abs

2.7 通过super引用成员方法

@FunctionalInterface
public interface Greetable {
        void greet ();
}

然后是父类 Human 的内容:

 public class Human {

        public void sayHello () {
                System . out . println ( "Hello!" );
        }
}

最后是子类 Man 的内容,其中使用了Lambda的写法:

public class Man extends Human {
        @Override
        public void sayHello () {
                System . out . println ( " 大家好 , 我是 Man!" );
}
// 定义方法 method, 参数传递 Greetable 接口
public void method ( Greetable g ){
        g . greet ();
}
public void show (){
        //调用 method 方法 , 使用 Lambda 表达式
        method (() ‐> {
        //创建 Human 对象 , 调用 sayHello 方法
                new Human (). sayHello ();
});
// 简化 Lambda
        method (() ‐> new Human (). sayHello ());
        //使用 super 关键字代替父类对象
        method (() ‐> super . sayHello ());
        }
}

 但是如果使用方法引用来调用父类中的 sayHello 方法会更好,

public class Man extends Human {
        @Override
        public void sayHello () {
                System . out . println ( " 大家好 , 我是 Man!" );
}
// 定义方法 method, 参数传递 Greetable 接口
        public void method ( Greetable g ){
                g . greet ();
        }
        public void show (){
                method ( super :: sayHello );
        }
}
  • Lambda表达式: () -> super.sayHello()

  • 方法引用: super::sayHello

2.8 通过this引用成员方法  

this::成员方法

@FunctionalInterface
public interface Richable {
        void buy ();
}

 

丈夫 Husband 类:

public class Husband {
        private void marry ( Richable lambda ) {
                lambda . buy ();
        }
public void beHappy () {
        marry (() ‐> System . out . println ( " 买套房子 " ));
        }
}

public class Husband {
        private void buyHouse () {
                System . out . println ( " 买套房子 " );
        }
        private void marry ( Richable lambda ) {
        lambda . buy ();
        }
        public void beHappy () {
        marry (() ‐> this . buyHouse ());
        }
}
如果希望取消掉 Lambda 表达式,用方法引用进行替换,则更好的写法为:
public class Husband {
        private void buyHouse () {
                System . out . println ( " 买套房子 " );
        }
        private void marry ( Richable lambda ) {
        lambda . buy ();
        }
        public void beHappy () {
        marry ( thisbuyHouse ());
        }
}
在这个例子中,下面两种写法是等效的:
  • Lambda表达式: () -> this.buyHouse()

  • 方法引用: this::buyHouse

2.9 类的构造器引用

Person 类:
package com.itheima.demo09.ConstructorMethodReference;

public class Person {
    private String name;

    public Person() {
    }

    public Person(String name) {
        this.name = name;
    }

    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }
}
Person 对象的函数式接口:
public interface PersonBuilder {
        Person buildPerson ( String name );
}

要使用这个函数式接口,可以通过Lambda表达式:

 

public class Demo09Lambda {
        public static void printName ( String name , PersonBuilder builder ) {
                System . out . println ( builder . buildPerson ( name ). getName ());
}
public static void main ( String [] args ) {
        printName ( " 赵丽颖 " , name ‐> new Person ( name ));
        }
}

但是通过构造器引用,有更好的写法:

 

public class Demo10ConstructorRef {
        public static void printName ( String name , PersonBuilder builder ) {
                System . out . println ( builder . buildPerson ( name ). getName ());
}
public static void main ( String [] args ) {
        printName ( " 赵丽颖 " , Person :: new );
        }
}

在这个例子中,下面两种写法是等效的:

  • Lambda表达式: name -> new Person(name)

  • 方法引用: Person::new

 2.10 数组的构造器引用

@FunctionalInterface
public interface ArrayBuilder {
        int [] buildArray ( int length );
}
public class Demo11ArrayInitRef {
        private static int [] initArray ( int length , ArrayBuilder builder ) {
        return builder . buildArray ( length );
}
public static void main ( String [] args ) {
        int [] array = initArray ( 10 , length ‐> new int [ length ]);
        }
}

 

但是更好的写法是使用数组的构造器引用:  

public class Demo12ArrayInitRef {
        private static int [] initArray ( int length , ArrayBuilder builder ) {
                return builder . buildArray ( length );
}
public static void main ( String [] args ) {
        int [] array = initArray ( 10 , int []:: new );
        }
}
在这个例子中,下面两种写法是等效的:
  • Lambda表达式: length -> new int[length]

  • 方法引用: int[]::new

 

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

LI JS@你猜啊

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值