java8新特性 lambda表达式、Stream、Optional

简介: java8新特性 lambda表达式、Stream、Optional

一、函数式接口

1、定义

2、使用场景

2.1、函数式接口作为方法参数

forEach(Consumer<? super E> action)

2.2、函数式接口作为方法返回值

public class ComparatorDemo {
    public static void main(String[] args) {
        ArrayList<String> array = new ArrayList<>();
        array.add("a");
        array.add("ccccc");
        array.add("bb");
        array.add("ddd");
        System.out.println("排序前" + array);
 
        Collections.sort(array, getComparator());
 
        System.out.println("排序后" + array);
 
    }
 
    private static Comparator<String> getComparator() {
        /*  使用匿名内部类
        Comparator<String> comp = new Comparator<String>() {
            @Override
            public int compare(String s1, String s2) {
                return s1.length()-s2.length();
            }
        };
        return comp;
         */
    }
}

2.3、函数式接口作为匿名内部类

Runnable r = new Runnable(){
            @Override
            public void run() {
                System.out.println("My Runnable");
            }};

3、常用函数式接口

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

1 BiConsumer<T,U>

代表了一个接受两个输入参数的操作,并且不返回任何结果

2 BiFunction<T,U,R>

代表了一个接受两个输入参数的方法,并且返回一个结果

3 BinaryOperator

代表了一个作用于于两个同类型操作符的操作,并且返回了操作符同类型的结果

4 BiPredicate<T,U>

代表了一个两个参数的boolean值方法

5 BooleanSupplier

代表了boolean值结果的提供方

6 Consumer

代表了接受一个输入参数并且无返回的操作

7 DoubleBinaryOperator

代表了作用于两个double值操作符的操作,并且返回了一个double值的结果。

8 DoubleConsumer

代表一个接受double值参数的操作,并且不返回结果。

9 DoubleFunction

代表接受一个double值参数的方法,并且返回结果

10 DoublePredicate

代表一个拥有double值参数的boolean值方法

11 DoubleSupplier

代表一个double值结构的提供方

12 DoubleToIntFunction

接受一个double类型输入,返回一个int类型结果。

13 DoubleToLongFunction

接受一个double类型输入,返回一个long类型结果

14 DoubleUnaryOperator

接受一个参数同为类型double,返回值类型也为double 。

15 Function<T,R>

接受一个输入参数,返回一个结果。

16 IntBinaryOperator

接受两个参数同为类型int,返回值类型也为int 。

17 IntConsumer

接受一个int类型的输入参数,无返回值 。

18 IntFunction

接受一个int类型输入参数,返回一个结果 。

19 IntPredicate

:接受一个int输入参数,返回一个布尔值的结果。

20 IntSupplier

无参数,返回一个int类型结果。

21 IntToDoubleFunction

接受一个int类型输入,返回一个double类型结果 。

22 IntToLongFunction

接受一个int类型输入,返回一个long类型结果。

23 IntUnaryOperator

接受一个参数同为类型int,返回值类型也为int 。

24 LongBinaryOperator

接受两个参数同为类型long,返回值类型也为long。

25 LongConsumer

接受一个long类型的输入参数,无返回值。

26 LongFunction

接受一个long类型输入参数,返回一个结果。

27 LongPredicate

R接受一个long输入参数,返回一个布尔值类型结果。

28 LongSupplier

无参数,返回一个结果long类型的值。

29 LongToDoubleFunction

接受一个long类型输入,返回一个double类型结果。

30 LongToIntFunction

接受一个long类型输入,返回一个int类型结果。

31 LongUnaryOperator

接受一个参数同为类型long,返回值类型也为long。

32 ObjDoubleConsumer

接受一个object类型和一个double类型的输入参数,无返回值。

33 ObjIntConsumer

接受一个object类型和一个int类型的输入参数,无返回值。

34 ObjLongConsumer

接受一个object类型和一个long类型的输入参数,无返回值。

35 Predicate

接受一个输入参数,返回一个布尔值结果。

36 Supplier

无参数,返回一个结果。

37 ToDoubleBiFunction<T,U>

接受两个输入参数,返回一个double类型结果

38 ToDoubleFunction

接受一个输入参数,返回一个double类型结果

39 ToIntBiFunction<T,U>

接受两个输入参数,返回一个int类型结果。

40 ToIntFunction

接受一个输入参数,返回一个int类型结果。

