Java SE : java8新特性

编译软件:IntelliJ IDEA 2019.2.4 x64
运行环境:win10 家庭中文版
jdk版本:1.8.0_361



一. 函数式编程思想

在数学中,函数就是有输入量、输出量的一套计算方案,也就是“拿什么东西做什么事情”。编程中的函数,也有类以的概念,你调用我的时候,给我实参为形参赋值,然后通过运行方法体,给你返回一个结果。

对于调用者来做,关注汶个方法具备什么样的功能。相对而言,面向对象过分强调"必须涌过对象的形式来做事情",而函数式思想则尽量忽略面向对象的复杂语法一一强调做什么,而不是以什么形式做。

面向对象的思想:

做一件事情,找一个能解决这个事情的对象调用对象的方法,完成事情

函数式编程思想:

只要能获取到结果,谁去做的,怎么做的都不重要,重视的是结果,不重视过程


二. 函数式接口

函数式接口是指一种特殊的接口,SAM接口 (Single Abstract Method) 只有唯一抽象方法的接口。

说明: 抽象只有唯一的一个,但是对于非抽象方法不限制。

函数式接口的概念时JDK1.8引入的,然后它除了要求接口是SAM特征之外,还强烈建议接口的声明上方加一个注解:

@FunctionalInterface 函数式接口的注解

回忆之前学过的接口中,哪些接口符合SAM接口特征的?
(1)java.util.Comparator int compare(T t1 ,T t2)

在这里插入图片描述

(2)java.lang.Comparable int compareTo(T t)

在这里插入图片描述

(3)java.lang.Runnable void run()

在这里插入图片描述

(4)java.util.function.Predicate boolean test()

在这里插入图片描述

(5)java.lang.Iterable<>接口 Iterator iterator()

(6)java.io.FileFilter boolean accept(File pathname)

在这里插入图片描述

java.io.Serializable接口,不是,没有抽象方法
java.Lang.CLoneabLe接口,不是,没有抽象方法
java.util.Iterator接口,不是,有两个抽象方法boolean hasNext()
E next)
java.util.Collection、List、Set、Oueue、Deque、Map<K,V>,不是,有很多抽象方法

上面这些SAM接口中,哪些有@FunctionalInterface注解:
(1)java.util.Comparator int compare(T t1 ,T t2)

(2)java.lang.Runnable void run() boolean test() (这个其实也是新的)

(3)java.util.function .Predicate

(4) java.io.FileFilter boolean accept(File pathname)

Java8在java.util.function包中新增了很多很多的函数式接口。

三. 新版函数式接口的四大经典代表

(1) 消费型接口代表

Consumer
抽象方法: void accept(T t)

特点: 有一个参数,返回值是void,表示无返回值。

调用这个抽象方法时,相当于你要给它一个实参,但是得不到返回值,相当于 有去无回,纯消费行为。

(2) 供给型接口代表/泰献型接口代表

Supplier

抽象方法 T get()

特点:没有参数,有返回值

调用这个抽象方法时,相当于你不用给它传参数,却可以得到一个返回值,相当于 空手套白狼。

(3)判断型接口/断定型接口代表

Predicate

抽 象方法 boolean test(T t)

特点: 有一个参数,返回值类型是固定的boolean

调用这个抽象方法时,相当于你给它一个参数之后,它会告诉你这个参数是否满足xx条件,满足就返回true,否则就返回false

(4)功能型接口代表

Function<T,R>

抽象方法 RLapply(T t)

特点是:有一个参数,有一个返回值

调用这个抽象方法时,相当于你要给它一个实参,同时也 可以得到一个返回值,相当于礼尚往来

四. Lambda 表达式

4.1 Lambda表达式的作用

给函数式接口的变量或形参赋值用的。传递一段代码这段代码本质上就是西数式接口的抽象方法的方法体。

4.2 语法格式

(形参列表) ->{Lambda体)

(形参列表) : 是函数式接口的抽象方法的形参列表

->: 称为Lambda操作符,中间不能有空格

