lambda表达式

需求分析

创建一个新线程

package com.xqm.jdk8.lambda;

public class Demo1Lambda {

    public static void main(String[] args) {
        // 创建一个新线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("线程"+Thread.currentThread().getName()+"启动");
            }
        },"thread1").start();
        System.out.println("主线程"+Thread.currentThread().getName());
    }
}
代码分析:


1. Thread类需要一个Runnable接口作为参数,其中的抽象方法run方法是用来指定线程任务内容的核心
2. 为了指定run方法体,不得不需要Runnable的实现类
3. 为了省去定义一个Runnable 的实现类,不得不使用匿名内部类
4. 必须覆盖重写抽象的run方法,所有的方法名称,方法参数,方法返回值不得不都重写一遍,而且不能出错,
5. 而实际上,我们只在乎方法体中的代码

lambda表达式

1.lambda表达式是一个匿名函数,可以理解为一段可以传递的代码
2.lambda表达式的优点:简化了匿名内部类的使用,语法更加简单
3.简化代码
package com.xqm.jdk8.lambda;

public class Demo2Lambda {

    public static void main(String[] args) {
        // 创建一个新线程
        new Thread(()->{
            System.out.println("新线程Lambda表达式..." +Thread.currentThread().getName());
        }).start();
    }

}

lambda表达式语法规则

由三个部分组成:

(参数类型 参数名称) -> {
代码体;
}
格式说明:

- (参数类型 参数名称):参数列表
- {代码体;} :方法体
- -> : 箭头,分割参数列表和方法体

练习

无参返回

// 接口

package com.xqm.jdk8.lambda.service;

import java.util.function.Supplier;

public interface UserService {
    /**
     * 普通方法必须实现
     */
    void show();

    /**
     * 默认方法可以实现也可以不实现
     */
    default void show1(){};

}
// 测试类
package com.xqm.jdk8.lambda;

import com.xqm.jdk8.lambda.service.UserService;

/**
 * 无参
 */
public class Demo3Lambda {

    public static void main(String[] args) {
        // 必须new才行
        goShow(new UserService() {
            @Override
            public void show() {
                System.out.println("show 方法执行了");
            }
        });

        // 使用lambda表达式
        goShow(()->{
            System.out.println("show 方法又执行了");
        });
    }

    private static void goShow(UserService userService){
        userService.show();
    }

}

有参有返回值

// 实体类
package com.xqm.jdk8.lambda.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {

    private String name;

    private Integer age;

    private Integer height;
}

// 测试类
package com.xqm.jdk8.lambda;

import com.xqm.jdk8.lambda.model.Person;
import com.xqm.jdk8.lambda.service.UserService;

import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator;
import java.util.List;

/**
 * 无参
 */
public class Demo4Lambda {

    public static void main(String[] args) {
        List<Person> list=new ArrayList<>();
        list.add(new Person("周杰伦",44,170));
        list.add(new Person("刘德华",43,171));
        list.add(new Person("周星驰",46,172));
        list.add(new Person("郭富城",48,173));
        // 进行普通排序
        Collections.sort(list, new Comparator<Person>() {
            @Override
            public int compare(Person o1, Person o2) {
                return o1.getAge()-o2.getAge();
            }
        });

        // 使用lambda表达式改造排序规则
        Collections.sort(list,(Person o1,Person o2)->{
           return o1.getAge()- o2.getAge();
        });

        // 简化后排序
        list.sort(Comparator.comparingInt(Person::getAge));

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

    }
}

lambda表达式局限

只能实现一个方法(如果实现接口,那么接口只能有一个抽象方法)

lambda表达式原理

1.匿名内部类的本质是在编译的时候生成xxxxx$1.class文件

2.
javap -c -p   xxx.class
-c 表示反汇编
-p 表示显示所有类和接口

3.lambda会生成一个方法A,方法A中就是lambda的方法体,实现接口重写方法B,方法B会调用方法A

lambda表达式省略写法

1.小括号中参数类型可以省略
2.如果小括号内只有一个参数,则括号可以省略
3.如果大括号内有且仅有一条语句,则大括号、return、分号都可以省略

lambda表达式使用前提

1.方法的参数或者局部变量类型必须是接口才能使用lambda
2.接口有且仅有一个抽象方法

lambda表达式和匿名内部类的区别

1.所需类型
匿名内部类的类型可以是类、抽象类、接口
lambda只能是接口

2.抽象方法的数量不一样
匿名内部类所需接口的抽象方法数量随意
lambda所需接口的方法只能有一个抽象类

3.实现原理不一样
匿名内部类是在编译后形成一个class
lambda是在程序运行的时候动态生成class文件

接口

JDK1.8之前

interface 接口名{
   静态常量
   抽象方法
}

如果接口新增一个抽象方法,实现的子类必须重写全部抽象方法,不利于接口扩展

JDK1.8之后

interface 接口名{
  静态常量
  抽象方法
  默认方法
  静态方法
}

默认方法

interface 接口名 {
  修饰符 default  返回值类型  方法名{
          方法体
 }
}
1.实现类实例直接调用接口的默认方法
2.实现类重写接口的默认方法

静态方法

interface 接口名{
修饰符 static 返回值类型 方法名{
       方法体 
}
}
1.不能被重写
2.只能通过 接口名.方法名 调用

函数式接口

Supplier:无参有返回值

格式

@FunctionalInterface
public interface Supplier<T> {

    /**
     * Gets a result.
     *
     * @return a result
     */
    T get();
}

使用

package com.xqm.jdk8.fun;


import java.util.Arrays;
import java.util.function.Consumer;
import java.util.function.Supplier;

/**
 * supplier函数式接口,其中get()方法是无参有返回值
 */
public class SupplierTest {

    public static void main(String[] args) {
        sum(()->{
            int[] arr={11,44,77,22,10,2};
            // 计算出数组最大值
            Arrays.sort(arr);
            return arr[arr.length-1];
        });
    }

    private static void sum(Supplier<Integer> supplier){
        Integer max = supplier.get();
        System.out.println("max = " + max);
    }
}

Consumer:有参无返回值

supplier是用来生产数据的,consumer是用来消费数据的,消费的时候需要指定一个泛型来定义参数类型

格式


@FunctionalInterface
public interface Consumer<T> {

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

    /**
     * Returns a composed {@code Consumer} that performs, in sequence, this
     * operation followed by the {@code after} operation. If performing either
     * operation throws an exception, it is relayed to the caller of the
     * composed operation.  If performing this operation throws an exception,
     * the {@code after} operation will not be performed.
     *
     * @param after the operation to perform after this operation
     * @return a composed {@code Consumer} that performs in sequence this
     * operation followed by the {@code after} operation
     * @throws NullPointerException if {@code after} is null
     */
    default Consumer<T> andThen(Consumer<? super T> after) {
        Objects.requireNonNull(after);
        return (T t) -> { accept(t); after.accept(t); };
    }
}

使用

package com.xqm.jdk8.fun;


import java.util.function.Consumer;

/**
 * consumer
 */
public class Consumer1Test {

    public static void main(String[] args) {
        test(msg->{
            System.out.println(msg.toLowerCase());
        });
    }

    public static void test(Consumer<String> consumer){
        consumer.accept("HELLO WORLD!");
        consumer.accept("HELLO KETTY!");
    }

}
package com.xqm.jdk8.fun;


import java.util.function.Consumer;

/**
 * consumer
 */
public class Consumer2Test {

    public static void main(String[] args) {
        test(msg1->{
            System.out.println(msg1.toLowerCase());
        },msg2->{
            System.out.println(msg2.toUpperCase());
        });
    }

    public static void test(Consumer<String> consumer1,Consumer<String> consumer2){
        String str="Hello World";
        // 转小写
        consumer1.accept(str);
        // 转大写
        consumer2.accept(str);
    }
}
package com.xqm.jdk8.fun;


