JDK1.8 新特性

Java 8 新特性

Java8 新增了非常多的特性,我们主要讨论以下几个:

  • Lambda 表达式 − Lambda 允许把函数作为一个方法的参数(函数作为参数传递到方法中)。
  • 方法引用 − 方法引用提供了非常有用的语法,可以直接引用已有Java类或对象(实例)的方法或构造器。与lambda联合使用,方法引用可以使语言的构造更紧凑简洁,减少冗余代码。
  • 默认方法 − 默认方法就是一个在接口里面有了一个实现的方法。
  • 新工具 − 新的编译工具,如:Nashorn引擎 jjs、 类依赖分析器jdeps。
  • Stream API −新添加的Stream API(java.util.stream) 把真正的函数式编程风格引入到Java中。
  • Date Time API − 加强对日期与时间的处理。
  • Optional 类Optional 类已经成为 Java 8 类库的一部分,用来解决空指针异常。
  • Nashorn, JavaScript 引擎Java 8提供了一个新的Nashorn javascript引擎,它允许我们在JVM上运行特定的javascript应用。

1. Lambda 表达式

Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。

Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。使用 Lambda 表达式可以使代码变的更加简洁紧凑。

语法

lambda 表达式的语法格式如下:

(parameters) -> expression
或
(parameters) ->{ statements; }

以下是lambda表达式的重要特征:

  • 可选类型声明: 不需要声明参数类型,编译器可以统一识别参数值。
  • 可选的参数圆括号: 一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • 可选的大括号: 如果主体包含了一个语句,就不需要使用大括号。
  • 可选的返回关键字: 如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定明表达式返回了一个数值。

Lambda 表达式实例

public static void main(String[] args) {
        // 普通写法
        PersonCallBack p=new PersonCallBack() {
            @Override
            public void callback() {
                System.out.println("写作业!");
            }
        };
        new lamdabaTest3().test("小明",p);
    }

    public void test(String name,PersonCallBack  callBack){
        System.out.println(name);
        callBack.callback();
    }
}

// 函数式接口
@FunctionalInterface
interface PersonCallBack{
    void callback();
}

很显然,这个并不是一个很简洁的写法,我们采用Java8Lambada表达式来实现,那么如何简化呢?

整个过程:去掉修饰符(public等)、去掉函数的名字(因为已经赋给变量,变量知道此方法名–往后知道抽象方法唯一,不需要方法名了)、去掉返回值类型(编译器可以推断)、去掉参数类型(编译器可以推断参数类型),最终的结果是下面的形式:

// Lambada 写法
PersonCallBack p1= () -> System.out.println("写作业!");

new lamdabaTest3().test("小明",p);

分析: 这样的最终结果就是把"一块代码赋给一个变量"。或者说是"这个被赋给一个变量的函数"就是一个Lambada表达式,由于Lambada可以直接赋给一个"变量",我们可以把Lambada(这里表示为变量)作为参数传递给函数。但是变量(Lambada表达式)的类型是什么呢?

// 函数式接口
@FunctionalInterface
interface PersonCallBack{
    void callback();
}

说明:所有的Lambada的类型都是一个接口,而Lambada表达式本身(“那段代码”)就是一个接口的实现,这是理解Lambada的一个关键所在,理解上可以这样认为:Lambada表达式就是产生一个实现接口中唯一的抽象方法的子实现类的对象,因此最终结果:

// Lambada 写法
PersonCallBack p1= () -> System.out.println("写作业!");

函数式接口: 接口中只有一个需要被实现的抽象函数

说明:为了避免后来的人在接口中增加新的接口函数,导致其有多个接口函数需要被实现,变成非函数式接口,引入了一个新的Annotation(注解):@FunctionalInterface。可以把他它放在一个接口前,表示这个接口是一个函数式接口,加上它的接口不会被编译,如果加上此标记就不能再添加其他的抽象方法,否则会报错。它有点像@Override,都是声明了一种使用意图,避免你把它用错。

总结: lambda表达式本质是匿名方法

Lambda 表达式的结构

Lambada表达式的语法

(param1,param2,param3) -> { }

Lambda 表达式在Java 语言中引入了一个新的语法元素和操作符。这个操作符为 “ ->”,该操作符被称为 Lambda 操作符或箭头操作符。它将 Lambda 分为两个部分:

  • 左侧:指定了 Lambda 表达式需要的方法参数列表
  • 右侧:指定了 Lambda 体,即 Lambda 表达式要执行的功能

使用说明:

  • 一个 Lambda 表达式可以有零个或多个参数,参数的类型既可以明确声明,也可以根据上下文来推断
  • 圆括号内,方法参数列表之间用逗号相隔
  • 当只有一个参数,且其类型可推导时,圆括号()可省略
  • Lambda 表达式的主体可包含零条或多条语句,如果Lambda表达式的主体只有一条语句,花括号{}可省略,如果有返回值,return也可以省略,同时body中的“;”也可以省略。匿名函数的返回类型与该主体表达式一致
  • 如果 Lambda 表达式的主体包含一条以上语句,则表达式必须包含在花括号{}中(形成代码块)。匿名函数的返回类型与代码块的返回类型一致,若没有返回则为空

简单应用

对比匿名内部类做为参数传递和Lambda表达式作为参数来传递–Runnable,Callable接口(具体看例子)