{Lambda体: 就是函数式接口的抽象方法的方法体

代码演示如下

//为何不用JUite的test方法去测试,该方法有一个缺点,在其内部使用多线程,有可能test里的线程还没启动就被test干掉了
//相比而言,main方法会等待里面的线程全部执行完,相比较而言,main方法比较保险
public static void main(String[] args) {
    //使用lambda表达式对Runnable接口的变量赋值。要实现打印一句话: hello lambda
    //Runnable接口的抽象方法: void run()

    Runnable r= () -> {
        System.out.println("hello lambda");  //里面的;是输出语句的
    }; //外面的;是 赋值语句 + lambda表达式的
    new Thread(r).start(); //完全等价于下面的代码

    System.out.println("-----------------");
    new Thread(new Runnable() {
        @Override
        public void run() {
            System.out.println("hello lambda");
        }
    }).start();

}

在这里插入图片描述

4.3 Lambda表达式在某些情况下,可以简化

(1)当{Lambda体}里面只有一个语句时,那么可以省略 {},同时省略里面的;

代码演示如下:

 	Runnable r= () -> {
        System.out.println("hello lambda");  //里面的;是输出语句的
    }; //外面的;是 赋值语句 + lambda表达式的
    new Thread(r).start();


@Test
public void test01(){
    //精简如下
    Runnable r= () -> System.out.println("hello lambda");
    new Thread(r).start();
}

(2)当Lambda体)里面只有一个语句时,那么可以省略,同时省略里面的;如果此时这个语句是一个return语句,那么要连同return省掉

代码演示如下:

//案例:在一个全是字符串的集合中,删除 包含“o”字母的单词
ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("atguigu");
list.add("java");
list.add("bigdata");
//删除 包含“o”字母的单词
//匿名类的形式
list.removeIf(new Predicate<String>() {
    @Override
    public boolean test(String s) {
        return s.contains("o");
    }
});
System.out.println(list);


 //从匿名类转换为lambda表达式
        list.removeIf(
                (String s) -> {
                    return s.contains("o");
                }
        );

        //上面的lambda式子精简如下
        list.removeIf(
               (String s) -> s.contains("o")
        );
        System.out.println(list);


在这里插入图片描述

(3)当(形参列表)的形参类型是已知的,或者可以自动推断的,那么形参列表的类型可以省略

代码演示如下:

//接上面的案例
//精简如下
list.removeIf(
        (s) -> s.contains("o")
);

💡如果此时省略了形参的数据类型之后,只剩下一个形参,它是这样的(形参名),那么此时()也可以省略

代码演示如下:

//再精简lambda表达式
list.removeIf(
        s -> s.contains("o")
);

在这里插入图片描述

🔔说明:

如果形参列表是(),()不能省略

如果形参不止一个,()也不能省略

如果Lambda体中不止一个语句等也不能省略。

代码演示如下:

@Test
public void test03(){
    String[] arr = {"hello","Bob","Rose","java","chai"};
    //使用匿名类
    Arrays.sort(arr, new Comparator<String>() {
        @Override
        public int compare(String o1, String o2) {
            return o1.compareToIgnoreCase(o2);
        }
    }
    );

    //使用lambda表达式
    Arrays.sort(arr, (o1,o2) -> o1.compareToIgnoreCase(o2) );

}

五. 消费型接口与lambda 表达式

消费型接口Consumer抽象方法:
void accept(T t)

有参无返回值

例子:

JDK1.8 java.Lang.terable接口增加了一个默认方法,

public default void forEach(Consumer<? super T> action)
Iterable接口增加了方法,意味着所有的CoLLection系列的集合都有这个方法。因为ColLection接口继承了Iterable接口

代码演示如下:

 @Test
    public void test04(){

        ArrayList<String> list = new ArrayList<>();list.add("hello");
        list.add("world");
        list.add("atguigu");
        list.add("java");
        list .add("bigdata");

        //forEach方法的功能是,遍历集合,并对集合的每一个元素做xx事情,具体做什么事情
        // 由Consumer接口的accept抽象方法决定
        //使用匿名内部类的形式
        /*list.forEach(new Consumer<String>() {
            @Override
            public void accept(String s) {
                System.out.println(s);
            }
        });
*/
        //使用lambda表达式
        list.forEach((s) -> System.out.println(s) );




    }

在这里插入图片描述


六. 供给型接口与lambda表达式

供给型接口Supplier 抽象方法:

T get() 无参有返回值

例如: 使用Lambda表达式给一个Supplier类型的变量赋值一个“轨道”

代码演示如下:

@Test
public void test05(){
    Supplier<String> s= ()-> {return "轨道";};
    //镜检如下
    Supplier<String> str= ()-> "轨道";
    methiod(s);
    
    //注意以下,在方法()中并非为lambda式的精简缩写,而是str的替换
    methiod(()-> "轨道");
    
}

//泛型方法
public <T> void  methiod(Supplier<T> s){
    System.out.println(s.get());

}

在这里插入图片描述


七. 判断型接口与lambda表达式

案例:在一个全是字符串的集合中,删除 包含“o”字母的单词

代码演示如下:

ArrayList<String> list = new ArrayList<>();
list.add("hello");
list.add("world");
list.add("atguigu");
list.add("java");
list.add("bigdata");
//删除 包含“o”字母的单词
//匿名类内部类的形式
list.removeIf(new Predicate<String>() {
    @Override
    public boolean test(String s) {
        return s.contains("o");
    }
});

//精简如下
list.removeIf(
        s -> s.contains("o")
);


System.out.println(list);




八. 功能型接口与lambda表达式

案例一:使用Lambda表达式,给一个Function接口的变量赋值完成给某个字符串的首字母转为大写。

思路:

把字符串的首字母变为大写怎么实现?

(1) 把字符串的首字母拿出来,转为大写 (2) 把转换后的首字母 拼接上原来字符串除了首字母的部分,例如"heLlo"
把h拿出来,转为H,然后用H 与eLLo"拼接

Function<T,R> 抽象方法 R apply(T t) 有参有返回值

代码演示如下:

@Test
public void test06(){
    //某个字符串的首字母转为大写
    //Function<T,R> T是指定形参类型,R是指定返回值类型
    Function<String,String> f= s -> Character.toUpperCase(s.charAt(0))+s.substring(1);
    System.out.println(f.apply("hello"));
    
}

在这里插入图片描述

JDK1.8中Map接口新增了一个方法:

default void replaceAll(BiFunction<? super K,? super V,? extends V> function)
BiFunction<T,U,R>函数式接口,它的抽象方法 R apply(T t,u u)有2个参数,一个返回值。

案例二:将所有的value转换为大写

代码演示如下:

@Test
public void test07(){
    //将所有的value转换为大写
    HashMap<Integer,String> map=new HashMap<>();
    map.put(1,"jack");
    map.put(2,"hello");

    map.replaceAll((key,value) -> value.toUpperCase());
    System.out.println(map);


}

在这里插入图片描述


九. lambda 表达式综合案例

9.1 案例一

创建一个ArrayList,并添加26个小写字母到List中,并使用forEach遍历输出

方法思路:

第一步:

搞清楚forEach 方法的方法签名
方法 = 方法头 + 方法体,方法头又被称为方法签名。
方法头:[修饰符] 返回值类型
方法名([形参列表] )[throws 异常列表] 方法体:[语句代码}

void forEach(Consumerk? super E> action)搞清楚Consumer接口的抽象方法,它是一个函数式接口,就可以使用Lambda表达式

第二步:

抽象方法: void accept(T t) 【有参无返回值】
对于函数式接口的抽象方法来说,用Lambda表达式给他赋值时,不关心方法名,关心形参列表和返回值类型。

代码演示如下:

@Test
public void test08(){
    ArrayList<Character> list=new ArrayList<>();
    for (char i = 'a'; i <='z' ; i++) {
        list.add(i);
    }
    System.out.println(list);
    System.out.println("------------------");
    list.forEach(c -> System.out.print(c+"\t"));

}

在这里插入图片描述

9.2 案例二

创建一个HashMap,并添加如下编程语言排名和语言名称到map中,并使用forEach遍历输出

排名语言
1Java
2c
3python
4c++
5c#

代码演示如下:

@Test
public void test09(){

    HashMap<Integer,String> map=new HashMap<>();
    map.put(1,"java");
    map.put(2,"c");
    map.put(3,"python");
    map.put(4,"c++");
    map.put(5,"c#");
    map.forEach((key,value) -> System.out.println(key+":"+value));


}

在这里插入图片描述

9.3 案例三

使用Lambda表达式给Supplier接口的变量赋值,实现产生1个100以内的整数功能。

代码演示如下:

@Test
public void test10(){
    //使用Lambda表达式给Supplier接口的变量赋值,实现产生1个100以内的整数功能。
    Supplier<Integer> s= ()-> new Random().nextInt(100);
    System.out.println(s);//打印s是得不到这个随机数值的
    System.out.println(s.get());//通过调用s.get()得到随机数值


}

在这里插入图片描述

9.4 案例四

声明一个Employee员工类型,包含属性编号、姓名、薪资,属性私有化,提供有参构造,get/set,重写toString.
添加n个员工对象到一个HashMap<lnteger,Employee>集合中,其中员工编号为key,员工对象为value。
调用Map的forEach遍历集合
调用Map的replaceAll方法,将其中薪资低于10000元的,薪资设置为10000。
再次调用Map的forEach遍历集合查看结果

代码演示如下:

public class Employee {
    private int id;
    private String name;
    private int salary;

    @Override
    public String toString() {
        return "Employee{" +
                "id=" + id +
                ", name='" + name + '\'' +
                ", salary=" + salary +
                '}';
    }