import java.util.function.Consumer;

/**
 * consumer
 */
public class Consumer3Test {

    public static void main(String[] args) {
        test(msg1->{
            System.out.println(msg1.toLowerCase());
        },msg2->{
            System.out.println(msg2.toUpperCase());
        });
    }

    public static void test(Consumer<String> consumer1,Consumer<String> consumer2){
        String str="Hello World";
        // 转小写-->转大写
       consumer1.andThen(consumer2).accept(str);
    }
}

Function:有参有返回值

根据一个类型的数据获取另一个类型的数据。前者称为前置条件,后者称为后置条件。

格式

@FunctionalInterface
public interface Function<T, R> {

    R apply(T t);

    default <V> Function<V, R> compose(Function<? super V, ? extends T> before) {
        Objects.requireNonNull(before);
        return (V v) -> apply(before.apply(v));
    }

  
    default <V> Function<T, V> andThen(Function<? super R, ? extends V> after) {
        Objects.requireNonNull(after);
        return (T t) -> after.apply(apply(t));
    }

    static <T> Function<T, T> identity() {
        return t -> t;
    }
}

测试

传递一个字符串,返回一个int

package com.xqm.jdk8.fun;

import java.util.function.Function;

public class FunctionTest {

    public static void main(String[] args) {
        test(msg->{
            Integer i = Integer.parseInt(msg);
            System.out.println(i.getClass());
            return i;
        });
    }

    public static void test(Function<String,Integer> function){
        Integer apply = function.apply("666");
        System.out.println("apply:"+apply);
    }
}

andThen

package com.xqm.jdk8.fun;

import java.util.function.Function;

public class Function2Test {

    public static void main(String[] args) {
        test(msg->{
            Integer i = Integer.parseInt(msg);
            System.out.println(i.getClass());
            return i;
        },msg2->{
            System.out.println(msg2);
            return msg2;
        });
    }

    public static void test(Function<String,Integer> function1,Function<Integer,Integer> function2){
        // Integer a = function1.apply("666");
        // Integer b = function2.apply(a);
        Integer apply = function1.andThen(function2).apply("123");
    }
}

compose

package com.xqm.jdk8.fun;

import java.util.function.Function;

public class Function3Test {

    public static void main(String[] args) {
        test(msg->{
            Integer i = Integer.parseInt(msg);
            System.out.println(i.getClass());
            return i;
        },msg2->{
            System.out.println(msg2);
            return msg2;
        });
    }

    public static void test(Function<String,Integer> function1,Function<Integer,Integer> function2){
        // 这里就是先执行compose中的,然后再执行外层
        function2.compose(function1).apply("123");
    }
}

Predicate:有参且返回值为Boolean

结构


package java.util.function;

import java.util.Objects;


@FunctionalInterface
public interface Predicate<T> {

   
    boolean test(T t);

    default Predicate<T> and(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) && other.test(t);
    }


    default Predicate<T> negate() {
        return (t) -> !test(t);
    }


    default Predicate<T> or(Predicate<? super T> other) {
        Objects.requireNonNull(other);
        return (t) -> test(t) || other.test(t);
    }

    static <T> Predicate<T> isEqual(Object targetRef) {
        return (null == targetRef)
                ? Objects::isNull
                : object -> targetRef.equals(object);
    }
}

测试

package com.xqm.jdk8.fun;

import java.util.function.Predicate;

public class PredicateTest {

    public static void main(String[] args) {
        test(msg->{
			// 如果长度大于三就返回true
            return msg.length()>3;
        },"hello world");
    }


    public static void test(Predicate<String> predicate,String msg){
        boolean test = predicate.test(msg);
        System.out.println(test);
    }
}

在Predicate中默认方法提供了and(与)、or(或)、negate(非)、isEqual

package com.xqm.jdk8.fun;

import java.util.function.Predicate;

/**
 * 包含H和W
 */
public class Predicate2Test {

    public static void main(String[] args) {
        test(msg->msg.contains("H")
                ,msg2->
             msg2.contains("W")
        );
    }
  
    public static void test(Predicate<String> predicate,Predicate<String> predicate2){
        boolean hello = predicate.and(predicate2).test("Hello World");
        System.out.println(hello);
    }
}

方法引用

lambda表达式冗余

在使用lambda表达式的时候,也会出现表达式冗余的情况。比如求数组之和的时候。

package com.xqm.jdk8.funcRef;

import java.util.function.Consumer;

public class FunRef1 {

    public static void main(String[] args) {
        printSum(a->{
            int sum=0;
            for (int i : a) {
                sum+=i;
            }
            System.out.println(sum);
        });
    }
  
    public static void printSum(Consumer<int[]> consumer){
        int a[]={1,2,3,4,5,6};
        consumer.accept(a);

    }
}

解决方案

在lambda表达式中要执行的代码和另一个方法中的代码是一样的,这时没有必要重写一份逻辑

,这时候就可以引用重复的代码。使用双冒号进行方法引用::。

package com.xqm.jdk8.funcRef;

import java.util.function.Consumer;

public class FunRef2 {

    public static void main(String[] args) {
        // 引用方法,双冒号::就是方法引用
        printSum(FunRef2::getTotla);
    }

    /**
     * 求数组之和
     * @param a
     */
    private static void getTotla(int[] a){
        int sum=0;
        for (int i : a) {
            sum+=i;
        }
        System.out.println(sum);
    }

    private static void printSum(Consumer<int[]> consumer){
        int a[]={1,2,3,4,5,6};
        consumer.accept(a);
    }
}

方法引用的格式

符号表示:::

符号说明:双冒号为方法引用运算符,而它所在的表达式被称为方法引用

应用场景:如果lambda表达式所要实现的方案,已经有其他的方法存在相同的方案,那么则可以使用方法引用

常见引用方式:

  • InstanceName::methodName  对象::方法名
    
  • ClassName::staticMethodName 类名::静态方法名
    
  • ClassName::methodName  类名::普通方法
    
  • ClassName::new   类名::new调用的构造器
    
  • TypeName[]::new   String[]::new调用的数组构造器
    

对象::方法名

package com.xqm.jdk8.funcRef;

import java.util.Date;
import java.util.function.Supplier;

public class FunRef3 {

    public static void main(String[] args) {
        Date now =new Date();
        // 对象::方法名
        Supplier<Long> supplier= now::getTime;
        System.out.println(supplier.get());
    }
}

类名::静态方法名

package com.xqm.jdk8.funcRef;

import java.util.function.Supplier;

public class FunRef4 {

    public static void main(String[] args) {
        // 未使用方法引用
        Supplier<Long> supplier= ()->{
            return System.currentTimeMillis();
        };
        System.out.println(supplier.get());
        // 使用方法引用 方法名::静态方法
        Supplier<Long> supplier1= System::currentTimeMillis;
    }
}

类名::实例方法

java面向对象中,类名只能调用静态方法,类名引用实例方法是有前提的,实际上是拿第一个参数作为方法的调用者

package com.xqm.jdk8.funcRef;

import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;

public class FunRef5 {

    public static void main(String[] args) {
        Function<String,Integer> function=s-> s.length();
        System.out.println(function.apply("hello"));
        // 通过方法引用来实现
        Function<String,Integer> function1=String::length;
        System.out.println(function1.apply("world"));

        BiFunction<String,Integer,String> function2=String::substring;
        String msg = function2.apply("Hello world", 3);
        System.out.println(msg);
    }
}

类名::构造器

package com.xqm.jdk8.lambda.model;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@AllArgsConstructor
@NoArgsConstructor
public class Person {

    private String name;

    private Integer age;
}

package com.xqm.jdk8.funcRef;


import java.util.function.BiFunction;
import java.util.function.Supplier;

public class FunRef6 {