41 ToLongBiFunction<T,U>

接受两个输入参数,返回一个long类型结果。

42 ToLongFunction

接受一个输入参数,返回一个long类型结果。

43 UnaryOperator

接受一个参数为类型T,返回值类型也为T。

二、lambda表达式

1、lambda定义

Lambda表达式需要函数式接口的支持,格式:()→ {}

常见格式

格式1:无参无返回值
//使用Lambda替代runnable接口中无参无返回的run方法
@Test
public void demoOne(){
Runnable r =()->System.out.println("==========");
r.run();
}
格式2:一参无返回值
//使用Lambda替代Consumer接口中有一个参数无返回值的accept方法
@Test
public void demoOne() {
Consumer<String> c = (e) -> System.out.println(e);
c.accept("============");
}
当然对已一个参数的情况,箭头操作符右边的括号可以省略,等价的代码
@Test
public void demoOne() {
Consumer<String> c = e -> System.out.println(e);
c.accept("============");
}
格式3:多个参数有返回值
//使用Lambda替代比较器接口有两个参数并且返回值的
@Test
public void demoOne() {
Comparator<Integer> comparator = (x,y)->{
return Integer.compare(x, y);
};
}
当然当Lambda体只有一条语句,可以省略右边的大括号以及return,等级代码
@Test
public void demoOne() {
Comparator<Integer> comparator = (x, y) -> Integer.compare(x, y);
}
格式4:多个参数有返回值,并且Lambda体多条语句
@Test
public void demoOne() {
Comparator<Integer> comparator = (x, y) -> {
System.out.println("=============");
return Integer.compare(x, y);
};
}
格式5:参数列表的数据类型可写可不写。Lambda会通过上下文进行类型推断,建议没必要写
@Test
public void demoOne() {
Comparator<Integer> comparator = (Integer x, Integer y) -> {
System.out.println("=============");
return Integer.compare(x, y);
};
}

2、lambda改写函数式接口三种使用场景

2.1、作为参数传递 Lambda 表达式

foreach语法就是函数式接口作为参数

2.2、函数式接口作为方法返回值

public class ComparatorDemo {
    public static void main(String[] args) {
        ArrayList<String> array = new ArrayList<>();
        array.add("a");
        array.add("ccccc");
        array.add("bb");
        array.add("ddd");
        System.out.println("排序前" + array);
 
        Collections.sort(array, getComparator());
 
        System.out.println("排序后" + array);
 
    }
 
    private static Comparator<String> getComparator() {
        /*  使用匿名内部类
        Comparator<String> comp = new Comparator<String>() {
            @Override
            public int compare(String s1, String s2) {
                return s1.length()-s2.length();
            }
        };
        return comp;
         */
 
        //使用lambda表达式
        return (s1, s2) -> s1.length() - s2.length();
    }
}

2.3、函数式接口作为匿名内部类

Runnable r = ()->{System.out.println("My Runnable")};

3、Java中双冒号(::)运算操作符

1、定义

双冒号运算操作符是类方法的句柄,lambda表达式的一种简写,这种简写的学名叫eta-conversion或者叫η-conversion。

英文格式双冒号::,读:double colon,双冒号(::)运算符在Java 8中被用作方法引用(method reference),方法引用是与lambda表达式相关的一个重要特性。它提供了一种执行方法的方法,为此,方法引用需要由兼容的函数式接口组成的目标类型上下文。

使用lambda表达式会创建匿名函数, 但有时候需要使用一个lambda表达式只调用一个已经存在的方法(不做其它), 所以这才有了方法引用!

2、使用场景

2.1、引用静态方法

 

import org.junit.Test;
import java.util.Arrays;
import java.util.List;
 
public class Colon{
    @Test
    public void test(){
        List<String> list = Arrays.asList("a","b","c");
        //静态方法引用  ClassName::methodName
        list.forEach(Colon::print);
        //上一行等价于
        //list.forEach((x)->Colon.print(x));
    }
    //静态方法
    public static void print(String s){
        System.out.println(s);
    }
}

2.2、引用特定对象实例方法

import org.junit.Test;
import java.util.Arrays;
import java.util.List;
 
public class Colon{
    @Test
    public void test(){
        List<String> list = Arrays.asList("a","b","c");
        //实例方法引用  instanceRef::methodName
        list.forEach(new Colon()::print);
        //上一行等价于
        //list.forEach((x)->new Colon().print(x));
    }
    //实例方法
    public void print(String s){
        System.out.println(s);
    }
}