    public int getId() {
        return id;
    }

    public void setId(int id) {
        this.id = id;
    }

    public String getName() {
        return name;
    }

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

    public int getSalary() {
        return salary;
    }

    public void setSalary(int salary) {
        this.salary = salary;
    }

    public Employee(int id, String name, int salary) {
        this.id = id;
        this.name = name;
        this.salary = salary;
    }
}
@Test
public void test11(){
    HashMap<Integer,Employee> map=new HashMap<>();
    Employee e1=new Employee(1,"张三",6700);
    Employee e2=new Employee(2,"李四",9700);
    Employee e3=new Employee(3,"王五",16000);
    Employee e4=new Employee(4,"赵六",27000);
    map.put(e1.getId(),e1);
    map.put(e2.getId(),e2);
    map.put(e3.getId(),e3);
    map.put(e4.getId(),e4);
    map.forEach((key,value)-> System.out.println(map.get(key)));
    System.out.println("--------------------------------------");
    map.replaceAll((key,value)-> {
        if (map.get(key).getSalary()<10000){
            map.get(key).setSalary(10000);
        }
        return value;
    });
    map.forEach((key,value)-> System.out.println(map.get(key)));




}

在这里插入图片描述


十. 方法引用与构造器引用

10.1 方法引用

10.1.1 什么是方法引用?

它是指一种新的语法格式:

类名 :: 方法名
对象名 :: 方法名

10.1.2 方法引用用在哪里?

作用: 方法引用是用于简化Lambda表达式的语法。

10.1.3 方法引用可使用的情况

注意:

不是所有的Lambda表达式都可以用方法引用进行简化的,只有满足以下情况时,才能用它进行简化:

1)Lambda体只有一个语句并且这个语句是通过调用一个现有的类或对象的方法来完成的
并且在调用方法时,所使用的实参正好时Lambda表达式的形参,整个使用过程中,没有额外的数据出现。

代码演示如下:

@Test
public void test12(){
    ArrayList<String> list=new ArrayList<>();
    list.add("jack");
    list.add("hello");
    list.add("rose");
    list.add("tom");
    list.forEach(t -> System.out.println(t));//使用lambda进行遍历,可进行方法引用以简化
    list.forEach(t -> System.out.println(t+"\t"));//不能根据这个lambda进行方法引用
    System.out.println("--------------------------");
    list.forEach(System.out:: println );//使用方法引用
    
}

在这里插入图片描述

(2) Lambda体只有一个语句,并且这个语句是通过调用一个现有的对象的方法来完成的。
此时调用方法的对象是Lambda表达式的第一个形参,并且Lambda表达式的剩下的形参,正好作为该方法的实参,

整个使用过程中,没有额外的数据出现

代码演示如下:

@Test
public void test13(){
    String[] arr = {"hello","Bob","Rose","java","chai"};
    Arrays.sort(arr, (o1,o2) -> o1.compareToIgnoreCase(o2) );
    for (String s : arr) {
        System.out.println(s);
    }
    System.out.println("-----------------------");
    String[] arr1 = {"hello","Bob","Rose","java","chai"};
    Arrays.sort(arr1, String::compareToIgnoreCase );
    for (String s : arr) {
        System.out.println(s);
    }
}

在这里插入图片描述

10.2 构造器引用

当Lambda表达式是一个创建对象的表达式,即Lambda体是一个new表达式,而且 所有构造器的实参,都是Lambda表达式的形参,那么此时就可以使用构造器引用。

语法格式:

类名 :: new

代码演示如下:

    @Test
    public void test14(){
/*        功能型接口Function<T,R>,有一个抽象方法 R apply(T t)
        希望它通过某个字符串,作为Person对象的name值,创建一个Person对象*/
//        Function<String,Person> fun= s -> new Person(s);
        Function<String,Person> fun= Person::new;

        String[] names={"jack","rose","tom"};
        //根据names的字符串,创建一个Person数组
        Person[] people=new Person[names.length];
        for (int i = 0; i < names.length ; i++) {
            people[i]=fun.apply(names[i]);
        }

        for (Person person : people) {
            System.out.println(person);
        }


    }

在这里插入图片描述

10.3 数组引用

当Lambda表达式是创建一个数组对象,并且满足Lambda表达式形参正好是给创建这个数组对象的长度,就可以数组构造引用

语法格式:

数组类型名: :new