public class LamadaDemo {
    public static void main(String[] args) {
 
        //匿名内部类的形式开启一个线程
        new Thread(new Runnable() {
            @Override
            public void run() {
                System.out.println("我爱你!");
            }
        }).start();
 
        //Lambada表达式创建匿名内部类开启一个线程
        new Thread(() -> System.out.println("-------------")).start();
    }
public class LamadaDemo1 {
    public static void main(String[] args) {
        //常见的函数式接口:Runnable、 Comparable--排序(是一个函数式接口吗?)
 
         Comparable<Integer> comparable=new Comparable<Integer>() {
             @Override
             public int compareTo(Integer o) {
                 return 0;
             }
         };
 
         //Lambada表达式的方法
 
        Comparable<Integer> com=(a)->a;
        int i = com.compareTo(3);
        System.out.println(i);
    }
}

2. 方法引用

概念: 方法引用其实是Lambda表达式的另一种写法,当要传递给Lambda体的操作已经有实现的方法了,可以使用方法引用

语法:使用操作符 “ ::” 将方法名和对象或类的名字分隔开来

几种常见形式:

  • 类名::静态方法
  • 对象::实例方法
  • 类名::实例方法

二.构造器引用

  • ClassName::new

三 数组引用

  • Type::new

注意:

  • Lambda体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保存一致
  • 若Lambda参数列表中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用ClassName::method;不管怎么说,实质还是抽象方法的实现
/** 一.方法引用:若Lambda体中的内容有方法已经实现了,我们可以使用"方法引用"
 *          (可以理解为方法引用是Lambda表达式的另外一种表现形式)
 *
 *  主要有三种语法格式:
 *    对象::实例方法
 *    类::静态方法名
 *    类::实例方法名
 * 注意:
 *  1.Lambda 体中调用方法的参数列表与返回值类型,要与函数式接口中抽象方法的函数列表和返回值类型保持一致!
 *  2.若Lambda参数列表中的第一个参数是实例方法的调用者,而第二个参数是实例方法的参数时,可以使用ClassName::method
 * 二.构造器引用
 *
 * ClassName::new
 *
 * 三 数组引用
 *
 * Type::new
 *
 *
 *
 */

public class TestMethodRef {

    //数组引用
    @Test
    public void test7(){
        Function<Integer,String[]> function = (x) -> new String[x];
        String[] strings = function.apply(10);
        System.out.println(strings);

        Function<Integer,String[]> function2 = String[]::new;
        System.out.println(function2.apply(10).length);

        Supplier<ArrayList<Person>> supplier = () -> new ArrayList<>();

        Supplier<ArrayList<Person>> supplier1 = ArrayList::new;
        ArrayList<Person> people = supplier1.get();

    }





    //构造器引用
    @Test
    public void test5(){
        Supplier<Person> supplier = () -> new Person();

        Supplier<Person> supplier1 = Person::new;
        System.out.println(supplier1.get());
    }

    @Test
    public void test6(){
        Function<Integer,Person> function = (x) -> new Person(x);

        Function<Integer,Person> function1 = Person::new;

        Person apply = function1.apply(15);
        System.out.println(apply);
    }


    //对象::实例方法
    @Test
    public void  test1(){
        Consumer<String> con = (x) -> System.out.println(x);

        PrintStream out = System.out;

        Consumer<String> con1 = out::println;

        con1.accept("asdfs");

        BiFunction<String,Integer,Person> biFunction = (x,y) -> new Person(x,y);

        BiFunction<String,Integer,Person> biFunction1 = Person::new;
        System.out.println(biFunction1.apply("小名",19));
    }
    @Test
    public void test2() {
        Person person = new Person();

        Supplier<String> supplier =  () -> person.getUserName();

        String s = supplier.get();
        //对象::实例方法
        Supplier<Integer> supplier1 = person::getAge;

        System.out.println(supplier1.get());

        System.out.println(s);
    }

    //类::静态方法名
    @Test
    public void test3(){
        Comparator<Integer> com = (x,y) -> Integer.compare(x,y);

        Comparator<Integer> com1 = Integer::compare;

    }

    //类::实例方法名
    @Test
    public void test4(){
        BiPredicate<String,String> bp = (x,y) -> x.equals(y);

        BiPredicate<String,String> bp1 = String::equals;
    }


}

3. 函数式接口

函数式接口(Functional Interface)就是一个有且仅有一个抽象方法,但是可以有多个非抽象方法的接口。在接口上添加@FunctionalInterface注解声明为函数时接口

函数式接口可以被隐式转换为 lambda 表达式。

@FunctionalInterface
public interface MathOperation {
    Integer add(Integer a,Integer b);

}

函数式接口可以对现有的函数友好地支持 lambda

JDK 1.8 新增加的函数接口:

  • java.util.function

java.util.function 它包含了很多类,用来支持 Java的 函数式编程,该包中的函数式接口有:

序号接口 & 描述
1Function<T,R> method: R apply(T t);
-接受一个输入参数,返回一个结果。
2Consumer method: void accept(T t);
-代表了接受一个输入参数并且无返回的操作
3Predicate method: boolean test(T t);
-接受一个输入参数,返回一个布尔值结果。
4Supplier method : T get();
-无参数,返回一个结果。

函数式接口实例

Predicate <T> 接口是一个函数式接口,它接受一个输入参数 T,返回一个布尔值结果。

该接口包含多种默认方法来将Predicate组合成其他复杂的逻辑(比如:与,或,非)。

该接口用于测试对象是 true 或 false。

/*
    Java 8 内置的四大核心函数式接口

    Consumer<T> :消费型接口
         void accept(T t);
    Supplier<T> : 供给型接口
           T get();
    Function<T,R> :函数式接口
            R apply(T t);
    Predicate<T> :断言型接口
            boolean test(T t);

 */
public class LambdaDemo6 {
    //  Consumer<T> :消费型接口
    @Test
    public void test1(){
        happy(1000,(m)-> System.out.println("洗澡消费"+m+"元"));
    }
    public void happy(double money, Consumer<Double> con){
        con.accept(money);
    }


    //Supplier<T> : 供给型接口
    @Test
    public void test2(){
        getNumList(10,() -> (int)( Math.random()*100)).forEach((m)-> System.out.println(m));
    }
    //产生指定个数的整数,并放入集合中
    public List<Integer> getNumList(int num, Supplier<Integer> supplier){
        ArrayList<Integer> list = new ArrayList<>();
        for (int i=0;i<num;i++){
            Integer integer = supplier.get();
            list.add(integer);
        }
        return list;
    }