    public static void main(String[] args) {
        // 无参构造方法
        Supplier<Person> supplier=Person::new;
        System.out.println(supplier.get());

        BiFunction<String,Integer,Person> function=Person::new;
        System.out.println(function.apply("张三",18));
    }
}

数组::构造器

package com.xqm.jdk8.funcRef;


import java.util.Arrays;
import java.util.function.Function;

public class FunRef7 {

    public static void main(String[] args) {
        // 未使用方法引用
        Function<Integer,String[]> function=(len)->{
            return new String[len];
        };
        // 这里的方法引用,Integer代表的是创建String数组的长度
        Function<Integer,String[]> function1=String[]::new;
        // 输出为 [null, null, null, null, null]
        System.out.println(Arrays.toString(function1.apply(5)));
    }
}

Stream流

集合处理数据的弊端

对集合进行操作的时候,除了必须的增删改查,典型的操作就是遍历集合。只要对集合操作,就得进行循环。

package com.xqm.jdk8.stream;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;

public class Test01Stream {
    public static void main(String[] args) {
        List<String> list=Arrays.asList("张三","张三丰","张大炮","思密达","斯密斯");
        List<String> list2=new ArrayList<>();
        for (String s : list) {
            if (s.startsWith("张")){
                list2.add(s);
            }
        }
        System.out.println(list2.toString());
    }
}

使用stream流来处理

package com.xqm.jdk8.stream;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

public class Test02Stream {
    public static void main(String[] args) {
        List<String> list=Arrays.asList("张三","张三丰","张大炮","思密达","斯密斯");
        List<String> list2 = list.stream().filter(s -> s.startsWith("张")).collect(Collectors.toList());
        System.out.println(list2.toString());
    }
}

Stream流概述

注意:Stream和IO流(InputStream/OutputStream)没有任何关系,请暂时忘记对传统IO流的固有印象!
Stream流式思想类似于工厂车间的“生产流水线”,Stream流不是一种数据结构,不保存数据,而是对数据进行加工处理。Stream可以看作是流水线上的一个工序。在流水线上,通过多个工序让一个原材料加工成一个商品。

Stream API能让我们快速完成许多复杂的操作,如筛选、切片、映射、查找、去除重复,统计,匹配和归约。

Stream流的获取方式

1.通过Collection获取

java.util.Collection 接口中加入了default方法 stream,也就是说Collection接口下的所有的实现都可以通过steam方法来获取Stream流。

 public static void main(String[] args) {
        List<String> list = new ArrayList<>();
        list.stream();
        Set<String> set = new HashSet<>();
        set.stream();
        Vector vector = new Vector();
        vector.stream();
    }

但是Map接口别没有实现Collection接口,那这时怎么办呢?这时我们可以根据Map获取对应的key value的集合。

    public static void main(String[] args) {
        Map<String,Object> map = new HashMap<>();
        Stream<String> stream = map.keySet().stream(); // key
        Stream<Object> stream1 = map.values().stream(); // value
        Stream<Map.Entry<String, Object>> stream2 = map.entrySet().stream(); // entry
    }

2.通过Stream的of方法获取

在实际开发中不可避免会操作数组中的数据,由于数组对象不可能添加默认方法,所以Stream中提供了静态方法

Stream不能处理基本类型的数据

package com.xqm.jdk8.stream;

import java.util.stream.Stream;


public class Test04Stream {
    public static void main(String[] args) {
        Stream<String> a=Stream.of("a1","a2","a3");
        String[] a1={"aa","bb","cc"};
        Stream<String> a2=Stream.of(a1);
        // aa bb cc
        a2.forEach(System.out::print);
        Integer[] a3={1,2,3,4};
        // 1 2 3 4
        Stream.of(a3).forEach(System.out::print);
        // 将{1,2,3,4}当作一个对象来处理,因此使用Stream不能处理基本类型
        int[] a4={1,2,3,4};
        //  [I@533ddba
        Stream.of(a4).forEach(System.out::print);
    }
}

Stream常用方法

常用方法

方法名 方法作用 返回值类型 方法种类
count 统计个数 long 终结
forEach 逐个处理 void 终结
collect 生成集合 collect 终结
filter 过滤 Stream 函数拼接
limit 取用前几个 Stream 函数拼接
skip 跳过前几个 Stream 函数拼接
map 映射 Stream 函数拼接
concat 拼接 Stream 函数拼接

终结方法:返回值类型不再是 Stream 类型的方法,不再支持链式调用。本小节中,终结方法包括 count 和

forEach 方法。

非终结方法:返回值类型仍然是 Stream 类型的方法,支持链式调用。(除了终结方法外,其余方法均为非终结

方法。)

Stream注意事项(重要)

  1. Stream只能操作一次
  2. Stream方法返回的是新的流
  3. Stream不调用终结方法,中间的操作不会执行

forEach

用来遍历stream流

 void forEach(Consumer<? super T> action);
package com.xqm.jdk8.stream;

import java.util.stream.Stream;


public class Test05Stream {
    public static void main(String[] args) {
        Integer[] a3={1,2,3,4};
        // 1 2 3 4
        Stream.of(a3).forEach(System.out::print);
    }
}

count

用来统计流中数据个数

long count();
package com.xqm.jdk8.stream;

import java.util.stream.Stream;


public class Test06Stream {
    public static void main(String[] args) {
        Integer[] a3={1,2,3,4};
        long count = Stream.of(a3).count();
        // 4
        System.out.println(count);
    }
}

filter

用来过滤语句

Stream<T> filter(Predicate<? super T> predicate);
package com.xqm.jdk8.stream;

        import java.util.stream.Stream;


public class Test07Stream {
    public static void main(String[] args) {
        Integer[] a3={1,2,3,4};
        // 3 4
        Stream.of(a3).filter(a->a>2).forEach(System.out::print);
    }
}

limit

取用前几个数据,如果集合当前长度大于参数则截取,否则不操作

 Stream<T> limit(long maxSize);
package com.xqm.jdk8.stream;

import java.util.stream.Stream;


public class Test08Stream {
    public static void main(String[] args) {
        Integer[] a3={1,2,3,4};
        // 1 2
        Stream.of(a3).limit(2).forEach(System.out::print);
    }
}

skip

跳过前几个,如果参数大于流数据长度,则不操作,否则截取

Stream<T> skip(long n);
package com.xqm.jdk8.stream;

import java.util.stream.Stream;


public class Test09Stream {
    public static void main(String[] args) {
        Integer[] a3={1,2,3,4};
        // 3 4
        Stream.of(a3).skip(2).forEach(System.out::print);
    }
}

map

将流中的元素映射到另一个流中

 <R> Stream<R> map(Function<? super T, ? extends R> mapper);
package com.xqm.jdk8.stream;

import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import java.util.stream.Stream;


public class Test10Stream {
    public static void main(String[] args) {
        Integer[] a3={1,2,3,4};
        // false true false false
        Stream.of(a3).map(s->s==2).forEach(System.out::print);
        System.out.println();
        // 1 2 3 4
        Stream.of(a3).map(String::valueOf).forEach(System.out::print);
    }
}

sorted

数据进行排序

 Stream<T> sorted();
package com.xqm.jdk8.stream;

import java.util.Comparator;
import java.util.stream.Stream;


public class Test11Stream {
    public static void main(String[] args) {
        Integer[] a3 = {1, 4, 3, 2,8,0,4,6};
        // 01234468
        Stream.of(a3).sorted().forEach(System.out::print);
        System.out.println();
        //  86443210
        Stream.of(a3).sorted(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o2-o1;
            }
        }).forEach(System.out::print);
    }
}

distinct

如果要去掉重复数据,可以使用distict。基本数据类型直接可以去重,自定义对象则需要重写hashCode和equals方法

Stream<T> distinct();
package com.xqm.jdk8.stream;



import java.util.stream.Stream;


