JavaSE | Lambda| Optional| Stream API
JDK1.8新特性
1、接口:默认方法、 静态方法
2、Lambda表达式和StreamAPI
3、Optional类
4、新的日期时间API
Lambda表达式:为了简化代码,使得Java支持
StreamAPI:为了支持内存中的数据的筛选、管理等
Optional类:为了避免,简化空指针的处理
新的日期时间API:为了解决原来老版的日期时间(1)对象的可变性(2)闰秒的考虑(3)本地化考虑
1. Lambda表达式
* Lambda表达式是实现SAM接口的语法糖,使得Java支持函数式编程。
*
* 原来Java要给Runnable类型的形参,一个对象:
* Thread(Runnable target),给target赋值一个对象,一个匿名内部类的对象
* new Runnable(){
@Override
public void run() {
System.out.println("hello");
}
}
*
* 现在使用Lambda表达式:
* Thread(Runnable target),给target赋值的是一段代码:
* () -> System.out.println("hello")
*
* 左边():其实是方法的形参列表
* 右边:System.out.println("hello")是方法体
*
* () -> System.out.println("hello")相当于是一个函数。
*
*
* SAM接口:函数式接口,Single Abstract Method,这个接口只有一个唯一的抽象方法。
* 例如:Runnable接口是一个函数式接口,它只有一个抽象方法:public void run();
*
* 换句话说:目前Java的Lambda表达式,只适合给SAM接口赋值。
* Lambda表达式是给SAM接口赋值的,因此SAM接口的抽象方法的方法签名就很重要了。
* 重点关注抽象方法的:形参列表,返回值类型
*
* Lambda表达式的语法:
* (形参列表) -> {Lambda体}
* 说明:
* (1)Lambda表达式的(形参列表)就是你给赋值的SAM接口的抽象方法的形参列表
* (2)->是称为Lambda操作符,中间不要加空格
* (3){Lambda体}就是你给赋值的SAM接口的抽象方法的方法体
* 方法的形式:
* 1、无参无返回值
* 2、有参无返回值
* 3、无参有返回值
* 4、有参有返回值
*
* 提示:
* (1)如果我们的(形参列表),只有一个形参,并且类型是已知的或可以推断的,那么可以省略(形参的类型),只要写形参名
* (2)如果{Lambda体}只有一个语句,那么可以省略{}和这句语句的;,并且如果这一句是return语句,这个return也可以省略
* (3)如果我们的(形参列表),无参的,或者是参数有多个的,那么()绝对不能省略
* (4)如果抽象方法有返回值,并且{}没有省略的话,那么return也不能省略
*/
函数式接口:SAM接口
* Single Abstract Method,即该接口中只有一个抽象方法需要实现,当然该接口可以包含其他非抽象方法。
*
* 原来JDK1.8之前就已经存在以下接口,符号这样的特征:
* (1)java.lang.Runnable:public void run();
* (2)java.lang.Comparable<T>:int compareTo(T t)
* (3)java.util.Comparator<T>:int compare(T t1, T t2)
* (4)java.io.FileFilter:public boolean accept(File pathname)
* (5)java.lang.reflect.InvocationHandler:public Object invoke(Object proxy, Method method, Object[] args)
* (6)java.lang.Iterable<T>: Iterator iterator()
* ....
*
* 函数式接口最好用一个注解标记一下:@FunctionalInterface
*
* JDK1.8之前的那些满足SAM特征的接口是否都加了呢?
* (1)java.lang.Runnable
* (3)java.util.Comparator<T>
* (4)java.io.FileFilter
* 如果没加@FunctionalInterface,表示将来可能扩展其他的抽象方法,变成非SAM接口。
* 结论:最好只对加了@FunctionalInterface的接口使用Lambda表达式。
*
* JDK1.8之后又增加了很多新的函数式接口。
JDK1.8增加的函数式接口:java.util.function
* 它设计的这些个接口,基本能保证大多数接口的需求,不需要重新设计新的接口。
* 分为四大类:
* 1、消费型接口:它的抽象方法的特征 有参无返回值
* 2、供给型接口:它的抽象方法的特征 无参有返回值
* 3、判断型接口:它的抽象方法的特征 有参有返回值,并且返回值类型是boolean
* 4、功能型接口:它的抽象方法的特征 有参有返回值
*
* 一、消费型接口:有参无返回值
* 1、Consumer<T> void accept(T t)
* 2、BiConsumer<T,U> void accept(T t,U u)
* 3、DoubleConsumer void accept(double value)
* 4、IntConsumer void accept(int value)
* 5、LongConsumer void accept(long value)
* 6、ObjDoubleConsumer<T> void accept(T t, double value)
* 6、ObjIntConsumer<T> void accept(T t, int value)
* 6、ObjLongConsumer<T> void accept(T t, long value)
* 二、供给型接口:无参有返回值
* 1、Supplier<T> T get()
* 2、BooleanSupplier boolean getAsBoolean()
* 3、DoubleSupplier double getAsDouble()
* 4、IntSupplier int getAsInt()
* 5、LongSupplier long getAsLong()
* 三、判断型接口:有参,但是返回值类型是boolean结果
* 1、Predicate<T> boolean test(T t)
* 2、BiPredicate<T,U> boolean test(T t,U u)
* 3、DoublePredicate boolean(double value)
* 4、IntPredicate boolean(int value)
* 5、LongPredicate boolean(long value)
* 四、功能型接口:有参有返回值
* 1、Function<T,R> R apply(T t)
* 2、UnaryOperator<T> T apply(T t) 与上一个的区别就是形参的类型与返回值的类型是一样
* 3、DoubleFunction<R> R apply(double value)
* 4、IntFunction<R> R apply(int value)
* 5、LongFunction<R> R apply(long value)
* 6、ToDoubleFunction<T> double apply(T t)
* 7、ToIntFunction<T> int apply(T t)
* 8、ToLongFunction<T> long apply(T t)
* 9、DoubleToIntFunction int int applyAsInt(double value)
* 10、DoubleToLongFunction......
* 11、IntToDoubleFunction......
* 12、IntToLongFunction......
* 13、LongToDoubleFunction......
* 14、LongToIntFunction ......
*
* 15、DoubleUnaryOperator double applyAsDouble(double value)
* 16、IntUnaryOperator...
* 17、LongUnaryOperator...
*
* 19、BiFunction<T,U,R> R apply(T t, U u)
* 20、BinaryOperator<T> T apply(T t1, T t2)
* 21、ToDoubleBiFunction<T,U> double apply(T t, U u)
* ....
* 方法引用和构造引用:
* 当Lambda表达式出现更特殊的情况时,我们可以对Lambda表达式进行再次简化。
* (1)当{Lambda体}的实现是通过调用一个类或一个对象的现有的方法来完成功能时。
* (2)你这个Lambda表达式所赋值的SAM接口的抽象方法的形参列表与返回值类型与实现{Lambda体}所调用的方法的形参列表与返回值类型对应。
*
* 方法引用的语法:
* (1)对象名::实例方法名
* (2)类名::静态方法名
* (3)类名::实例方法名
* 构造引用:
* (4)构造器名::new
* (5)数组类型::new
*/
2. Optional类
* 设计Optional类的初衷:因为Java的对象可能为null值,当用null值去调用方法,属性等时,会报NullPointerExecption空指针异常。
* 程序中为了避免这个空指针异常,会需要加入大量的非空判断,这样会导致程序中有很多重复的非空判断。
*
* Optional是一个容器。
* 数组和集合是用来装一堆(大量)对象的容器,而Optional是用来包装一个对象的容器。
*
* java.util.Optional<T>,这个T就是所包装的对象的类型。
* 1、如何包装对象?
* (1)static <T> Optional<T> empty() :包装一个空对象
* (2)static <T> Optional<T> of(T value) :只能用来包装“非空”对象
* (3)static <T> Optional<T> ofNullable(T value) :包装一个对象,这个对象可以是null,也可以非空
*
* 2、如何拿出这个被包装的对象
* (1)T get()
* 可以取出Optional容器中的对象,但是如果容器中的对象是null,那么会报java.util.NoSuchElementException: No value present
* (2)T orElse(T other)
* 如果Optional容器中的对象是非空,就返回该对象,如果是空的,就用other来代替。
* (3)T orElseGet(Supplier<? extends T> other)
* 如果Optional容器中的对象是非空,就返回该对象,如果是空的,就用由Supplier这个供给型接口提供的对象来代替
* (4) T orElseThrow(Supplier<X> exceptionSupplier)
* 如果Optional容器中的对象是非空,就返回该对象,如果是空的,就抛出你指定的异常对象
*
* 3、对所包装的对象,进行判断或处理
* (1)Optional<T> filter(Predicate<? super T> predicate) :
* 如果Optional容器中的对象满足你指定的条件,就保留,否则就清空。
* (2)<U> Optional<U> map(Function<? super T,? extends U> mapper)
* 如果Optional容器中的对象非空,可以对该对象进行处理,如何处理,由Function接口的lambda体来决定。
3. StreamAPI
* 原来的数据都在数据库中,现在很多数据都在内存中,例如:在集合、数组等容器中。
* 我们要对内存中的数据进行筛选、查找等各种操作,那么我们就可以使用StreamAPI,
* 像我们用SQL语句对象数据库一样操作。
*
* Stream是一个数据的渠道,专门对数据进行加工处理的渠道,不是数据本身。
* Stream的特点:
* (1)不负责存储数据,存储数据仍然是集合、数组等容器。
* (2)Stream对数据的加工,不会影响数据源(集合、数组等容器中的数据),而是产生一个新的副本。
* 每一次对Stream的操作都会产生一个新的Stream对象。
* (3)Stream的加工操作是一个延迟操作,只有在最后拿结果时,才一口气执行完,
* 创建Stream->(1)加工1(2)加工2....->取结果
*
Stream的操作分为三步:
(1)创建一个Stream
(2)中间操作:可以很多步
(3)终结操作:拿结果
一旦结束,这个Stream结束了,如果要重新加工,要重新创建。
一、如何创建Stream
* 1、通过集合来创建Stream
* Collection系列的集合.stream()
*
* 2、通过数组来创建Stream
* Arrays.stream(数组)
*
* 3、Stream.of(...)
*
* 4、无限流
*
* static <T> Stream<T> generate(Supplier<T> s)
* static <T> Stream<T> iterate(T seed, UnaryOperator<T> f)
*/
@Test
public void test1(){
ArrayList<String> list = new ArrayList<>();
list.add("Hello");
list.add("world");
String[] arr = {"hello","java","world"};
Stream<String> stream = list.stream();
Stream<String> stream2 = Arrays.stream(arr);
Stream.of("Hi","kk");
}
@Test
public void test2(){
Stream<Integer> ite = Stream.iterate(1, t -> t + 2); //T seed :UnaryOperator<T> 是一个SAM接口,是一个功能型接口
//抽象方法:T apply(T t)
ite.forEach((t) -> System.out.println(t));
//ite.forEach(System.out::println);
}
中间操作----每次中间操作都会返回一个新的stream,要重新接收
@Test public void test1(){ //(1)filter(Predicate p):按照p指定的条件进行过滤 Stream<Integer> stre = Stream.of(1, 2, 3, 4, 5, 6); Stream<Integer> fil = stre.filter((t) -> t%2 == 0); fil.forEach(System.out::println); //(2)distinct():去重 Stream.of(1,3,6,2,6,6,8,9) .distinct() //去除重复的元素 .forEach(System.out::println); //(3)limit(long maxSize):取出前maxSize个 Stream.of(1,3,6,2,6,6,8,9) .limit(3) //取前3个 .forEach(System.out::println); //(4)skip(long n):跳过前n个 Stream.of(1,3,6,2,6,6,8,9) .skip(4) //把前4个跳过去 .forEach(System.out::println); //(5)peek(Consumer action) long count = Stream.of(1,3,6,2,6,6,8,9) .peek((t) -> System.out.println(t)) //.forEach(System.out::println); //不加.count输出是每个元素都重复了2遍; .count(); System.out.println("count=" + count); //8 //(6)sorted() 要求元素实现java.lang.Comparable Stream.of(7,3,6,2,4,6,8,9) //创建Stream .sorted() //从小到大排序 .forEach(t -> System.out.println(t)); //终结操作 //(7)sorted(Comparator com)可以指定定制比较器 ArrayList<Employee> list = new ArrayList<>(); list.add(new Employee(2, "张三", 100000)); list.add(new Employee(1, "李四", 8000)); list.add(new Employee(3, "王五", 9000)); //list.stream().sorted((t1, t2) -> Double.compare(t1.getSalary(), t2.getSalary())); list.stream().sorted((t1, t2) -> Double.compare(t1.getSalary(), t2.getSalary())) .forEach(System.out::println); //(8)map(Function f):给stream中的每一个数据进行xx操作,结果仍然放回stream中,最终生成一个新的流 list.stream() //设置值!!!! .map(t -> {t.setSalary(t.getSalary() + 100); return t;}) //中间操作 Function<T,R>: R apply(T t) .forEach(System.out::println); //(9)mapToDouble(ToDoubleFunction f) OptionalDouble reduce = list.stream() .mapToDouble((t) -> t.getSalary()) .reduce((t1, t2) -> t1 + t2); System.out.println(reduce.getAsDouble()); //117300.0 /*(10)flatMap(Function<? super T,? extends Stream<? extends R>> mapper) * Function<T,R>的抽象方法 R apply(T t),这的R是一个Stream * 对stream中的每一个数据进行某个操作,会得到一个stream,然后把这些个stream再合成一个大的stream */ Stream.of("hello","java","wolrd") .flatMap(t -> Stream.of(t.split("|"))) .forEach(System.out::println); }
Stream终结操作
* Stream:
* (1)创建:必须有
* (2)中间操作:0~n步
* (3)终结操作:必须有
*
* 三、Stream的终结操作
* 1、void forEach(Consumer c):遍历stream中的数据
* 2、long count():统计流中的数据的个数
* 3、boolean allMatch(Predicate p):判断流中的数据是否都满足p的条件
* boolean anyMatch(Predicate p):判断流中的数据是否至少有一个满足p的条件
* boolean noneMatch(Predicate p):判断流中的数据是否都不满足p的条件
* 4、Optional<T> findFirst() :返回流中的第一个
* Optional<T> findAny() :返回流中的任意一个,如果流是固定的,那么相当于findFirst()
*
* 5、Optional<T> max(Comparator c)
* Optional<T> min(Comparator c)
*
* 6、U reduce(BinaryOperator b) :把流中的数据,返回结合,得到一个值
* 7、R collect(Collector c):把流中的数据收集起来放到一个容器中
*
* 工具类:Collectors
*
*
* 区别:
* Collection和Collector
* Collections(Collection集合的工具类)和Collectors
@Test public void test1(){ Random random = new Random(); long count = Stream.generate(() -> random.nextInt(100)) .limit(10) .peek(System.out::println) .filter(t -> t%2 == 0) .count(); System.out.println("偶数的个数:" + count); } @Test public void test2(){ boolean flag = Stream.of(1, 2, 3, 4) .allMatch(num -> num < 4); System.out.println(flag); //false } @Test public void test3(){ ArrayList<Employee> list = new ArrayList<>(); list.add(new Employee(2, "张三", 100000)); list.add(new Employee(1, "李四", 8000)); list.add(new Employee(3, "王五", 9000)); Optional<Employee> max = list.stream() .max((t1, t2) -> Double.compare(t1.getSalary(), t2.getSalary())); System.out.println(max.get().getSalary()); Optional<Integer> sum = Stream.of(1,2,3,4) .reduce((t1, t2) -> t1+t2); System.out.println(sum); } @Test public void test4(){ ArrayList<Employee> list = new ArrayList<>(); list.add(new Employee(2, "张三", 100000)); list.add(new Employee(1, "李四", 8000)); list.add(new Employee(3, "王五", 9000)); list.add(new Employee(4, "小路", 7000)); list.add(new Employee(5, "小周", 8800)); //找出所有薪资低于10000的员工,放到一个集合中 List<Employee> collect = list.stream() .filter(emp -> emp.getSalary() < 10000) .collect(Collectors.toList()); for (Employee employee : collect) { System.out.println(employee); } }