    //Function<T,R> :函数式接口
    @Test
    public void test3(){
        String newStr = strHandler("\t\t\t\t 我爱张柏芝",(a)-> a.trim());
        System.out.println(newStr);

        String subStr = strHandler("我喜欢你",(a)->a.substring(0,3));
        System.out.println(subStr);
    }

    //需求:用于处理字符串
    public String strHandler(String str, Function<String,String> function){
        return function.apply(str);
    }

    //Predicate<T> :断言型接口
    @Test
    public void test4(){
        List<String> lists = Arrays.asList("Hello","atagui","Lambda","www","ok");
        filterStr(lists,(s) -> s.length()>3).forEach((a)-> System.out.println(a));
    }

    //将满足条件的字符串放入到集合中
    public List<String> filterStr(List<String> list,Predicate<String> predicate) {
        ArrayList<String> strList = new ArrayList<>();
        for (String str : list) {
            if (predicate.test(str)){
                strList.add(str);
            }
        }
        return strList;
    }
}


4. Java 8 默认方法

Java 8 新增了接口的默认方法。简单说,默认方法就是接口可以有实现方法,而且不需要实现类去实现其方法。

为什么要有这个特性?

首先,之前的接口是个双刃剑,好处是面向抽象而不是面向具体编程,缺陷是,当需要修改接口时候,需要修改全部实现该接口的类,目前的 java 8 之前的集合框架没有 foreach 方法,通常能想到的解决办法是在JDK里给相关的接口添加新的方法及实现。然而,对于已经发布的版本,是没法在给接口添加新方法的同时不影响已有的实现。所以引进的默认方法。他们的目的是为了解决接口的修改与现有的实现不兼容的问题。

语法

public interface Vehicle {
   default void print(){
      System.out.println("我是一辆车!");
   }
}

多个默认方法

一个接口有默认方法,考虑这样的情况,一个类实现了多个接口,且这些接口有相同的默认方法,以下实例说明了这种情况的解决方法:

public interface Vehicle {
   default void print(){
      System.out.println("我是一辆车!");
   }
}
 
public interface FourWheeler {
   default void print(){
      System.out.println("我是一辆四轮车!");
   }
}

第一个解决方案是创建自己的默认方法,来覆盖重写接口的默认方法:

public class Car implements Vehicle, FourWheeler {
   default void print(){
      System.out.println("我是一辆四轮汽车!");
   }
}

第二种解决方案可以使用 super 来调用指定接口的默认方法:

public class Car implements Vehicle, FourWheeler {
   public void print(){
      Vehicle.super.print();
   }
}

静态默认方法

Java 8 的另一个特性是接口可以声明(并且可以提供实现)静态方法。例如:

public interface Vehicle {
   default void print(){
      System.out.println("我是一辆车!");
   }
    // 静态方法
   static void blowHorn(){
      System.out.println("按喇叭!!!");
   }
}

5. Java 8 Stream

Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。

Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对Java集合运算和表达的高阶抽象。

Stream API可以极大提高Java程序员的生产力,让程序员写出高效率、干净、简洁的代码。

这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选排序聚合等。

元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。

+--------------------+       +------+   +------+   +---+   +-------+
| stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
+--------------------+       +------+   +------+   +---+   +-------+

什么是 Stream?

Stream(流)是一个来自数据源的元素队列并支持聚合操作

  • 元素是特定类型的对象,形成一个队列。 Java中的Stream并不会存储元素,而是按需计算。
  • 数据源 流的来源。 可以是集合,数组,I/O channel, 产生器generator 等。
  • 聚合操作 类似SQL语句一样的操作, 比如filter, map, reduce, find, match, sorted等。

和以前的Collection操作不同, Stream操作还有两个基础的特征:

  • Pipelining: 中间操作都会返回流对象本身。 这样多个操作可以串联成一个管道, 如同流式风格(fluent style)。 这样做可以对操作进行优化, 比如延迟执行(laziness)和短路( short-circuiting)。
  • 内部迭代: 以前对集合遍历都是通过Iterator或者For-Each的方式, 显式的在集合外部进行迭代, 这叫做外部迭代。 Stream提供了内部迭代的方式, 通过访问者模式(Visitor实现。

生成流

Java 8中, 集合接口有两个方法来生成流:

  • stream() − 为集合创建串行流。
  • parallelStream() − 为集合创建并行流。
/**
 * 一 Stream的三个步骤
 * 1. 创建Stream流
 * 2. 中间操作
 * 3. 终止操作(终端操作)
 */
public class TestStreamAPI {

    //创建Stream
    @Test
    public void test1(){
        //1. 可以通过Collection系列集合提供的stream() 或 parallelSteam()
        List<String> list = new ArrayList<>();
        Stream<String> stream = list.stream();

        //2.通过Arrays中的静态方法stream()获取数组流
        Person[] persion = new Person[10];
        Stream<Person> stream1 = Arrays.stream(persion);

        //3. 通过Stream类中的静态方法of()
        Stream<String> aaa = Stream.of("aaa", "bbb", "ccc");

        //4. 创建无限流
        //迭代
        Stream<Integer> iterate = Stream.iterate(1, (x) -> x + 3);
        iterate.limit(10).forEach((x)-> System.out.println(x));

        //生成
        Stream.generate(()->(int)(Math.random()*100)).limit(10).forEach(System.out::println);
    }
}

Stream API

中间操作
filter(),limit(),shkip(),distinct()
    /**
     * 筛选与切片
     * filter---接收Lambda,从流中排除某些元素;filter(能产生boolean结果的Lambda),如果参数Lambda产生了true值,则要元素;
     * 如果产生了false,则不要这个元素。
     * limit()---截断流,使其不超过给定数量
     * skip()---跳过元素,返回一个扔掉了前n个元素的流,若流中元素不足n个,则返回一个空流,与limit(n)互补
     * distinct--筛选,通过流所生成元素的hashcode()和equals()去除重复元素
     */
    
    @Test
    public void test3(){
        list.stream()
                .filter((x)->x.getAge()>20)
                .skip(2)
                .distinct()
                .forEach(System.out::println);
    }


    @Test
    public void test2(){
        list.stream()
                .filter((e)-> {
                    System.out.println("短路");
                   return e.getAge()>20;
                })
                .limit(2)
                .forEach(System.out::println);


    }

    //内部迭代:迭代操作有Stream API完成
    @Test
    public void test1(){
        //中间操作
        Stream<Person> personStream = list.stream()
                .filter((e) -> {
                    System.out.println("Stream API的中间操作");
                   return e.getAge() > 22;
                });

        //终止操作:一次性执行全部内容,即"惰性求值"
        personStream.forEach(System.out::println);
    } 
     
map()
    /**
     * 映射
     * map---接收Lambda,将元素转换成其他形式或提取信息,接收一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
     *flatMap--- 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流
     */
    @Test
    public void test4(){
        List<String> lists = Arrays.asList("aaa","bbb","ccc","ddd","eee");
        lists.stream()
                .map((x)-> x.toUpperCase())
                .forEach(System.out::println);
        System.out.println("----------------------");

        list.stream()
                .map(Person::getUserName)
                .forEach(System.out::println);
        System.out.println("-----------------------");

        Stream<Stream<Character>> stream = lists.stream()
                .map((x) -> TestStreamAPI2.filterCharacter(x));
        stream.forEach((sm)->
                sm.forEach(System.out::println)
                );

        System.out.println("-----------------------");
        //flatMap
        Stream<Character> characterStream = lists.stream()
                .flatMap(TestStreamAPI2::filterCharacter);
        characterStream.forEach(System.out::println);


    }


    public static  Stream<Character> filterCharacter(String str){
        List<Character> list1 = new ArrayList<>();
        for (Character ch:str.toCharArray()) {
            list1.add(ch);
        }
        return  list1.stream();
    }
peek()

peek入参是Consumer,没有返回值

Stream<T> peek(Consumer<? super T> action);

map入参是Function,是需要返回值的

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

peek接收一个没有返回值的λ表达式,可以做一些输出,外部处理等。map接收一个有返回值的λ表达式,之后Stream的泛型类型将转换为map参数λ表达式返回的类型

当我们对集合中的对象进行填充值时可以使用
map

 List<OverallFund> fundList = overallFunds.stream().map(item -> {
                    long nextLong = RandomUtils.nextLong();
                    // 设置主键id
                    item.setId(nextLong);
                    // 设置区划
                    item.setAdmdivCode("430500");
                    // 设置创建时间
                    item.setCreateUser("430500");
                    item.setCreateTime(new Date());
                    return item;
                }).collect(Collectors.toList());

peek()

List<OverallFund> fundList = overallFunds.stream().peek(item -> {
                    long nextLong = RandomUtils.nextLong();
                    // 设置主键id
                    item.setId(nextLong);
                    // 设置区划
                    item.setAdmdivCode("430500");
                    // 设置创建时间
                    item.setCreateUser("430500");
                    item.setCreateTime(new Date());
                }).collect(Collectors.toList());

我们发现Function 比 Consumer 多了一个 return。这也就是peek 与 map的区别了。

sorted(),sorted(Comparator com)
    /**
     * 排序
     * sorted()---自然排序 (Comparable)
     * sorted(Comparator com) ---定制排序(Comparator)
     */

     @Test
     public void test6(){
         List<String> lists = Arrays.asList("bbb","aaa","ddd","eee","ccc");
         lists.stream()
                 .sorted()
                 .forEach(System.out::println);

         System.out.println("------------------------------");

         list.stream()
                 .sorted((e1,e2)-> e1.getAge().compareTo(e2.getAge())).forEach(System.out::println);
     }

终止操作(终端操作)

    /**
     * 查找与匹配
     * allMatch---检查是否匹配所有元素
     * anyMatch---检查是否至少匹配一个元素
     * noneMatch---检查是否没有匹配所有元素
     * findFirst---返回第一个元素
     * findAny---返回当前流中的任意元素
     * count---放回流中元素的总个数
     * max---返回流中最大值
     * min---返回流中最小值
     */

    @Test
    public  void test2(){
        long count = list.stream()
                .count();
        System.out.println(count);

        Optional<Person> max = list.stream()
                .max((e1, e2) -> Integer.compare(e1.getAge(), e2.getAge()));
        System.out.println(max.get());

        Optional<Integer> min = list.stream()
                .map(Person::getAge)
                .min(Integer::compareTo);
        System.out.println(min.get());

    }



    @Test
    public void test1(){
        boolean b = list.stream()
                .allMatch((e) -> e.getStatus().equals(Status.BUSY));
        System.out.println(b);

        boolean b1 = list.stream()
                .anyMatch((e) -> e.getStatus().equals(Status.BUSY));
        System.out.println(b1);

        boolean b2 = list.stream()
                .noneMatch((e) -> e.getStatus().equals(Status.BUSY));
        System.out.println(b2);

        Optional<Person> op = list.stream()
                .sorted((e1, e2) -> -Integer.compare(e1.getAge(), e2.getAge()))
                .findFirst();
        //System.out.println(op.get());

        //多线程查找
        Optional<Person> op1 = list.parallelStream()
                .filter((e) -> e.getStatus().equals(Status.FREE))
                .findAny();
        System.out.println(op1.get());
    }
reduce
    /**归约:
     * reduce(T identity, BinaryOperator<T> accumulator)
     * reduce(BinaryOperator<T> accumulator)
     * 可以将流中元素反复结合起来,得到一个值
     */

      @Test
      public void test3(){
          List<Integer> list1 = Arrays.asList(1,2,3,4,5,6,7,8,9,10);
          Integer sum = list1.stream()
                  .reduce(0, (x, y) -> x + y);
          System.out.println(sum);
          System.out.println("---------------------");

          Optional<Integer> reduce = list.stream()
                  .map(Person::getAge)
                  .reduce(Integer::sum);
          System.out.println(reduce.get());
      }
collect
    /**
     * 收集
     * collect---将流转换为其他形式,接收一个Collector接口的实现,用于给Stream中元素做汇总的方法
     */
    @Test
    public void test5(){
        //总数
        Long collect = list.stream()
                .collect(Collectors.counting());
        System.out.println("总数:"+collect);


        //平均值
        Double collect1 = list.stream()
                .collect(Collectors.averagingLong(Person::getAge));

        System.out.println("平均值:"+collect1);
        //总和
        Integer collect2 = list.stream()
                .collect(Collectors.summingInt(Person::getAge));
        System.out.println("总和:"+collect2);

        //最大值
        Optional<Person> collect3 = list.stream()
                .collect(Collectors.maxBy((e1, e2) -> Integer.compare(e1.getAge(), e2.getAge())));
        System.out.println("最大值:"+collect3.get());

        //最小值
        Optional<Person> collect4 = list.stream()
                .collect(Collectors.minBy((e1, e2) -> Integer.compare(e1.getAge(), e2.getAge())));
        System.out.println("最小值:"+collect4.get());
    }


	/**
     * Collectors: 收集转换为其他集合类型
     */
    @Test
    public void test4(){
        List<String> collect = list.stream()
                .map(Person::getUserName)
                .collect(Collectors.toList());
        //collect.forEach(System.out::println);

        Set<String> collect1 = list.stream()
                .map(Person::getUserName)
                .collect(Collectors.toSet());
        collect1.forEach(System.out::println);
		
		
        Set<String> collect2 = list.stream()
                .map(Person::getUserName)
                .collect(Collectors.toCollection(HashSet::new));
    }
   //分组
    @Test
    public void test6(){
        Map<Status, List<Person>> map = list.stream()
                .collect(Collectors.groupingBy(Person::getStatus));
        System.out.println(map);

    }
    
    //多级分组
    @Test
    public void test7(){
        Map<Status, Map<String, List<Person>>> collect = list.stream()
                .collect(Collectors.groupingBy(Person::getStatus, Collectors.groupingBy(
                        (e) -> {
                            if (((Person) e).getAge() < 20) {
                                return "高中生";
                            } else if (((Person) e).getAge() > 20) {
                                return "大学生";
                            } else {
                                return "成年人";
                            }
                        }
                )));
        System.out.println(collect);
    }
    
    
    
    //分区  满足条件一个区,不满足条件一个区
    @Test
    public void test8(){
        Map<Boolean, List<Person>> collect = list.stream()
                .collect(Collectors.partitioningBy((e) -> e.getAge() > 22));
        System.out.println(collect);
    }
    
    
        @Test
    public void test10(){
        String collect = list.stream()
                .map(Person::getUserName)
                .collect(Collectors.joining(",","===","==="));
        System.out.println(collect);
    }


    @Test
    public void test9(){
        IntSummaryStatistics collect = list.stream()
                .collect(Collectors.summarizingInt(Person::getAge));

        System.out.println(collect.getMax());
        System.out.println(collect.getSum());
        System.out.println(collect.getMin());
    }

分组:Collectors.groupingBy
        // 1.按照年龄分组
        Map<Integer, List<Person>> collect = list.stream().collect(Collectors.groupingBy(Person::getAge));
        collect.forEach((k,v)->{
            System.out.println(k+"="+v);
        });


        // 2.按照年龄和工资分组
        list.stream().collect(Collectors.groupingBy(item->{
            StringBuffer buffer = new StringBuffer();
            // 这里是Map的key: age_salary
            return buffer.append(item.getAge()).append("_").append(item.getSalary());
        })).forEach((k,v)->{
            System.out.println(k+"="+v);
        });


        // 3.按照条件分组  A组:20岁以下  B组:20到60岁  C组:60岁以上的
        Map<String, List<Person>> collect2 = list.stream()
                .collect(Collectors.groupingBy(p -> {
                    if(p.getAge() < 20){
                        // map的key是A
                        return "A";
                    }else if(p.getAge()>=20 && p.getAge()<=60){
                        // map的key是B
                        return "B";
                    }else {
                        // map的key是C
                        return "C";
                    }
                }));
        collect2.forEach((k,v)->{
            System.out.println(k+"="+v);
        });


        //4.多级分组  先按照城市分组 在按照年龄分组
        //要实现多级分组,可以使用一个由双参数版本的Collectors.groupingBy工厂方法创建的收集器,
        // 它除了普通的分类函数之外,还可以接受collector类型的第二个参数。
        // 那么要进行二级分组的话,我们可以把一个内层groupingBy传递给外层groupingBy,并定义一个为流中项目分类的二级标准。
        Map<String, Map<String, List<Person>>> collect3 = list
                .stream()
                .collect(Collectors.groupingBy(Person::getUsername, // 一级分组:姓名
                        Collectors.groupingBy(p -> { // 二级分组:年龄
                            if (p.getAge() < 20) {
                                // map的key是A
                                return "A";
                            } else if (p.getAge() >= 20 && p.getAge() <= 60) {
                                // map的key是B
                                return "B";
                            } else {
                                // map的key是C
                                return "C";
                            }
                        })));

        // 5.分组求每组数量  每个城市有几个人
        Map<String, Long> collect4 = list
                .stream()
                .collect(Collectors.groupingBy(Person::getCity, Collectors.counting()));

        // 6.每个城市的人的工资总和
        Map<String, Double> collect5 = list
                .stream()
                .collect(Collectors.groupingBy(Person::getCity, Collectors.summingDouble(Person::getSalary)));


        // 7.找到每个城市的员工姓名  联合其他的收集器使用
        Map<String, List<String>> collect6 = list
                .stream()
                .collect(Collectors.groupingBy(Person::getCity, Collectors.mapping(Person::getUsername, Collectors.toList())));
   
		// Bigdecimal类型对象 分组求和
    	//不分组
		BigDecimal intergralAmount = integralRecords.stream().map(UserIntegralRecord::getIntegral).reduce(BigDecimal.ZERO,BigDecimal::add);

		//分组
		//Map<uid,总积分>
		Map<Integer, BigDecimal> integralMap = integralRecords.stream().collect(
		        Collectors.groupingBy(UserIntegralRecord::getUid,
		                Collectors.reducing(BigDecimal.ZERO,UserIntegralRecord::getIntegral,BigDecimal::add)));
 }
Collectors.toMap
    public static void main(String[] args) {
        List<Person> list = new ArrayList<>();
        list.add(new Person("hepengju", 28, 20000.0));
        list.add(new Person("lisi"    , 44, 40000.0));
        list.add(new Person("wangwu"  , 55, 50000.0));
        list.add(new Person("zhaoliu" , 66, 60000.0));
        list.add(new Person("zhangsan", 33, 33333.0));
        list.add(new Person("zhangsan", 23, 10000.0));
    }

写法一、