public class Test12Stream {
    public static void main(String[] args) {
        Integer[] a3 = {1, 4, 3, 2,8,0,4,6};
        // 0123468
        Stream.of(a3).distinct().sorted().forEach(System.out::print);
        System.out.println();
        //  8643210
        Stream.of(a3).distinct().sorted((o1, o2) -> o2-o1).forEach(System.out::print);
        // Person(name=张三, age=13)Person(name=李四, age=22)Person(name=张三, age=22)
        // 如果要去重,必须重写hashcode和equals方法
        Stream.of(new Person("张三",13),
                new Person("李四",22),
                new Person("张三",13))
                .distinct().forEach(System.out::print);
    }
}

match

判断数据是否匹配指定的条件

元素是否都满足条件
boolean allMatch(Predicate<? super T> predicate);
元素是否任意一个满足条件
boolean anyMatch(Predicate<? super T> predicate);
元素是否都不满足条件
boolean noneMatch(Predicate<? super T> predicate);
package com.xqm.jdk8.stream;



import java.util.stream.Stream;


public class Test13Stream {
    public static void main(String[] args) {
        boolean b = Stream.of("1", "3", "3", "4", "0", "1", "7")
                .map(Integer::parseInt)
                // 里面是不是全部大于0
                .allMatch(a -> a > 0);
        // false
        System.out.println(b);
        boolean b1 = Stream.of("1", "3", "3", "4", "0", "1", "7")
                .map(Integer::parseInt)
                // 里面只要有一个大于4
                .anyMatch(a -> a > 4);
        // true
        System.out.println(b1);
        boolean b2 = Stream.of("1", "3", "3", "4", "0", "1", "7")
                .map(Integer::parseInt)
                // 一个都不匹配大于10
                .noneMatch(a -> a > 10);
        // true
        System.out.println(b2);

    }
}

find

需要找到某些数据

取第一个元素
Optional<T> findFirst();
作用就是从Stream中取任意一个元素,正常情况下一般会取第一个元素,在并行流的情况下会随机取一个元素。
Optional<T> findAny();
package com.xqm.jdk8.stream;

import java.util.stream.Stream;

public class Test14Stream {
    public static void main(String[] args) {
        Stream<String> stringStream = Stream.of("1", "3", "3", "4", "0", "1", "7");
        String s = stringStream.findFirst().get();
        // 1
        System.out.println(s);
        Stream<String> stringStream1 = Stream.of("1", "3", "3", "4", "0", "1", "7");
        String s1 = stringStream1.findAny().get();
        // 1
        System.out.println(s1);

    }
}

max和min

找出最大值和最小值

Optional<T> max(Comparator<? super T> comparator);

Optional<T> min(Comparator<? super T> comparator);
package com.xqm.jdk8.stream;

import java.util.Comparator;
import java.util.Optional;
import java.util.stream.Stream;

public class Test15Stream {
    public static void main(String[] args) {
        Stream<String> stringStream = Stream.of("1", "3", "3", "4", "0", "1", "7");
        Optional<Integer> max = stringStream.map(Integer::parseInt).max(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        });
        // 7
        System.out.println(max.get());

        Stream<String> stringStream1 = Stream.of("1", "3", "3", "4", "0", "1", "7");
        Optional<Integer> min = stringStream1.map(Integer::parseInt).min(new Comparator<Integer>() {
            @Override
            public int compare(Integer o1, Integer o2) {
                return o1 - o2;
            }
        });
        // 0
        System.out.println(min.get());

    }
}

// 极简写法
package com.xqm.jdk8.stream;

import java.util.Comparator;
import java.util.Optional;
import java.util.stream.Stream;

public class Test15Stream {
    public static void main(String[] args) {
        Stream<String> stringStream = Stream.of("1", "3", "3", "4", "0", "1", "7");
        Optional<Integer> max = stringStream.map(Integer::parseInt).max((o1, o2) -> o1 - o2);
        // 7
        System.out.println(max.get());

        Stream<String> stringStream1 = Stream.of("1", "3", "3", "4", "0", "1", "7");
        Optional<Integer> min = stringStream1.map(Integer::parseInt).min(Comparator.comparingInt(o -> o));
        // 0
        System.out.println(min.get());
    }
}

reduce

将所有数据归纳得到一个数据。聚合,比如做累加、求取最大值、最小值等

T reduce(T identity, BinaryOperator<T> accumulator);

Optional<T> reduce(BinaryOperator<T> accumulator);

<U> U reduce(U identity,
                 BiFunction<U, ? super T, U> accumulator,
                 BinaryOperator<U> combiner);
package com.xqm.jdk8.stream;

import java.util.Comparator;
import java.util.Optional;
import java.util.stream.Stream;

public class Test16Stream {
    public static void main(String[] args) {
        Stream<Integer> stream = Stream.of(4,5,3,9);
        // 两两元素相加,第一个参数identity是初始默认值,这里赋值为0,就是0+4+5+3+9
        Integer reduce = stream.reduce(0, Integer::sum);
        // 21
        System.out.println(reduce);

        // 获取最大值
        Stream<Integer> stream1 = Stream.of(4,5,3,9);
        Integer reduce1 = stream1.reduce(0, Integer::max);
        // 9
        System.out.println(reduce1);
    }
}

map和reduce组合

在实际开发中,经常用map和reduce进行组合运算

 package com.xqm.jdk8.stream;

import java.util.stream.Stream;

public class Test17Stream {
    public static void main(String[] args) {
        // 求出所有年龄的总和
        Stream<Person> personStream = Stream.of(new Person("张三", 13),
                new Person("李四", 22),
                new Person("张三", 20));
        Integer reduce = personStream.map(Person::getAge).reduce(0, Integer::sum);
        // 55
        System.out.println(reduce);
    }
}

package com.xqm.jdk8.stream;

import java.util.Optional;
import java.util.stream.Stream;

public class Test18Stream {
    public static void main(String[] args) {
        Stream<String> stream=Stream.of("a","b","c","a","a","b","d","a");
        // 统计字符a出现的次数
        Optional<Integer> reduce = stream.map(c -> "a".equals(c) ? 1 : 0)
                .reduce(Integer::sum);
        // 4
        System.out.println(reduce.get());
    }
}

mapToInt、mapToLong、mapToDouble

将stream流中Integer转换为int

IntStream mapToInt(ToIntFunction<? super T> mapper);
LongStream mapToLong(ToLongFunction<? super T> mapper);
DoubleStream mapToDouble(ToDoubleFunction<? super T> mapper);
package com.xqm.jdk8.stream;

import java.util.Optional;
import java.util.stream.Stream;

public class Test19Stream {
    public static void main(String[] args) {
        // Integer占用的内存比int多很多,在stream流操作中有装箱和拆箱
        Integer[] arr ={1,3,5,7,9};
        // 会进行拆箱操作
        Stream.of(arr).filter(i->i>0)
                .forEach(System.out::println);

        // 为了提高代码的效率,可以先将Integer转换成int,在进行其他操作
        Stream.of(arr).mapToInt(Integer::intValue).filter(i->i>3).forEach(System.out::println);;

    }
}

concat

如果有两个流希望合并成为一个流,那么可以使用stream静态方法cancat

  public static <T> Stream<T> concat(Stream<? extends T> a, Stream<? extends T> b) {
        Objects.requireNonNull(a);
        Objects.requireNonNull(b);

        @SuppressWarnings("unchecked")
        Spliterator<T> split = new Streams.ConcatSpliterator.OfRef<>(
                (Spliterator<T>) a.spliterator(), (Spliterator<T>) b.spliterator());
        Stream<T> stream = StreamSupport.stream(split, a.isParallel() || b.isParallel());
        return stream.onClose(Streams.composedClose(a, b));
    }
package com.xqm.jdk8.stream;

import java.util.stream.Stream;