代码演示如下:

	@Test
    public void test15(){
/*        如果使用Lambda表达式给一个Function接口的变量赋值
        需求: 给定一个长度,你返回一个对应长度的String[]数组给我*
        功能型接口Function<T,R>  有一个抽象方法 R apply(T t)
        形参的类型: Integer
        返回值类型: String[]*/
//        Function<Integer,String[]> fun=l -> new String[l];
        Function<Integer,String[]> fun= String[]::new;
        String[] str=fun.apply(5);
        for (String s : str) {
            System.out.print(s+"\t");
        }

    }

在这里插入图片描述


十一. Stream API

11.1 stream是什么?

stream这个单词,在之前IO流的文章就提到过。在在IO流中是代表一个字节输入流或一个字节输出流,它是用于数据的传输的今天讲的这个stream也是代表一个数据流,但是这个数据流和之前的IO流是有区别的;

今天说的stream它是指用于数据“加工”的一套流程。例如:数据的过滤、数据的统计、数据的迭代、数据的修改、删除、查询筛选等

stream本身是不存储数据的,存储数据的是数组、集合这样的容器。

11.2 特点

(1) stream本身是不存储数据的

(2) stream每一次加工处理都会产生一个新的stream对象

代码演示如下:

 @Test
    public void test16(){
        ArrayList<String> list=new ArrayList<>();
        list.add("jack");
        list.add("option");
        list.add("java");
        list.add("world");
        list.add("h5");

        //1)创建stream
        //通过集合创建Stream
        Stream<String> stream = list.stream();
     /*   stream.filter(new Predicate<String>() { //Predicate接口为判断式,可使用lambda
            @Override
            public boolean test(String s) {
                return s.contains("o");
            }
        });*/

        //2)中间加工处理
        //筛选含”o“的单词
        //lambda表达式
        Stream<String> o = stream.filter(s -> s.contains("o"));//原先得Steream流stream本身不会存储任何数据且每一次加工处理都会产生一个新的Stream流对象
        

        //3)终结
        //遍历流中剩余的元素
//        o.forEach(s -> System.out.println(s));
        o.forEach(System.out::println);


        System.out.println("list:"+list);//list:[jack, option, java, world, h5]  不会改变源数据
    }

在这里插入图片描述

(3) stream的中间处理/加工操作会被延迟,一直要到最后取结果的“终结作”才会执行。

不加”终结操作“前后对比:

没加代码演示如下:

 @Test
    public void test17(){
        ArrayList<Student> list=new ArrayList<>();
        list.add(new Student("KFC"));
        list.add(new Student("dragon"));

        //1)创建流对象
        Stream<Student> stream = list.stream();

        //2)加工处理
        //peek():peek(Consumer<? super T> action)
        //返回由该流的元素组成的流,另外在从生成的流中消耗元素时对每个元素执行提供的操作。
        Stream<Student> peek = stream.peek(System.out::println);

        //3)终结操作
        //3)没写得时间,前面得代码都没执行
/*
        long nums=peek.count();//统计流中的元素的个数
        //peek.count()有返回值,不是stream,所以它是一个终结操作
        System.out.println(nums);
*/


    }

在这里插入图片描述

加了 代码演示如下:

@Test
public void test17(){
    ArrayList<Student> list=new ArrayList<>();
    list.add(new Student("KFC"));
    list.add(new Student("dragon"));

    //1)创建流对象
    Stream<Student> stream = list.stream();

    //2)加工处理
    //peek():peek(Consumer<? super T> action)
    //返回由该流的元素组成的流,另外在从生成的流中消耗元素时对每个元素执行提供的操作。
    Stream<Student> peek = stream.peek(System.out::println);

    //3)终结操作
    //3)没写得时间,前面得代码都没执行
    long nums=peek.count();//统计流中的元素的个数
    //peek.count()有返回值,不是stream,所以它是一个终结操作
    System.out.println(nums);


}

在这里插入图片描述

(4) stream的加工处理不会改变的数据源(集合和数组的元素,个数等)

11.3 如何使用stream?

(1) 创建Stream

创建Stream的几种方法:

①通过集合对象.stream

JDK1.8在
ColLection系列集合中增加了方法:default Stream stream()

②通过数组工具类Arrays.stream方法

代码演示如下:

@Test
public void test18(){
    //通过Arrays。stream()创建
    int[] a={1,2,4,5,10};
    IntStream stream = Arrays.stream(a);
}

③Stream接口中有这样的一些静态方法,可以创建

Streamof(T… value) : 有限流

static Stream generate(Supplier s) : 无限流

static Stream iterate(T seed, UnaryOperator f):无限流

代码演示如下:

@Test
public void test19(){

    Stream<String> jack = Stream.of("jack", "hello", "world");
    Stream<Integer> integerStream = Stream.of(1, 3, 5, 2);
    
}
 @Test
    public void test20(){
        //static <T> Stream<T> generate(Supplier<T> s)
        // 返回无限顺序无序流,其中每个元素由提供的Supplier 。 这适合于产生恒定流,随机元素流
        Stream<Double> generate = Stream.generate(Math::random);
        generate.forEach(s -> System.out.println(s));

    }

在这里插入图片描述

 @Test
    public void test21(){
/*        static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)
         返回有序无限连续Stream由函数的迭代应用产生f至初始元素seed ,产生Stream包括seed , f(seed) , f(f(seed)) ,等
        第一元件(位置0在) Stream将是提供seed 。 对于n > 0 ,位置n的元素将是将函数f应用于位置n - 1的元素的n - 1 。

        参数类型
        T - 流元素的类型
        参数
        seed - 初始元素
        f - 要应用于前一个元素以生成新元素的函数
        结果
        一个新的顺序 Stream*/

/*

        Function<T,R>接口抽象方法 R apply(T t);参数的类型和返回值的类型可以不同
        UnaryOperator<T> 接口抽象方法 T apply(T t)参数的类型和返回值的是一样的
*/
        //需求:对每一个元素,进行迭代处理,从第一个元素开始,每次迭代都+2,然后一直在上一次的基础上,不断迭代。

        Stream<Integer> iterate = Stream.iterate(1, t -> t + 2);
        iterate=iterate.peek(t->{  //让流中的数据每隔100ms产生一个
            try {
                Thread.sleep(100);
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });
        iterate.forEach(t-> System.out.println(t));
    }

在这里插入图片描述

(2) 中间加工处理

处理方法如下:

在这里插入图片描述

代码演示如下:

@Test
public void test22(){
    //过滤所有的偶数
    Stream.of(1,2,3,4,5) //创建流
            .filter(t -> t%2==0)  //中间加工处理
            .forEach(t -> System.out.println(t)); //终结操作
}

在这里插入图片描述

@Test
public void test23(){
    //去掉所有的重复数字
    Stream.of(1,2,3,4,5,2,4,6,5,3,1) //创建流
            .distinct()  //中间加工处理
            .forEach(t-> System.out.println(t)); //终结操作
}


 @Test
    public void test23(){
        //去掉所有的重复数字
        Stream.of(1,2,3,4,5,2,4,6,5,3,1) //创建流
                .distinct()  //中间加工处理
                .forEach(t-> System.out.print(t+"\t")); //终结操作
    }

在这里插入图片描述

@Test
public void test24(){
    //去掉所有的重复数字 且 只留偶数
    //中间加工处理绝不仅仅只能写一步,它可以写很多步
    Stream.of(1,2,3,4,5,2,4,6,5,3,1) //创建流
            .distinct()  //中间加工处理
            .filter(t-> t%2==0) //中间加工处理
            .forEach(t-> System.out.print(t+"\t")); //终结操作
}

在这里插入图片描述

@Test
public void test25(){
    //在初始元素为1,元素累加2的无限流中,取前10个
    Stream.iterate(1,t->t+2)
            .limit(10)//取无限流中前10个
            .forEach(System.out::println);
}

在这里插入图片描述

@Test
public void test26(){
    //在初始元素为1,元素累加2的无限流中,跳过前10个,并只取前10个
    Stream.iterate(1,t->t+2)
            .skip(10)//跳过无限流中前10个
            .peek(t->{  //数据每隔200ms出现一次,使之不要出现的那么的快
                try {
                    Thread.sleep(200);
                } catch (InterruptedException e) {
                    e.printStackTrace();
                }
            })
            .limit(10) //只取前10个
            .forEach(System.out::println);


}

在这里插入图片描述

@Test
public void test27(){
    //统计这组数据中的偶数个数,是不重复的偶数,并且要打印出来
    long count=Stream.of(1,2,3,4,5,2,4,6,5,3,1) //创建流
            .filter(t -> t%2==0)  //  过滤偶数  中间加工处理
            .distinct()  //去重复
            .peek(t-> System.out.print(t+"\t")) //中间加工
            .count();
    System.out.println();
    System.out.println("偶数个数:"+count);
}

在这里插入图片描述

@Test
public void test28(){
    //从小到大排序
    Stream.of(1,5,2,89,45,32,9,12)  //创建流
            .sorted()  //中间加工 排序
            .forEach(System.out::println); //终结
}

在这里插入图片描述

@Test
public void test29(){
    ArrayList<Student> list=new ArrayList<>();
    list.add(new Student("kfc"));
    list.add(new Student("dragon"));
    //按照名字排序
    list.stream()  //创建流
            .sorted((s1,s2)-> s1.getName().compareTo(s2.getName()))//中间加工 排序
            .forEach(t -> System.out.println(t)); //终结


}

在这里插入图片描述

@Test
public void test30(){
    //将原来流中的数据变成它的2倍并打印
    Stream.of(1,2,3,4,5)//创建流
            .map(t-> t*2) //中间加工
            .forEach(System.out::println); //终结
}

在这里插入图片描述

@Test
public void test31(){
    //取出单词的首字目
    Stream.of("hello","world","java")
            .map(t -> t.charAt(0) )
            .forEach(System.out::println);
}

在这里插入图片描述

@Test
public void test32(){
    //把字符串每个字符串起来,变成一个新的流
   /*
    <R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
    Function<TR>是功能型函数式接口 抽象方法 R apply(T t)
        T:  ? super T
        R : ? extends Stream?extends R> 说明function函数使用后,apply方法结果要是一个stream对象
    <R> Stream<R> map(Function<? super T, ? extends R> mapper);
        T: ? super T
        R: ? extends R

    */
    Stream.of("hello","world","java")
            .flatMap( t-> {
                char[] chars = t.toCharArray();
                Character[] c = new Character[chars.length];
                for (int i = 0; i < chars.length; i++) {
                    c[i] = chars[i];
                }
                return Arrays.stream(c);
            })
            .forEach(System.out::println);
}

在这里插入图片描述

(3) 终结操作,它的方法的返回值不再是一个流,而是其他的类型

注意:

中间加工处理的方法返回值结果仍然是stream类型。
终结操作处理的方法返回值结果就不再是stream类型。表示之后无法再进行流处理。

具体的方法:

方法描述
boolean allMatch(Predicate p)检查是否匹配所有元素
boolean anyMatch(Predicate p)检查是否至少匹配一个元素
boolean noneMatch(Predicate p)检查是否没有匹配所有元素
Optional findFirst()返回第一个元素
Optional findAny()返回当前流中的任意元素
long count()返回流中元素总数
Optional max(Comparator c)返回流中最大值
Optional min(Comparator c)返回流中最小值
void forEach(Consumer c)迭代
T reduce(T iden, BinaryOperator b)可以将流中元素反复结合起来,得到一个值。返回 T
U reduce(BinaryOperator b)可以将流中元素反复结合起来,得到一个值。返回 Optional
R collect(Collector c)将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法

代码演示如下:

@Test
public void test33(){
    //判断是否全部为奇数
   boolean b= Stream.of(1,2,3,4,5,2,4,6,5,3,1)//创建流
            .allMatch(t -> t%2!=0); //终结
    System.out.println("是否全部为奇数:"+b);
}

在这里插入图片描述

@Test
public void test34(){
    Optional<Integer> first = Stream.of(1, 2, 3, 4, 5, 2, 4, 6, 5, 3, 1)//创建流
            .findFirst(); //终结
    System.out.println("第一个:"+first);
}

@Test
public void test35(){
    //对于稳定的流(元素稳定且固定),findAny() 等价于 findFirst()
    Optional<Integer> any = Stream.of(1, 2, 3, 4, 5, 2, 4, 6, 5, 3, 1)//创建流
            .findAny(); //终结
    System.out.println("第一个:"+any);
}

在这里插入图片描述

@Test
public void test36(){
    //随机产生10个【0-100】的整数,找出最大值
    Optional<Integer> max= Stream.generate(() -> (new Random().nextInt(101)))
            .limit(10)
             .peek(t-> System.out.print(t+"\t"))
                .max(Integer::compareTo);
    System.out.println(max);


}

在这里插入图片描述

@Test
public void test37(){
    //累加和
    /*
    Optional<T> reduce(BinaryOperator<T> accumulator);
    public interface BinaryOperator<T> extends BiFunction<T,T,T>
    public interface BiFunction<T,U,R> 的抽象方法 R apply(T t,U u);
    BinaryOperator接口的抽象方法 T apply(T t,T u);

    */
    Optional<Integer> reduce = Stream.of(1, 2, 3, 4, 5)
            .reduce((t1, t2) -> t1 + t2);
    System.out.println(reduce);

}

在这里插入图片描述

 @Test
    public void test38(){
/*        ColLector 接口中方法的实现决定了如何对流执行收集的操作(如收集到 List、Set、Map)。
        另外, CoLLectors 实用类提供了很多静态方法,可以方便地创建常见收集器实例。*/
        ArrayList<String> list=new ArrayList<>();
        list.add("jack");
        list.add("hello");
        list.add("world");
        list.add("java");
        Set<String> a = list.stream()
                .filter(s -> s.contains("a"))
                .peek(s -> System.out.println(s))
                .collect(Collectors.toSet());
        System.out.println("list:"+list);
        System.out.println("set:"+a);

    }

在这里插入图片描述


十二.Optional 类

12.1 为什么要引入Optional类?

原因:

Java很多方法的返回值 (更多是返回值)
或形参是引用数据类型时,返回null。对方拿到这个数据时,没有做非空判断的话,会发生空指针异常。或者说是加非空判断f (xx !=
null)使得代码很臃肿。

引入了optional类来尽量的降低空指针异常的风险,又可以简化代码。

12.2 Optional类特点

(1) 它是一个容器
(2) 只包含一个对象的容器

12.3 如何使用它?

12.3.1 创建它

static Optional empty(): 得到一个空的容器对象,里面没有元素,容器对象有。staticOptionalof(T value) : 得到一个包含元素的容器对象,里面一定有元素,而且非null

代码演示如下:

@Test
public void test01(){
    Optional<Object> empty = Optional.empty();
    System.out.println(empty);
    Optional<String> java = Optional.of("java");
    System.out.println(java);
    Optional<Object> o = Optional.of(null);
    System.out.println(o);
}

在这里插入图片描述

static Optional ofNullable(T value) : 得到一个包含元素的容器对象,里面一定有元素,元素可以是非null。可以是null

代码演示如下:

@Test
public void test02(){
    String str=null;
    Optional<String> str1 = Optional.ofNullable(str);//允许为null
    System.out.println(str1);
}

12.3.2 取出容器中的数据

T get(): 要求容器中必须有一个非空对象
T orELse(T other) : 如果容器中有非空对象,就取容器中非空对象,如果没有,就用other备胎。

代码演示如下:

@Test
    public void test03(){
        Optional<Integer> maxOptional = Stream.of(1, 3, 5, 7, 9)
                .filter(t -> t % 2 == 0)
                .max(Integer::compareTo);
//        Integer integer = maxOptional.get();//java.util.NoSuchElementException: No value present  maxOptional里值为空
        Integer max = maxOptional.orElse(0);
        System.out.println(max);

    }

在这里插入图片描述

T orELseget(Supplier<? extends T>other): 如果容器中有非空对象,就取容器中非空对象,如果没有,就用Supplier接口提供的对象代替

代码演示如下:

  	@Test
    public void test04(){
        Random random=new Random();
        Optional<Integer> maxOptional = Stream.of(1, 3, 5, 7, 9)
                .filter(t -> t % 2 == 0)
                .max(Integer::compareTo);
//        Integer integer = maxOptional.get();//java.util.NoSuchElementException: No value present  maxOptional里值为空
        Integer max = maxOptional.orElseGet(() -> random.nextInt(100)); //若无偶数,则返回一个100以内的随机整数
        System.out.println(max);

    }

在这里插入图片描述

T orElseThrow(Supplier<? extends X> exceptionSupplier) :

如果Optional容器中非空,就返回所包装值,如果为空,就抛出你指定的异常类型代替原来的NoSuchElementException

代码演示如下:

	@Test
    public void test05(){
        Random random=new Random();
        Optional<Integer> maxOptional = Stream.of(1, 3, 5, 7, 9)
                .filter(t -> t % 2 == 0)
                .max(Integer::compareTo);
//        Integer integer = maxOptional.get();//java.util.NoSuchElementException: No value present  maxOptional里值为空
        Integer max = maxOptional.orElseThrow(() -> new RuntimeException("没有最大值")); //没有偶数,就抛出里面的异常
        System.out.println(max);

    }

在这里插入图片描述

12.3.3 其他

boolean isPresent() : 判断容器中有没有元素
void ifPresent(Consumer<? super T> consumer) : 如果存在就执行xx代码

代码演示如下:

 @Test
    public void test06(){
        Optional<Integer> maxOptional = Stream.of(1, 3, 5, 7, 9)
                .filter(t -> t % 2 == 0)
                .max(Integer::compareTo);
//        Integer integer = maxOptional.get();//java.util.NoSuchElementException: No value present  maxOptional里值为空
        maxOptional.ifPresent(s -> System.out.println(s)); //若有最大的偶数,就打印,没有就什么都不打印

    }

在这里插入图片描述


  • 4
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 3
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

陌上少年,且听这风吟

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

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

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

打赏作者

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

抵扣说明:

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

余额充值