 // Function.identity()  为  Person对象
Map<String, Person> collect = list.stream().collect(Collectors.toMap(Person::getUsername, Function.identity()));

下面写法等同上面相同

Map<String, Person> collect1 = list.stream().collect(Collectors.toMap(Person::getUsername, v -> v));

当数组中有重复的username会报错,Duplicate key Person(username=zhangsan, age=33, salary=33333.0)
在这里插入图片描述
解决上面问题的写法

 Map<String, Person> collect1 = list.stream().collect(Collectors.toMap(Person::getUsername, v -> v,(entity1, entity2) -> entity2));

在这里插入图片描述

或者使用,也创建其他类型的Map,ConcurrentHashMap::new / HashMap::new

Map<String, Person> collect2 = list.stream().collect(Collectors.toMap(Person::getUsername, Function.identity(),(entity1, entity2) -> entity2, ConcurrentHashMap::new));    
collect1.forEach((k,v)->{ System.out.println(k+"="+v);});

(entity1, entity2) -> entity2) 里使用的箭头函数,也就是说当出现了重复key的数据时,会回调这个方法,可以在这个方法里处理重复Key数据问题。我这里取的是最后一个

  • (entity1, entity2) -> entity1): 如果数组中有多个,表示取第一个
  • (entity1, entity2) -> entity2): 如果数组中有多个,表示取最后一个