public class Test20Concat {
    public static void main(String[] args) {
        Stream<String> a = Stream.of("a", "b", "c");
        Stream<String> b = Stream.of("x", "y", "z");
        // abcxyz
        Stream.concat(a,b).forEach(System.out::print);
    }
}

综合案例

定义两个集合,存储用户信息。

1.第一个集合只保留长度为3的成员

2.第一个集合筛选之后只要前三个人

3.第二个集合只要姓张的成员

4.第二个集合筛选之后不要前两个人

5.将两个集合合并成一个集合

6.根据姓名创建Person对象

7.打印整个集合的Person信息

package com.xqm.jdk8.stream;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Random;
import java.util.stream.Stream;

/**
 * 综合案例
 * 1.第一个集合只保留长度为3的成员
 * 2.第一个集合筛选之后只要前三个人
 * 3.第二个集合只要姓张的成员
 * 4.第二个集合筛选之后不要前两个人
 * 5.将两个集合合并成一个集合
 * 6.根据姓名创建Person对象
 * 7.打印整个集合的Person信息
 */
public class Test21Test {
    public static void main(String[] args) {
        List<String> nameList=Arrays.asList("迪丽热巴","宋远桥","老子","庄子","孙子","洪七公");
        List<String> nameList2=Arrays.asList("古力娜扎","张无忌","张三丰","赵丽颖","张二狗","张天爱","张三");
        List<Person> list=new ArrayList<>();
        // 第一个集合 宋远桥 洪七公
        Stream<String> oneStream = nameList.stream().filter(s -> s.length() == 3).limit(3);
        // 第二个集合 张二狗 张天爱 张三
        Stream<String> twoStream = nameList2.stream().filter(s -> s.startsWith("张")).skip(2);
        // 合并成一个集合
        Stream<String> concat = Stream.concat(oneStream, twoStream);
        // 根据姓名创建Person对象 打印整个集合的Person信息
        concat.map(n->new Person(n,new Random().nextInt(20))).forEach(System.out::println);
    }
}

collect

将收集到的结果收集起来

<R, A> R collect(Collector<? super T, A, R> collector);

 <R> R collect(Supplier<R> supplier,
                  BiConsumer<R, ? super T> accumulator,
                  BiConsumer<R, R> combiner);
收集到集合中
package com.xqm.jdk8.stream;

import java.util.*;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**

 */
public class Test22Collect {
    public static void main(String[] args) {
        List<String> nameList = Arrays.asList("迪丽热巴", "宋远桥", "老子", "庄子", "孙子", "洪七公","洪七公");
        // list集合
        List<String> collect = nameList.stream().filter(s -> s.length() > 2)
                .collect(Collectors.toList());
        System.out.println(collect.toString());
        // set集合
        Set<String> collect1 = nameList.stream().filter(s -> s.length() > 2)
                .collect(Collectors.toSet());
        System.out.println(collect1.toString());
        // 收集到为具体的类型 arrayList
        ArrayList<String> collect2 = nameList.stream().filter(s -> s.length() > 2)
                .collect(Collectors.toCollection(ArrayList::new));
        System.out.println(collect2);

    }
}

[迪丽热巴, 宋远桥, 洪七公, 洪七公]
[洪七公, 迪丽热巴, 宋远桥]
[迪丽热巴, 宋远桥, 洪七公, 洪七公]
收集到数组中
package com.xqm.jdk8.stream;

import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.Set;
import java.util.stream.Collectors;

/**

 */
public class Test23Collect02 {
    public static void main(String[] args) {
        List<String> nameList = Arrays.asList("迪丽热巴", "宋远桥", "老子", "庄子", "孙子", "洪七公","洪七公");
        // 数组
        Object[] objects = nameList.stream().filter(s -> s.length() > 2)
                .toArray();
        // 指定返回数据类型
        String[] strings = nameList.stream().filter(s -> s.length() > 2)
                .toArray(String[]::new);
    }
}

对流中数据进行聚合计算

package com.xqm.jdk8.stream;


import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Test24 {
    public static void main(String[] args) {
        // 获取最大值
        Optional<Person> collect = Stream.of(new Person("张三", 13),
                new Person("李四", 22),
                new Person("张三", 20)).collect(Collectors.maxBy((p1, p2) -> p1.getAge() - p2.getAge()));
        // Person(name=李四, age=22)
        System.out.println(collect.get());

        // 获取最小值
        Optional<Person> collect1 = Stream.of(new Person("张三", 13),
                new Person("李四", 22),
                new Person("张三", 20)).collect(Collectors.minBy((p1, p2) -> p1.getAge() - p2.getAge()));
        // Person(name=张三, age=13)
        System.out.println(collect1.get());

        // 求和
        Integer collect2 = Stream.of(new Person("张三", 13),
                new Person("李四", 22),
                new Person("张三", 20))
                // .collect(Collectors.summingInt(s -> s.getAge()));
                .collect(Collectors.summingInt(Person::getAge));
        //  55
        System.out.println(collect2);

        // 平均值
        Double collect3 = Stream.of(new Person("张三", 13),
                new Person("李四", 22),
                new Person("张三", 20))
                .collect(Collectors.averagingInt(Person::getAge));
        // 18.333333333333332
        System.out.println(collect3);


        // 统计数量
        Long collect4 = Stream.of(new Person("张三", 13),
                new Person("李四", 22),
                new Person("张三", 20))
                .collect(Collectors.counting());
        // 3
        System.out.println(collect4);

    }
}

Collectors.groupingBy

分组计算

单级分组
package com.xqm.jdk8.stream;


import java.util.List;
import java.util.Map;
import java.util.Optional;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Test25Group {
    public static void main(String[] args) {
        // 根据姓名分组
        Map<String, List<Person>> collect = Stream.of(
                new Person("张三", 13),
                new Person("李四", 22),
                new Person("张三", 24),
                new Person("李四", 25),
                new Person("张三", 20)
        ).collect(Collectors.groupingBy(Person::getName));

        // 李四:[Person(name=李四, age=22), Person(name=李四, age=25)]
        // 张三:[Person(name=张三, age=13), Person(name=张三, age=24), Person(name=张三, age=20)]
        collect.forEach((k,v)->{
            System.out.println(k+":"+v);
        });

        // 根据年龄分组,如果大于18 则是成年
        Map<String, List<Person>> collect1 = Stream.of(
                new Person("张三", 13),
                new Person("李四", 22),
                new Person("张三", 24),
                new Person("李四", 25),
                new Person("张三", 20)
        ).collect(Collectors.groupingBy(s -> s.getAge() >= 18 ? "成年" : "未成年"));

        //未成年:[Person(name=张三, age=13)]
        // 成年:[Person(name=李四, age=22), Person(name=张三, age=24), Person(name=李四, age=25), Person(name=张三, age=20)]
        collect1.forEach((k,v)->{
            System.out.println(k+":"+v);
        });
    }
}

多级分组
package com.xqm.jdk8.stream;


import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Test26Group02 {
    public static void main(String[] args) {
        // 多级分组
        Map<String, Map<Integer, List<Person>>> collect = Stream.of(
                new Person("张三", 13),
                new Person("李四", 22), 
                new Person("张三", 24),
                new Person("李四", 25),
                new Person("张三", 20)
        ).collect(Collectors.groupingBy(Person::getName, Collectors.groupingBy(Person::getAge)));

        collect.forEach((k,v)->{
            System.out.println(k+":"+v);
        });


    }
}

李四:{22=[Person(name=李四, age=22)], 25=[Person(name=李四, age=25)]}
张三:{20=[Person(name=张三, age=20)], 24=[Person(name=张三, age=24)], 13=[Person(name=张三, age=13)]}

partitioningBy

对流中数据进行分区处理

Collectors.partitioningBy()会根据是否为true,把集合中数据分为两个列表,一个为true,一个为false
package com.xqm.jdk8.stream;


