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..."));
}


相关文章
|
22天前
|
数据采集 监控 Oracle
GraalVM 24 正式发布阿里巴巴贡献重要特性 —— 支持 Java Agent 插桩
阿里巴巴是 GraalVM 全球顾问委员会的唯一中国代表,阿里云程序语言与编译器团队和可观测团队合作实现了 GraalVM 应用的无侵入可观测能力,并在 ARMS 平台上线了该功能。目前在 GraalVM 24 中发布的是支持 Java agent 的第一步,其余能力将在 GraalVM 的后续版本中陆续发布。
126 21
|
1月前
|
缓存 运维 Java
Java静态代码块深度剖析:机制、特性与最佳实践
在Java中,静态代码块(或称静态初始化块)是指类中定义的一个或多个`static { ... }`结构。其主要功能在于初始化类级别的数据,例如静态变量的初始化或执行仅需运行一次的初始化逻辑。
74 4
|
4月前
|
存储 Java 开发者
什么是java的Compact Strings特性,什么情况下使用
Java 9引入了紧凑字符串特性,优化了字符串的内存使用。它通过将字符串从UTF-16字符数组改为字节数组存储,根据内容选择更节省内存的编码方式,通常能节省10%至15%的内存。
|
4月前
|
Rust 安全 Java
Java Stream 使用指南
本文介绍了Java中Stream流的使用方法,包括如何创建Stream流、中间操作(如map、filter、sorted等)和终结操作(如collect、forEach等)。此外,还讲解了并行流的概念及其可能带来的线程安全问题,并给出了示例代码。
326 0
|
Java Unix 数据库管理
java定时框架:表达式设置
Quartz中时间表达式的设置-----corn表达式 (注:这是让我看比较明白的一个博文,但是抱歉,没有找到原作者,如有侵犯,请告知)   时间格式: ,   分别对应: 秒>分>小时>日>月>周>年,  举例: 1.
894 0
|
2月前
|
存储 监控 Java
【Java并发】【线程池】带你从0-1入门线程池
欢迎来到我的技术博客!我是一名热爱编程的开发者,梦想是编写高端CRUD应用。2025年我正在沉淀中,博客更新速度加快,期待与你一起成长。 线程池是一种复用线程资源的机制,通过预先创建一定数量的线程并管理其生命周期,避免频繁创建/销毁线程带来的性能开销。它解决了线程创建成本高、资源耗尽风险、响应速度慢和任务执行缺乏管理等问题。
207 60
【Java并发】【线程池】带你从0-1入门线程池
|
17天前
|
Java 中间件 调度
【源码】【Java并发】从InheritableThreadLocal和TTL源码的角度来看父子线程传递
本文涉及InheritableThreadLocal和TTL,从源码的角度,分别分析它们是怎么实现父子线程传递的。建议先了解ThreadLocal。
53 4
【源码】【Java并发】从InheritableThreadLocal和TTL源码的角度来看父子线程传递
|
1月前
|
存储 网络协议 安全
Java网络编程,多线程,IO流综合小项目一一ChatBoxes
**项目介绍**:本项目实现了一个基于TCP协议的C/S架构控制台聊天室,支持局域网内多客户端同时聊天。用户需注册并登录,用户名唯一,密码格式为字母开头加纯数字。登录后可实时聊天,服务端负责验证用户信息并转发消息。 **项目亮点**: - **C/S架构**:客户端与服务端通过TCP连接通信。 - **多线程**:采用多线程处理多个客户端的并发请求,确保实时交互。 - **IO流**:使用BufferedReader和BufferedWriter进行数据传输,确保高效稳定的通信。 - **线程安全**:通过同步代码块和锁机制保证共享数据的安全性。
96 23
|
24天前
|
数据采集 存储 网络协议
Java HttpClient 多线程爬虫优化方案
Java HttpClient 多线程爬虫优化方案
|
1月前
|
Java 调度
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
当我们创建一个`ThreadPoolExecutor`的时候,你是否会好奇🤔,它到底发生了什么?比如:我传的拒绝策略、线程工厂是啥时候被使用的? 核心线程数是个啥?最大线程数和它又有什么关系?线程池,它是怎么调度,我们传入的线程?...不要着急,小手手点上关注、点赞、收藏。主播马上从源码的角度带你们探索神秘线程池的世界...
155 0
【源码】【Java并发】【线程池】邀请您从0-1阅读ThreadPoolExecutor源码
下一篇
oss创建bucket