判断集合中重复元素
@Data
@AllArgsConstructor
@NoArgsConstructor
public class User {
    private String name;
    private Integer age;
}

@Test
public void streamApi() {
   User user1 = new User("王维", 24);
   User user2 = new User("李白", 18);
   User user3 = new User("杜甫", 18);
   User user4 = new User("王之焕", 21);
   User user5 = new User("欧阳修", 21);

   List<User> users = Arrays.asList(user1, user2, user3, user4, user5);
   // 分组统计重复的年龄
   Map<Integer, Long> mapGroup = users.stream().collect(Collectors.groupingBy(User::getAge, Collectors.counting()));

   // 获取重复的key值
   List<Integer> list = mapGroup.entrySet().stream().
           filter(item -> item.getValue() > 1).map(Map.Entry::getKey).collect(Collectors.toList());

}

forEach

Stream 提供了新的方法'forEach'来迭代流中的每个数据。以下代码片段使用 forEach 输出了10个随机数:

Random random = new Random();
random.ints().limit(10).forEach(System.out::println);

map

map 方法用于映射每个元素到对应的结果,以下代码片段使用 map 输出了元素对应的平方数:

List<Integer> numbers = Arrays.asList(3, 2, 2, 3, 7, 3, 5);
// 获取对应的平方数
List<Integer> squaresList = numbers.stream().map( i -> i*i).distinct().collect(Collectors.toList());

filter

filter 方法用于通过设置的条件过滤出元素。返回由该流中匹配给定元素的元素组成的流。

解释:filter(string -> string.isEmpty()) 参数Lambda产生了true值,则保留元素;为false,则流中排除元素

以下代码片段使用 filter 方法过滤出空字符串:

List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
// 获取空字符串的数量
long count = strings.stream().filter(string -> string.isEmpty()).count();

limit

limit 方法用于获取指定数量的流。 以下代码片段使用 limit 方法打印出 10 条数据:

Random random = new Random();
random.ints().limit(10).forEach(System.out::println);

sorted

sorted 方法用于对流进行排序。以下代码片段使用 sorted 方法对输出的 10 个随机数进行排序:

Random random = new Random();
random.ints().limit(10).sorted().forEach(System.out::println);

并行(parallel)程序

parallelStream 是流并行处理程序的代替方法。用多线程的方式处理stream流

List.of(1,2,3,4,5,6).stream().parallel().forEach(x->{
    System.out.println(Thread.currentThread().getName()+"::"+x);
});

在这里插入图片描述

parallelStream并行流处理线程安全问题

用多线程并行流的方式往arrlist中添加1000个元素,list实际大小不等于1000

@Test
public void test1() {
    ArrayList<Integer> list = new ArrayList<>();
    IntStream.rangeClosed(1,1000).parallel().forEach(list::add);
    System.out.println(list.size());
}

解决线程安全问题

synchronized