import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Test27Partition {
    public static void main(String[] args) {
        // 多级分组
        Map<Boolean, List<Person>> collect = Stream.of(
                new Person("张三", 13),
                new Person("李四", 17),
                new Person("张三", 24),
                new Person("李四", 25),
                new Person("张三", 20)
        ).collect(Collectors.partitioningBy(p -> p.getAge() > 18));

        collect.forEach((k,v)->{
            System.out.println(k+":"+v);
        });


    }
}

false:[Person(name=张三, age=13), Person(name=李四, age=17)]
true:[Person(name=张三, age=24), Person(name=李四, age=25), Person(name=张三, age=20)]

对流中数据进行拼接处理

Collectors.joining会根据指定的连接符,将所有元素连接为一个字符串
package com.xqm.jdk8.stream;


import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Test28Join {
    public static void main(String[] args) {
        // 无参
        String collect = Stream.of(
                new Person("张三", 13),
                new Person("李四", 17),
                new Person("张三", 24),
                new Person("李四", 25),
                new Person("张三", 20)
        ).map(Person::getName)
                .collect(Collectors.joining());
        // 张三李四张三李四张三
        System.out.println(collect);

        // 连接符
        String collect1 = Stream.of(
                new Person("张三", 13),
                new Person("李四", 17),
                new Person("张三", 24),
                new Person("李四", 25),
                new Person("张三", 20)
        ).map(Person::getName)
                .collect(Collectors.joining("_"));
        // 张三李四张三李四张三
        System.out.println(collect1);


        // 连接符 前缀 后缀
        String collect2 = Stream.of(
                new Person("张三", 13),
                new Person("李四", 17),
                new Person("张三", 24),
                new Person("李四", 25),
                new Person("张三", 20)
        ).map(Person::getName)
                .collect(Collectors.joining("_","###","$$$"));
        //
        System.out.println(collect2);


    }
}

张三李四张三李四张三
张三_李四_张三_李四_张三
###张三_李四_张三_李四_张三$$$

并行的stream流

前面使用的stream流都是串行的。

package com.xqm.jdk8.stream;


import java.util.stream.Collectors;
import java.util.stream.Stream;

public class Test29 {
    public static void main(String[] args) {
        // 无参
        long count = Stream.of(
                1, 2, 3, 4, 5
        ).filter(s -> {
            System.out.println(Thread.currentThread().getName() + "_" + s);
            return s > 3;
        }).count();
  
        // main_1
        // main_2
        // main_3
        // main_4
        // main_5
    }
}

获取并行流的两种方式:

  • 通过list中的list.parallelStream()
  • 通过串行流转换为并行流 Stream.of().parallel()
package com.xqm.jdk8.stream;


import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;

/**
 * 演示并行流
 */
public class Test31 {
    public static void main(String[] args) {
        // 将已有的串行流转换为并行流
        Stream<Integer> parallel = Stream.of(1, 5, 7, 9, 4, 5, 3).parallel();
        long count = parallel.filter(s -> {
            System.out.println(Thread.currentThread().getName() + "_" + s);
            return s > 2;
        }).count();
    }
}

ForkJoinPool.commonPool-worker-4_5
main_4
ForkJoinPool.commonPool-worker-2_3
ForkJoinPool.commonPool-worker-13_7
ForkJoinPool.commonPool-worker-11_1
ForkJoinPool.commonPool-worker-6_9
ForkJoinPool.commonPool-worker-9_5

串行流和并行流对比

通过5亿个数相加来进行对比

package com.xqm.jdk8.stream;


import java.util.stream.LongStream;
import java.util.stream.Stream;

/**
 * 串行流和并行流对比
 */
public class Test32 {
    private static final long TIME=500000000;

    public static void main(String[] args) {
        Test32 test=new Test32();
        test.common();
        test.seri();
        test.parallel();

    }

    private long getTime(){
        return System.currentTimeMillis();
    }

    public void common(){
        long sum=0;
        long startTime = getTime();
        for (long i = 0; i < TIME; i++) {
            sum +=i;
        }
        long endTime = getTime();
        System.out.println("普通for循环消耗的时间为:"+(endTime-startTime)+"ms");
    }

    private void seri(){
        long startTime = getTime();
        long reduce = LongStream.rangeClosed(0, TIME)
                .reduce(0, Long::sum);
        long endTime = getTime();
        System.out.println("串行stream流for循环消耗的时间为:"+(endTime-startTime)+"ms");
    }


    private void parallel(){
        long startTime = getTime();
        long reduce = LongStream.rangeClosed(0, TIME).parallel()
                .reduce(0, Long::sum);
        long endTime = getTime();
        System.out.println("并行stream流for循环消耗的时间为:"+(endTime-startTime)+"ms");
    }

}

普通for循环消耗的时间为:141ms
串行stream流for循环消耗的时间为:229ms
并行stream流for循环消耗的时间为:60ms

并行流的安全问题

package com.xqm.jdk8.stream;


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

/**
 * 串行流和并行流对比
 */
public class Test33 {
    public static void main(String[] args) {
        List<Integer> list=new ArrayList<>();
        for (int i = 0; i < 1000; i++) {
            list.add(i);
        }
        List<Integer> listNew=new ArrayList<>();
        list.parallelStream().forEach(listNew::add);
        System.out.println(list.size());
		// 每次出现的结果不一定
        System.out.println(listNew.size());
    }


}

100
95

解决方案

1.加同步锁synchronized
package com.xqm.jdk8.stream;


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

/**
 * 串行流和并行流对比
 */
public class Test34 {
    public static void main(String[] args) {
        List<Integer> listNew=new ArrayList<>();
        Object obj=new Object();
        IntStream.rangeClosed(1,1000).parallel().forEach(s->{
            synchronized (obj){
                listNew.add(s);
            }
        });
        System.out.println(listNew.size());
    }


}

2.使用线程安全的容器
package com.xqm.jdk8.stream;


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

/**
 * 使用线程安全的容器
 */
public class Test35 {
    public static void main(String[] args) {
        Vector<Integer> listNew=new Vector<>();
        Object obj=new Object();
        IntStream.rangeClosed(1,1000).parallel().forEach(s->{
                listNew.add(s);
        });
        System.out.println(listNew.size());
    }
}

3.将线程不安全的容器转换为线程安全的容器
package com.xqm.jdk8.stream;


import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Vector;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * 使用线程安全的容器
 */
public class Test36 {
    public static void main(String[] args) {
        List<Integer> listNew=new ArrayList<>();
        List<Integer> list = Collections.synchronizedList(listNew);
        Object obj=new Object();
        IntStream.rangeClosed(1,1000).parallel().forEach(s->{
                list.add(s);
        });
        System.out.println(list.size());
    }
}

4.通过stream中的toArray、collect
package com.xqm.jdk8.stream;


import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.Vector;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

/**
 * 使用线程安全的容器
 */
public class Test36 {
    public static void main(String[] args) {
        List<Integer> collect = IntStream.rangeClosed(1, 1000).parallel()
                .boxed()
                .collect(Collectors.toList());
        System.out.println(collect.size());
    }
}

Optional类

optional类主要是解决空指针的问题。

以前对null的处理

package com.xqm.jdk8.optional;

import org.apache.commons.lang3.StringUtils;

public class Test01 {

    public static void main(String[] args) {
        String name=null;
        // 会抛异常
        // System.out.println(name.length());
        if (!StringUtils.isEmpty(name)){
            System.out.println(name.length());
        }else {
            System.out.println("name is null");
        }
    }
}

Optional类

public final class Optional<T> 

optional是final关键字修饰的类,是一个可以为null的容器对象,主要作用就是避免null检查,出现NullPointerException