2.3、引用特定类型的任意对象的实例方法

import org.junit.Test;
import java.util.Arrays;
 
public class Colon{
    @Test
    public void test(){
        String[] arr = { "Barbara", "James", "Mary", "John",
                "Patricia", "Robert", "Michael", "Linda" };
        //引用String类型的任意对象的compareToIgnoreCase方法实现忽略大小写排序
        Arrays.sort(arr, String::compareToIgnoreCase);
        //上一行等价于
        //Arrays.sort(arr, (a,b)->a.compareToIgnoreCase(b));
        //输出
        for(String s:arr){
            System.out.println(s);
        }
    }
}

2.4、引用超类(父类)实例方法

import org.junit.Test;
import java.util.Arrays;
import java.util.List;
 
public class Colon extends BaseColon{
    @Test
    public void test(){
        List<String> list = Arrays.asList("a","b","c");
        //实例方法引用  instanceRef::methodName
        list.forEach(super::print);
    }
}
class BaseColon{
    //实例方法
    public void print(String s){
        System.out.println(s);
    }
}

2.5、引用类构造方法

一般我们需要声明接口,该接口作为对象的生成器,通过 类名::new 的方式来实例化对象,然后调用方法返回对象。

//注意:该类无需实现接口
public class Colon{
    private String name;
    private int age;
    //无参构造
    public Colon() {
    }
    //有参构造
    public Colon(String name, int age) {
        this.name = name;
        this.age = age;
    }
 
    public static void main(String[] args) {
        //无参构造引用
        ColonNoParam cnp = Colon::new;
        Colon c1 = cnp.createColon();
        System.out.println(c1);
        //有参构造引用
        ColonWithParam cwp = Colon::new;
        Colon c2 = cwp.createColon("小明",20);
        System.out.println(c2);
    }
 