使用synchronized同步代码块

  @Test
    public void test1() {
        ArrayList<Integer> list = new ArrayList<>();
        Object o = new Object();
        IntStream.rangeClosed(1,1000).parallel().forEach(
               x->{
                   synchronized (o) {
                       list.add(x);
                   }
               }
        );
        System.out.println(list.size());
    }
Collections.synchronizedList

使用Collections集合工具类提供的集合安全转换方法
在这里插入图片描述

 @Test
    public void test2() {
        ArrayList<Integer> list = new ArrayList<>();
        List<Integer> list1 = Collections.synchronizedList(list);
        IntStream.rangeClosed(1,1000).parallel().forEach(
                list1::add
        );
        System.out.println(list.size());
    }
Vector集合类

使用类型安全的集合类Vector

  @Test
    public void test3() {
        Vector<Integer> list = new Vector<>();
        IntStream.rangeClosed(1,1000).parallel().forEach(
                list::add
        );
        System.out.println(list.size());
    }

6. Optional类

Optional 类(java.util.Optional) 是一个容器类,它可以保存类型T的值,代表这个值存在。或者仅仅保存null,表示这个值不存在。原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。

创建Optional类对象的方法

  • Optional.of(T t) : 创建一个 Optional 实例,t必须非空;
  • Optional.empty() : 创建一个空的 Optional 实例
  • Optional.ofNullable(T t):t可以为null
@Data
@AllArgsConstructor
@NoArgsConstructor
class Student {
    private String name;
    private Integer age;
}
public void test1() {
	// 声明一个空Optional
	Optional<Object> empty = Optional.empty();
	
	// 依据一个非空值创建Optional
	Student student = new Student();
	Optional<Student> os1 = Optional.of(student);
	
	// 可接受null的Optional
	Student student1 = null;
	Optional<Student> os2 = Optional.ofNullable(student1);
}	

判断Optional容器中是否包含对象

  • boolean isPresent() : 判断是否包含对象
  • void ifPresent(Consumer<? super T> consumer) :如果有值,就执行Consumer接口的实现代码,并且该值会作为参数传给它。
public void test1() {
    Student student = new Student();
    Optional<Student> os1 = Optional.ofNullable(student);
    boolean present = os1.isPresent();
    System.out.println(present);

    // 利用Optional的ifPresent方法做出如下:当student不为空的时候将name赋值为张三
    Optional.ofNullable(student).ifPresent(p -> p.setName("张三"));
}

获取Optional容器的对象

  • T get(): 如果调用对象包含值,返回该值,否则抛异常
  • T orElse(T other) :如果有值则将其返回,否则返回指定的other对象。
  • T orElseGet(Supplier<? extends T> other) :如果有值则将其返回,否则返回由Supplier接口实现提供的对象。
  • T orElseThrow(Supplier<? extends X> exceptionSupplier) :如果有值则将其返回,否则抛出由Supplier接口实现提供的异常。
public void test1() throws Exception {
    Student student = null;
    Optional<Student> os1 = Optional.ofNullable(student);
    // 使用get一定要注意,假如student对象为空,get是会报错的
    // java.util.NoSuchElementException: No value present
    Student student1 = os1.get();

    // 当student为空的时候,返回我们新建的这个对象,有点像三目运算的感觉
    Student student2 = os1.orElse(new Student("张三", 18));

    // orElseGet就是当student为空的时候,返回通过Supplier供应商函数创建的对象
    Student student3 = os1.orElseGet(() -> new Student("张三", 18));

    // orElseThrow就是当student为空的时候,可以抛出我们指定的异常
    os1.orElseThrow(() -> new Exception());
}

过滤

  • Optional<T> filter(Predicate<? super <T> predicate):如果值存在,并且这个值匹配给定的 predicate,返回一个Optional用以描述这个值,否则返回一个空的Optional。
public void test1() {
    Student student = new Student("李四", 3);
    Optional<Student> os1 = Optional.ofNullable(student);
    os1.filter(p -> p.getName().equals("张三")).ifPresent(x -> System.out.println("OK"));
}

映射

  • <U>Optional<U> map(Function<? super T,? extends U> mapper):如果有值,则对其执行调用映射函数得到返回值。如果返回值不为 null,则创建包含映射返回值的Optional作为map方法返回值,否则返回空Optional。
  • <U> Optional<U> flatMap(Function<? super T, Optional<U>> mapper):如果值存在,就对该值执行提供的mapping函数调用,返回一个Optional类型的值,否则就返回一个空的Optional对象
public void test1() {
     Student student = new Student("李四", 3);
     Optional<Student> os1 = Optional.ofNullable(student);
     // 如果student对象不为空,就加一岁
     Optional<Student> emp = os1.map(e ->
     {
         e.setAge(e.getAge() + 1);
         return e;
     });
 }

什么场景用Optional

1、场景一

PatientInfo patientInfo = patientInfoDao.getPatientInfoById(consultOrder.getPatientId());
if (patientInfo != null) {
    consultInfoResp.setPatientHead(patientInfo.getHead());
}

// 使用Optional 和函数式编程,一行搞定,而且像说话一样
Optional.ofNullable(patientInfo).ifPresent(p -> consultInfoResp.setPatientHead(p.getHead()));

2、场景二

public void test1() throws Exception {
    Student student = new Student(null, 3);
    if (student == null || isEmpty(student.getName())) {
        throw new Exception();
    }
    String name = student.getName();
    // 业务省略...
}

public static boolean isEmpty(CharSequence str) {
    return str == null || str.length() == 0;
}
// 使用Optional改造
Optional.ofNullable(student).filter(s -> !isEmpty(s.getName())).orElseThrow(() -> new Exception());

3、场景三