Optional创建

  • 通过of方法创建

  • 通过ofNullable方法创建

  • 直接创建empty的Optional

    package com.xqm.jdk8.optional;
    
    import org.apache.commons.lang3.StringUtils;
    
    import java.util.Optional;
    
    public class Test02 {
    
        public static void main(String[] args) {
            // of 方法不支持null
            Optional<String> optional=Optional.of("张三");
            // 会报异常
            // Optional<String> optionalNull=Optional.of(null);
    
            // 不报异常,但是不能使用get方法获取元素
            Optional<String> op3=Optional.ofNullable(null);
    
            // 通过empty创建一个空的对象
            Optional<String> op4=Optional.empty();
    
        }
    }
    
    

Optional常用方法

package com.xqm.jdk8.optional;

import java.util.Optional;

/**
 * get(): NoSuchElementException("No value present"):value
 * isPresent():  public boolean isPresent() {return value != null;}
 * orElse
 * orElseGet
 * ifPresent
 *
 *
 */
public class Test03 {

    public static void main(String[] args) {
        Optional<String> op1=Optional.of("张三");
        Optional<String> op2=Optional.empty();

        // 获取值 get(),如果为null则会抛异常
        System.out.println(op1.get());

        // isPresent()判断是否是空值,有值为true,无值为false
        System.out.println(op2.isPresent());

        // orElse  optional如果为null则是李四,否则为自己的value
        System.out.println(op2.orElse("李四"));

        //  orElseGet  return value != null ? value : other.get();
        System.out.println(op2.orElseGet(()->" Hello"));


        // ifPresent(Consumer)
      op1.ifPresent(s -> {
            System.out.println("存在值:"+s);
        });
    }
}

作用:简化代码

if(student != null){
  if(student.getClassId() != null){
    return student.getClassId();
  }else{
    return 0;
  }
}

return Optional.ofNullable(student).map(Student::getClassId).orElse(0);

日期和时间API

时间官网

https://docs.oracle.com/javase/8/docs/api/java/time/format/DateTimeFormatter.html

旧版本日期和时间

旧版本中对时间和日期的设计差。

java.util和java.sql中都有日期类。

没有时区支持,国际化差。

格式化日期有线程安全,不方便。

package com.xqm.jdk8.time;

import java.text.SimpleDateFormat;
import java.util.Date;

public class Test01 {
    public static void main(String[] args) throws Exception {
        // 1.有个基数在 1900,设计不合理
        Date date=new Date(2022,5,12);
        //  Mon Jun 12 00:00:00 CST 3922
        System.out.println(date);

        // 2.时间格式化
        SimpleDateFormat sdf=new SimpleDateFormat("yyyy-MM-dd HH:mm:ss");
        System.out.println(sdf.format(date));
        // Exception in thread "main" java.text.ParseException: Unparseable date: "2022-01-01"
        // 必须要按格式来
        System.out.println(sdf.parse("2022-01-01 12:12:12"));

        // 如果在多线程中使用SimpleDateFormat会有问题,线程不安全
    }
}

新版本日期和时间API

JDK 8中增加了一套全新的日期时间API,这套API设计合理,是线程安全的。新的日期及时间API位于 java.time 包
中,下面是一些关键类。

  • LocalDate :表示日期,包含年月日,格式为 2019-10-16
  • LocalTime :表示时间,包含时分秒,格式为 16:38:54.158549300
  • LocalDateTime :表示日期时间,包含年月日,时分秒,格式为 2018-09-06T15:33:56.750
  • DateTimeFormatter :日期时间格式化类。
  • Instant:时间戳,表示一个特定的时间瞬间。
  • Duration:用于计算2个时间(LocalTime,时分秒)的距离
  • Period:用于计算2个日期(LocalDate,年月日)的距离
  • ZonedDateTime :包含时区的时间

Java中使用的历法是ISO 8601日历系统,它是世界民用历法,也就是我们所说的公历。平年有365天,闰年是366
天。此外Java 8还提供了4套其他历法,分别是:

  • ThaiBuddhistDate:泰国佛教历
  • MinguoDate:中华民国历
  • JapaneseDate:日本历
  • HijrahDate:伊斯兰历

日期操作

 /**
     * 日期操作
     */
    @Test
    public void test01(){
        // 1. 创建指定的日期时间
        LocalDate date=LocalDate.of(2022,8,6);
        // 2022-08-06
        System.out.println("指定的日期为"+date);
        // 2. 获取当前日期
        LocalDate date1=LocalDate.now();
        // 2022-08-06
        System.out.println("当前日期为"+date1);

        // 3.获取年、月、日

        // 获取当前的年份
        System.out.println("年份:"+date1.getYear());
        // 获取当前是几月  AUGUST
        System.out.println("月份:"+date1.getMonth().getValue());
        //
        System.out.println("日:"+date1.getDayOfMonth());
        // 今年的第多少天
        System.out.println("今年的第"+date1.getDayOfYear()+"天");
        // 获取当前这周的星期几     SATURDAY
        System.out.println("星期:"+date1.getDayOfWeek().getValue());
    }
指定的日期为2022-08-06
当前日期为2022-08-06
年份:2022
月份:8
日:6
今年的第218天
星期:6

时间操作

   @Test
    public void test02(){
        LocalTime time=LocalTime.now();
        System.out.println("当前时间为:"+time);

        LocalTime time1=LocalTime.of(5,2,33);
        System.out.println("自定义时分秒为:"+time1);

        LocalTime time2=LocalTime.of(5,2);
        System.out.println("自定义时分为:"+time2);

        // 获取时间信息
        System.out.println("小时:"+time.getHour());
        System.out.println("分钟:"+time.getMinute());
        System.out.println("秒:"+time.getSecond());
        // 1s=1000ms 1ms=1000us 1us=1000ns
        System.out.println("纳秒:"+time.getNano());
    }
当前时间为:15:52:23.299
自定义时分秒为:05:02:33
自定义时分为:05:02
15
52
23
299000000

日期时间操作

   LocalDateTime localDateTime=LocalDateTime.of(2022,8,6,5,3,12);
        System.out.println("自定义时间为:"+localDateTime);

        LocalDateTime localDateTime1=LocalDateTime.now();
        System.out.println("当前时间为:"+localDateTime1);

        // 获取日期时间
        System.out.println(localDateTime1.getYear());
        System.out.println(localDateTime1.getMonth().getValue());
        System.out.println(localDateTime1.getDayOfMonth());
        System.out.println(localDateTime1.getHour());
        System.out.println(localDateTime1.getMinute());
        System.out.println(localDateTime1.getSecond());
自定义时间为:2022-08-06T05:03:12
当前时间为:2022-08-06T16:02:44.820
2022
8
6
16
2
44

日期时间修改和比较

修改
 LocalDateTime time = LocalDateTime.now();
        System.out.println(time);

        // 修改日期时间 with开头 不会修改原来的日期,而是生成一个新的对象
        // 单独修改一个参数
        LocalDateTime localDateTime = time.withYear(2010);
        LocalDateTime localDateTime1 = time.withMonth(10);
        LocalDateTime localDateTime2 = time.withDayOfMonth(20);
        // 同时修改多个参数
        LocalDateTime localDateTime3 = time.with(LocalDate.of(2020, time.getMonth(), time.getDayOfMonth()));
        System.out.println(localDateTime3);
增加和减少
// 对日期进行加法和减法的操作

        // 2天后
        LocalDateTime localDateTime4 = time.plusDays(2);
        // 2小时后
        LocalDateTime localDateTime5 = time.plusHours(2);
        // 10年前
        LocalDateTime localDateTime6 = time.minusYears(10);
比较
 // 日期时间比较
        LocalDate date=LocalDate.now();
        LocalDate date1=LocalDate.of(2020,12,9);
        boolean after = date.isAfter(date1);
        boolean before = date.isBefore(date1);
        boolean equal = date.isEqual(date1);
        System.out.println(after);
        System.out.println(before);
        System.out.println(equal);
        // 是否是闰年
        boolean leapYear = date1.isLeapYear();
        System.out.println(leapYear);