    //生成toString方法打印查看
    @Override
    public String toString() {
        return "Colon{" +
                "name='" + name + ''' +
                ", age=" + age +
                '}';
    }
}
interface ColonNoParam{
    //无参方法提供对象
    Colon createColon();
}
interface ColonWithParam{
    //有参方法提供对象(数据类型要与Colon有参构造函数对应)
    Colon createColon(String s,int i);
}

2.6、引用数组构造方法

我们可以借助jdk自带的java.util.function.Function类实现对数组构造函数的引用。

import java.util.function.Function;
 
public class Colon{
 
    public static void main(String[] args) {
        Function<Integer,Colon[]> function = Colon[]::new;
        //调用apply方法创建数组,这里的5是数组的长度
        Colon[] arr = function.apply(5);
        //循环输出-初始都为null
        for(Colon c:arr){
            System.out.println(c);
        }
    }
}

三、Stream

1、定义

Java 8的另一大亮点Stream,它与 java.io 包里的 InputStream 和 OutputStream 是完全不同的概念。

Java 8 中的 Stream 是对集合(Collection)对象功能的增强,它专注于对集合对象进行各种非常便利、高效的聚合操作(aggregate operation),或者大批量数据操作 (bulk data operation)。

Stream API 借助于同样新出现的 Lambda 表达式,极大的提高编程效率和程序可读性。同时它提供串行和并行两种模式进行汇聚操作,并发模式能够充分利用多核处理器的优势,使用 fork/join 并行方式来拆分任务和加速处理过程。

 

集合的流式操作是java8中的一个新特性 流式操作不是一个数据结构也不负责存储任何数据,类似一个迭代器,可以有序的获取数据源中的每一个数据,并且可以对这些数据进行一些操作(排序 条件过滤)流式操作的每一个返回值返回的流本身,对集合执行流式

2、Stream操作分类

中间操作

无状态(Stateless)操作:指元素的处理不受之前元素的影响

有状态(Stateful)操作:指该操作只有拿到所有元素之后才能继续下去

终结操作

短路(Short-circuiting)操作:指遇到某些符合条件的元素就可以得到最终结果

非短路(Unshort-circuiting)操作:指必须处理完所有元素才能得到最终结果

3、流式操作

3.1、创建流

stream是顺序流,由主线程按顺序对流执行操作,而parallelStream是并行流,内部以多线程并行执行的方式对流进行操作,需要注意使用并行流的前提是流中的数据处理没有顺序要求(会乱序,即使用了forEachOrdered)。例如筛选集合中的奇数,两者的处理不同之处:

当然,除了直接创建并行流,还可以通过parallel()把顺序流转换成并行流:

Optional<Integer> findFirst = list.stream().parallel().filter(x->x>4).findFirst();

 

3.2、中间操作

【Java 8系列】Stream详解,看这一篇就够啦_善良勤劳勇敢而又聪明的老杨的博客-CSDN博客_java stream

3.3、终结操作

【Java 8系列】Stream详解,看这一篇就够啦_善良勤劳勇敢而又聪明的老杨的博客-CSDN博客_java stream

四、Optional 类

public static void main(String[] args) throws Exception{
    User user = null;
 
    //1、可能为null的对象转成Optional对象
    Optional<User> optionalUser = Optional.ofNullable(user);
 
    //2、Optional链式操作
    Optional<String> s = optionalUser.map(User::getUsername).filter("test"::equals);
    optionalUser.map(User::getUsername).ifPresent(System.out::println);
 
    //3、Optional短路
    //从数据库查询,有返回,没有新建一个
    User user1 = optionalUser.orElse(new User());//错误 optionalUser为空也会执行new user()
    User user2 = optionalUser.orElseGet(User::new);//optionalUser才会执行User:new
 
    //4、Optional抛出异常
    optionalUser.orElseThrow(()-> new RuntimeException("error..."));
}


相关文章
|
8月前
|
Java API 数据处理
Java新特性:使用Stream API重构你的数据处理
Java新特性:使用Stream API重构你的数据处理
Java API 开发者
295 0
|
9月前
|
Java 编译器 API
Java Lambda表达式与函数式编程入门
Lambda表达式是Java 8引入的重要特性,简化了函数式编程的实现方式。它通过简洁的语法替代传统的匿名内部类,使代码更清晰、易读。本文深入讲解Lambda表达式的基本语法、函数式接口、方法引用等核心概念,并结合集合操作、线程处理、事件回调等实战案例,帮助开发者掌握现代Java编程技巧。同时,还解析了面试中高频出现的相关问题,助你深入理解其原理与应用场景。
|
9月前
|
安全 Java API
Java中的Lambda表达式:简洁与功能的结合
Java中的Lambda表达式:简洁与功能的结合
549 211
|
Java API 开发者
Java中的Lambda表达式与Stream API的协同作用
在本文中,我们将探讨Java 8引入的Lambda表达式和Stream API如何改变我们处理集合和数组的方式。Lambda表达式提供了一种简洁的方法来表达代码块,而Stream API则允许我们对数据流进行高级操作,如过滤、映射和归约。通过结合使用这两种技术,我们可以以声明式的方式编写更简洁、更易于理解和维护的代码。本文将介绍Lambda表达式和Stream API的基本概念,并通过示例展示它们在实际项目中的应用。
|
安全 Java API
Java中的Lambda表达式与Stream API的高效结合####
探索Java编程中Lambda表达式与Stream API如何携手并进,提升数据处理效率,实现代码简洁性与功能性的双重飞跃。 ####
236 0
|
Java API 数据处理
探索Java中的Lambda表达式与Stream API
【10月更文挑战第22天】 在Java编程中,Lambda表达式和Stream API是两个强大的功能,它们极大地简化了代码的编写和提高了开发效率。本文将深入探讨这两个概念的基本用法、优势以及在实际项目中的应用案例,帮助读者更好地理解和运用这些现代Java特性。
|
Java 程序员 API
Java 8新特性之Lambda表达式与Stream API的探索
【9月更文挑战第24天】本文将深入浅出地介绍Java 8中的重要新特性——Lambda表达式和Stream API,通过实例解析其语法、用法及背后的设计哲学。我们将一探究竟,看看这些新特性如何让Java代码变得更加简洁、易读且富有表现力,同时提升程序的性能和开发效率。
|
Java API
Java 8新特性:Lambda表达式与Stream API的深度解析
【7月更文挑战第61天】本文将深入探讨Java 8中的两个重要特性:Lambda表达式和Stream API。我们将首先介绍Lambda表达式的基本概念和语法,然后详细解析Stream API的使用和优势。最后,我们将通过实例代码演示如何结合使用Lambda表达式和Stream API,以提高Java编程的效率和可读性。