public static String getChampionName(Competition comp) throws IllegalArgumentException {
    if (comp != null) {
        CompResult result = comp.getResult();
        if (result != null) {
            User champion = result.getChampion();
            if (champion != null) {
                return champion.getName();
            }
        }
    }
    throw new IllegalArgumentException("The value of param comp isn't available.");
}
public static String getChampionName(Competition comp) throws IllegalArgumentException {
    return Optional.ofNullable(comp)
            .map(Competition::getResult)  // 相当于c -> c.getResult(),下同
            .map(CompResult::getChampion)
            .map(User::getName)
            .orElseThrow(()->new IllegalArgumentException("The value of param comp isn't available."));
}

4、场景四

int timeout = Optional.ofNullable(redisProperties.getTimeout())
					  .map(x -> Long.valueOf(x.toMillis()).intValue())
					  .orElse(10000);
public class TestOptional {
    //用于解决空指针异常
    //Optional 容器类的常用方法
    /**Optional.of(T t) :创建一个optional实例
     * Optional.empty() : 创建一个空的Optional实例
     * Optional.ofNullable(T t):若t不为null,创建Optional实例,否则创建空实例
     * isPresent(T t) : 判断是否包含值
     * orElse(T t) : 如果调用对象包含值,返回该值,否则返回t
     * orElseGet(Supplier s) : 如果调用对象包含值,返回该值,否则返回s获取的值
     * map(Function f) : 如果有值对其处理,并返回处理后的Optional,否则返回Optional.empty()
     * flatMap(Function mapper) : 与map类似,要求返回值必须是Optional
     */
    @Test
    public void test5(){
        Optional<Person> optionalPerson = Optional.ofNullable(new Person("韩信",21, Status.FREE));
        Optional<String> s = optionalPerson.map(Person::getUserName);
        System.out.println(s.get());

        Optional<String> s1 = optionalPerson.flatMap((e) -> Optional.of(e.getUserName()));
        System.out.println(s1);
    }





    @Test
    public void test4(){
        Optional<Person> optionalPerson = Optional.ofNullable(new Person());
        if (optionalPerson.isPresent())
        System.out.println(optionalPerson.get());

        Person person = optionalPerson.orElseGet(() -> new Person());

        System.out.println(person);


    }


    @Test
    public void test3(){
        Optional<Person> optionalPerson = Optional.ofNullable(new Person());

        System.out.println(optionalPerson.get());
    }



    @Test
    public void test2(){
        Optional<Person> optionalPerson = Optional.empty();
        System.out.println(optionalPerson.get());
    }

    @Test
    public void test1(){
        Optional<Person> person = Optional.of(new Person());
        System.out.println(person.get());


    }
}

Date Time API

public class TestLocalDateTime {
    //DateTimeFormatter  : 格式化时间/日期
    @Test
    public void test6(){
        DateTimeFormatter isoDateTime = DateTimeFormatter.ISO_DATE;
        LocalDateTime ldt = LocalDateTime.now();
        String format = isoDateTime.format(ldt);
        System.out.println(format);

        System.out.println("---------------------------------");
        DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy年MM月dd日 HH:mm:ss");
        String format1 = dateTimeFormatter.format(ldt);
        System.out.println(format1);

        LocalDateTime parse = ldt.parse(format1,dateTimeFormatter);
        System.out.println(parse);


    }


   //TemporalAdjuster : 时间校正器
    @Test
    public void  test5(){
        LocalDateTime ldt = LocalDateTime.now();
        System.out.println(ldt);

        LocalDateTime ldt2 = ldt.withDayOfMonth(10);
        System.out.println(ldt2);

        LocalDateTime ldt3 = ldt.with(TemporalAdjusters.next(DayOfWeek.SUNDAY));
        System.out.println(ldt3);

        //自定义 : 下一个工作日
        ldt3.with((x) -> {
            LocalDateTime ldt4 = (LocalDateTime)x;
            DayOfWeek dow = ldt4.getDayOfWeek();
            if (dow.equals(DayOfWeek.FRIDAY)){
                return ldt4.plusDays(3);
            }else if (dow.equals(DayOfWeek.SATURDAY)){
                return ldt4.plusDays(2);
            }else {
                return ldt4.plusDays(1);
            }
        });


    }






    //3. Duration : 计算两个时间之间的间隔
    //Period : 计算两个日期之间的间隔

    @Test
    public void test4(){
        LocalDate ld1 = LocalDate.of(2020, 1, 1);
        LocalDate ld2 = LocalDate.now();
        Period period = Period.between(ld1,ld2);
        System.out.println(period);
        System.out.println("年:"+period.getYears()+"--月:"+period.getMonths()+"--日:"+period.getDays()+"--年代学:"+period.getChronology()+"--\n" +
                "\n" +
                "单位 :"+period.getUnits());
    }

    @Test
    public void test3(){
        //Duration : 计算两个时间之间的间隔
        Instant now = Instant.now();

        try {
            Thread.sleep(3000);
        } catch (InterruptedException e) {
            e.printStackTrace();
        }
        Instant now1 = Instant.now();
        Duration duration = Duration.between(now, now1);
        System.out.println(duration.toMillis());


    }

    //2. Instant :时间戳(Unix 元年:1970 年1月1日 00:00:00 到某个时间之间的毫秒值)
    @Test
    public void test2(){
        Instant now = Instant.now();
        System.out.println(Instant.now());
        
        OffsetDateTime offsetDateTime = Instant.now().atOffset(ZoneOffset.ofHours(8));
        System.out.println(offsetDateTime);
        
        System.out.println(now.toEpochMilli());

    }


    //1. LocalTime  LocalDate  LocalDateTime

    @Test
    public  void test1() {
        LocalDateTime now = LocalDateTime.now();
        System.out.println(now);
        LocalDateTime of = LocalDateTime.of(2020, 4, 17, 16, 40);
        System.out.println(of);
    }
}

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

打赏作者

李熠漾

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

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

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

打赏作者

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

抵扣说明:

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

余额充值