格式化和解析
 // 格式化和解析操作
        String startTime="2022-01-02 12:12:12";
        LocalDateTime localDateTime=LocalDateTime.now();

        // 使用系统默认格式
        DateTimeFormatter formatter=DateTimeFormatter.ISO_LOCAL_DATE_TIME;
        // 将日期转换为字符串   2022-08-06T16:32:07.732
        System.out.println(localDateTime.format(formatter));

        DateTimeFormatter formatter1 = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
        // 使用自己指定的时间格式   2022-08-06 16:32:07
        System.out.println(localDateTime.format(formatter1));

        // 将string解析成日期
        System.out.println(LocalDateTime.parse(startTime, formatter1));

Instant

时间戳/时间线,内部保存了1970-01-01 00:00:00以来的秒

   // 时间戳,用来统计时间消耗
        Instant now=Instant.now();
        // 2022-08-06T08:39:16.662Z
        System.out.println(now);

        // 获取现在的纳秒,因为随时会变
        //  695000000
        System.out.println(now.getNano());
        // 获取从1970-01-01到现在的秒数
        System.out.println(now.getEpochSecond())

计算日期时间差

JDK8中提供了两个工具类Duration/Period:计算日期时间差

  1. Duration:用来计算两个时间差(LocalTime)
  2. Period:用来计算两个日期差(LocalDate)
// 计算时间差
        LocalTime now = LocalTime.now();
        LocalTime time = LocalTime.of(22, 48, 59);
        System.out.println("now = " + now);
        // 通过Duration来计算时间差
        Duration duration = Duration.between(now, time);
        System.out.println(duration.toDays()); // 0
        System.out.println(duration.toHours()); // 6
        System.out.println(duration.toMinutes()); // 368
        System.out.println(duration.toMillis()); // 22124240

        // 计算日期差
        LocalDate nowDate = LocalDate.now();
        LocalDate date = LocalDate.of(1997, 12, 5);
        // 后面减去前面
        Period period = Period.between(date, nowDate);
        System.out.println(period.getYears()); // 24
        System.out.println(period.getMonths()); // 8
        System.out.println(period.getDays()); // 2

TemporalAdjuster:时间校正器

有时候我们可以需要如下调整:将日期调整到"下个月的第一天"等操作。这时我们通过时间校正器效果可能会更好。

  • TemporalAdjuster:时间校正器
  • TemporalAdjusters:通过该类静态方法提供了大量的常用TemporalAdjuster的实现。
     LocalDateTime now=LocalDateTime.now();
        // 将当前的日期调整到下个月的1号
        TemporalAdjuster adjuster=(temporal) -> {
             LocalDateTime dateTime= (LocalDateTime)temporal;
            LocalDateTime nextMonth = dateTime.plusMonths(1).withDayOfMonth(1);
            return nextMonth;
        };
        // 通过TemporalAdjusters
        LocalDateTime with = now.with(adjuster);
        // 直接获取
        LocalDateTime with1 = now.with(TemporalAdjusters.firstDayOfNextMonth());
        // 下个月1号
        System.out.println(with);

日期时间的时区

jdk8加入对日期时间时区的支持。LocalDate、LocalTime、LocalDateTime是不带时区的,带时区的日期时间类为:ZonedTime、ZonedDate、ZonedDateTime。

其中每个时区都对应着ID。ID的格式为:区域/城市,比如Asia/Shanghai。

ZoneId:该类中包含了所有时区的信息。

 @Test
    public void test(){
        // 获取所有时区ID
        ZoneId.getAvailableZoneIds().forEach(System.out::println);
    }
  @Test
    public void test1(){

        // 获取当前时间 中国使用东八区,比标准时间晚8个小时
        LocalDateTime localDateTime=LocalDateTime.now();
        // 2022-08-06T18:18:32.460
        System.out.println(localDateTime);

        // 获取标准时间 也就是减去8个小时
        ZonedDateTime now = ZonedDateTime.now(Clock.systemUTC());
        // 2022-08-06T10:20:28.279Z
        System.out.println(now);


        // 使用计算机默认时区
        ZonedDateTime zonedDateTime=ZonedDateTime.now();
        // 2022-08-06T18:17:55.604+08:00[Asia/Shanghai]
        System.out.println(zonedDateTime);


        // 使用指定的时区创建时间
        ZonedDateTime dateTime=ZonedDateTime.now(ZoneId.of("America/Cuiaba"));
        // 2022-08-06T06:23:19.291-04:00[America/Cuiaba]
        System.out.println(dateTime);
    }

其他特性

重复注解

自从Java 5中引入 注解 以来,注解开始变得非常流行,并在各个框架和项目中被广泛使用。不过注解有一个很大的限制是:在同一个地方不能多次使用同一个注解。JDK 8引入了重复注解的概念,允许在同一个地方多次使用同一个注解。在JDK 8中使用@Repeatable注解定义重复注解。

1.定义重复注解的容器

package com.xqm.jdk8.annotation;


import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;


/**
 * @Retention作用是定义被它所注解的注解保留多久,一共有三种策略,定义在RetentionPolicy枚举中.
 *
 * 从注释上看:
 *
 * source:注解只保留在源文件,当Java文件编译成class文件的时候,注解被遗弃;被编译器忽略
 *
 * class:注解被保留到class文件,但jvm加载class文件时候被遗弃,这是默认的生命周期
 *
 * runtime:注解不仅被保存到class文件中,jvm加载class文件之后,仍然存在
 *
 * 这3个生命周期分别对应于:Java源文件(.java文件) ---> .class文件 ---> 内存中的字节码。
 *
 * 生命周期长度 SOURCE < CLASS < RUNTIME
 */
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotations {
    MyAnnotation[] value();
}

2.定义可以重复的注解

package com.xqm.jdk8.annotation;

import java.lang.annotation.Repeatable;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;

/**
 * 表示这个注解能重复使用
 */
@Repeatable(MyAnnotations.class)
@Retention(RetentionPolicy.RUNTIME)
public @interface MyAnnotation {
    String value();
}

3.定义测试类

package com.xqm.jdk8.annotation;

import java.lang.annotation.Annotation;

/**
 * 可以重复注解
 */
@MyAnnotation("test1")
@MyAnnotation("test2")
@MyAnnotation("test3")
public class MyAnnoTest {

    @MyAnnotation("fun1")
    @MyAnnotation("fun2")
    public void test(){

    }

    /**
     * 解析注解
     * @param args
     */
    public static void main(String[] args) throws NoSuchMethodException {
        MyAnnotation[] annotationsByType = MyAnnoTest.class.getAnnotationsByType(MyAnnotation.class);
        // 获取类中标注的注解
        // test1
        // test2
        // test3
        for (MyAnnotation myAnnotation : annotationsByType) {
            System.out.println(myAnnotation.value());
        }

        // 获取方法中标注的重复注解
        MyAnnotation[] tests = MyAnnoTest.class.getMethod("test").getAnnotationsByType(MyAnnotation.class);
        for (MyAnnotation test : tests) {
            System.out.println(test.value());
        }
    }

}

类型注解

JDK 8为@Target元注解新增了两种类型: TYPE_PARAMETER , TYPE_USE 。

  • TYPE_PARAMETER :表示该注解能写在类型参数的声明语句中。 类型参数声明如: <T>
  • TYPE_USE :表示注解可以在任何用到类型的地方使用。

TYPE_PARAMETER

@Target(ElementType.TYPE_PARAMETER)
public @interface TypeParam {
}

使用:

public class TypeDemo01 <@TypeParam T> {

    public <@TypeParam K extends Object> K test01(){
        return null;
    }
}

TYPE_USE

@Target(ElementType.TYPE_USE)
public @interface NotNull {
}

使用

public class TypeUseDemo01 {

    public @NotNull Integer age = 10;

    public Integer sum(@NotNull Integer a,@NotNull Integer b){
        return a + b;
    }
}