都2023年还不知道Java8如何优雅简化代码就落后了

news2024/12/26 21:55:41

1、使用 Stream 简化集合操作

Java8 Stream流操作总结_出世&入世的博客-CSDN博客

2、使用 Optional 简化判空逻辑

空指针异常(NullPointerExceptions)是 Java 最常见的异常之一,一直以来都困扰着 Java 程序员。一方面,程序员不得不在代码中写很多null的检查逻辑,让代码看起来非常臃肿;另一方面,由于其属于运行时异常,是非常难以预判的。

为了预防空指针异常,GoogleGuava项目率先引入了Optional类,通过使用检查空值的方式来防止代码污染,受到Guava项目的启发,随后在Java 8中也引入了Optional类。

Optional 类位于java.util包下,是一个可以为 null容器对象,如果值存在则isPresent()方法会返回 true ,调用 get() 方法会返回该对象,可以有效避免空指针异常。

2.1、创建Optional对象

empty()、of()、ofNullable()

//可以使用静态方法 empty() 创建一个空的 Optional 对象
Optional<String> empty = Optional.empty();
System.out.println(empty); // 输出:Optional.empty

//可以使用静态方法 of() 创建一个非空的 Optional 对象
Optional<String> opt = Optional.of("沉默王二");
System.out.println(opt); // 输出:Optional[沉默王二]

//当然了,传递给 of() 方法的参数必须是非空的,也就是说不能为 null,否则仍然会抛出 NullPointerException。
String name = null;
Optional<String> optnull = Optional.of(name);

//可以使用静态方法 ofNullable() 创建一个即可空又可非空的 Optional 对象
String name = null;
Optional<String> optOrNull = Optional.ofNullable(name);
System.out.println(optOrNull); // 输出:Optional.empty

2.2、判断值是否存在

isPresent(); isEmpty()

//可以通过方法 isPresent() 判断一个 Optional 对象是否存在,如果存在,该方法返回 true,否则返回 false——取代了 obj != null 的判断。
Optional<String> opt = Optional.of("沉默王二");
System.out.println(opt.isPresent()); // 输出:true

Optional<String> optOrNull = Optional.ofNullable(null);
System.out.println(opt.isPresent()); // 输出:false


//Java 11 后还可以通过方法 isEmpty() 判断与 isPresent() 相反的结果。

Optional<String> opt = Optional.of("沉默王二");
System.out.println(opt.isPresent()); // 输出:false

Optional<String> optOrNull = Optional.ofNullable(null);
System.out.println(opt.isPresent()); // 输出:true

2.3、非空表达式

ifPresent(),允许我们使用函数式编程的方式执行一些代码,因此,我把它称为非空表达式。如果没有该方法的话,我们通常需要先通过 isPresent() 方法对 Optional 对象进行判空后再执行相应的代码:

Optional<String> optOrNull = Optional.ofNullable(null);
if (optOrNull.isPresent()) {
    System.out.println(optOrNull.get().length());
}

有了 ifPresent() 之后,情况就完全不同了,可以直接将 Lambda 表达式传递给该方法,代码更加简洁,更加直观。

Optional<String> opt = Optional.of("沉默王二");
opt.ifPresent(str -> System.out.println(str.length()));

//Java 9 后还可以通过方法 ifPresentOrElse(action, emptyAction) 执行两种结果,非空时执行 action,空时执行 emptyAction。

Optional<String> opt = Optional.of("沉默王二");
opt.ifPresentOrElse(str -> System.out.println(str.length()), () -> System.out.println("为空"));

2.4、设置(获取)默认值

有时候,我们在创建(获取) Optional 对象的时候,需要一个默认值,orElse() 和 orElseGet() 方法就派上用场了。

orElse() 方法用于返回包裹在 Optional 对象中的值,如果该值不为 null,则返回;否则返回默认值。该方法的参数类型和值得类型一致。

String nullName = null;
String name = Optional.ofNullable(nullName).orElse("沉默王二");
System.out.println(name); // 输出:沉默王二

orElseGet() 方法与 orElse() 方法类似,但参数类型不同。如果 Optional 对象中的值为 null,则执行参数中的函数。

String nullName = null;
String name = Optional.ofNullable(nullName).orElseGet(()->"沉默王二");
System.out.println(name); // 输出:沉默王二

orElseGet性能更好一点,因为无论Optional 对象中的值是不是为空,orElse都会执行,只是不返回而已。但是通常这里的逻辑不会很复杂,所以影响不大。

2.5、获取值

直观从语义上来看,get() 方法才是最正宗的获取 Optional 对象值的方法,但很遗憾,该方法是有缺陷的,因为假如 Optional 对象的值为 null,该方法会抛出 NoSuchElementException 异常。这完全与我们使用 Optional 类的初衷相悖。

public class GetOptionalDemo {
    public static void main(String[] args) {
        String name = null;
        Optional<String> optOrNull = Optional.ofNullable(name);
        System.out.println(optOrNull.get());
    }
}

建议 orElseGet() 方法获取 Optional 对象的值。

2.6、filter 、map

  • filter() 方法的参数类型为 Predicate(Java 8 新增的一个函数式接口),也就是说可以将一个 Lambda 表达式传递给该方法作为条件,如果表达式的结果为 false,则返回一个 EMPTY 的 Optional 对象,否则返回过滤后的 Optional 对象。

  • map() 方法,该方法可以按照一定的规则将原有 Optional 对象转换为一个新的 Optional 对象,原有的 Optional 对象不会更改。

public class OptionalMapFilterDemo {
    public static void main(String[] args) {
        String pass word = "pass word";//测试时pass和word合在一起
        Optional<String>  opt = Optional.ofNullable(pass word);//测试时pass和word合在一起

        Predicate<String> len6 = pwd -> pwd.length() > 6;
        Predicate<String> len10 = pwd -> pwd.length() < 10;
        Predicate<String> eq = pwd -> pwd.equals("pass word");//测试时pass和word合在一起
    
        boolean result = opt.map(String::toLowerCase).filter(len6.and(len10 ).and(eq)).isPresent();
        System.out.println(result);
    }
}

总结:使用 Optional,不仅可以避免使用 Stream 进行级联调用的空指针问题;更重要的是,它提供了一些实用的方法帮我们避免判空逻辑。

如下是一些例子,演示了如何使用 Optional 来避免空指针,以及如何使用它的 fluent API 简化冗长的 if-else 判空逻辑

@Test(expected = IllegalArgumentException.class)
public void optional() { 
    //通过get方法获取Optional中的实际值,很少用到
    assertThat(Optional.of(1).get(), is(1));
    //通过ofNullable来初始化一个null,通过orElse方法实现Optional中无数据的时候返回一个默认值         
    assertThat(Optional.ofNullable(null).orElse("A"), is("A"));    
    //OptionalDouble是基本类型double的Optional对象,isPresent判断有无数据
    assertFalse(OptionalDouble.empty().isPresent());
    //通过map方法可以对Optional对象进行级联转换,不会出现空指针,转换后还是一个Optional
    assertThat(Optional.of(1).map(Math::incrementExact).get(), is(2));
    //通过filter实现Optional中数据的过滤,得到一个Optional,然后级联使用orElse提供默认值
    assertThat(Optional.of(1).filter(integer -> integer % 2 == 0).orElse(null), is(nullValue()));
    //通过orElseThrow实现无数据时抛出异常
    Optional.empty().orElseThrow(IllegalArgumentException::new);
}

3、Map接口 Map接口 putIfAbsent / computeIfAbsent/computeIfPresent

3.1、putIfAbsent

如果给定的key不存在(或者key对应的value为null),关联给定的key和给定的value,并返回null;如果存在,返回当前值(不会把value放进去);

使用场景:如果我们要变更某个key的值,我们又不知道key是否存在的情况下,而又不希望增加key的情况使用。

demo:

// 创建一个 HashMap
HashMap<Integer, String> sites = new HashMap<>();

// 往 HashMap 添加一些元素
sites.put(1, "Google");
sites.put(2, "Runoob");
sites.put(3, "Taobao");
System.out.println("sites HashMap: " + sites);

// HashMap 不存在该key
sites.putIfAbsent(4, "Weibo");

// HashMap 中存在 Key
sites.putIfAbsent(2, "Wiki");
System.out.println("Updated Languages: " + sites);

//结果为
sites HashMap: {1=Google, 2=Runoob, 3=Taobao}
Updated sites HashMap: {1=Google, 2=Runoob, 3=Taobao, 4=Weibo}

3.2、computeIfAbsent

如果给定的 key 不存在(或者 key 对应的 value 为 null),就去计算 mappingFunction 的值;

如果 mappingFunction 的值不为 null,就把 key = value 放进去;
如果 mappingFunction 的值为 null,就不会记录该映射关系,返回值为 null;
如果计算 mappingFunction 的值的过程出现异常,再次抛出异常,不记录映射关系,返回 null;

如果存在该 key,返回的是新的 value,就算 key 对应的 value 部位 null,返回 null;(HashMap 返回的是旧 value)

default V computeIfAbsent(K key, Function<? super K,? extends V> mappingFunction)
map.computeIfAbsent(key, k -> new Value(f(k)));
map.computeIfAbsent(key, k -> new HashSet<V>()).add(v);

demo:

public void test() {
    Map<String, List<String>> map = new HashMap<>();
    // 如果key不存在,则创建新list并放入数据;key存在,则直接往list放入数据
    map.computeIfAbsent("fruit", k -> new ArrayList<>()).add("apple");
    map.computeIfAbsent("fruit", k -> new ArrayList<>()).add("orange");
    map.computeIfAbsent("language", k -> new ArrayList<>()).add("english");
    // 遍历
    map.forEach((k, v) -> System.out.println(k + " " + v));
}

map.computeIfAbsent("fruit", k -> new ArrayList<>()).add("apple");
这一句的意思是
key为fruit的键值对是否存在
1、如果存在,则给当前操作的List集合添加 apple
2、如果不存在,则新创建List结构,将apple添加到List集合,再存入Map集合

putIfAbsent 和 computeIfAbsent

  • 都是在 key 不存在的时候才会建立 key 和 value 的映射关系
  • putIfAbset 不论传入的 value 是否为空,都会建立映射(并不适合所有子类,例如 HashTable),而computeIfAbsent 方法,当存入value为空时,不做任何操作
  • putIfAbsent返回的是旧value,当key不存在时返回null;computeIfAbsent返回的都是新的value,即使computeIfAbsent在传入的value为null时,不会新建映射关系,但返回的也是null;

3.3、computeIfPresent

key 存在并且不为空,计算 remappingFunction 的值 value;

如果 value 不为空,保存指定 key 和 value 的映射关系;
如果 value 为 null,remove(key);
如果计算 value 的过程抛出了异常,computeIfPresent 方法中会再次抛出,key 和其对应的值不会改变

default V computeIfPresent(K key, BiFunction<? super K,? super V,? extends V> remappingFunction)

demo:

// 创建一个 HashMap
 HashMap<String, Integer> prices = new HashMap<>();

// 往HashMap中添加映射关系
prices.put("Shoes", 200);
prices.put("Bag", 300);
prices.put("Pant", 150);
System.out.println("HashMap: " + prices);

// 重新计算鞋加上10%的增值税后的价值
int shoesPrice = prices.computeIfPresent("Shoes", (key, value) -> value + value * 10/100);
System.out.println("Price of Shoes after VAT: " + shoesPrice);

// 输出更新后的HashMap
System.out.println("Updated HashMap: " + prices);


执行以上程序输出结果为:
HashMap: {Pant=150, Bag=300, Shoes=200}
Price of Shoes after VAT: 220
Updated HashMap: {Pant=150, Bag=300, Shoes=220}}

computeIfPresent 和 computeIfAbsent

  • 这两个方法正好相反,computeIfPresent 在key存在时,才会用新的value替换oldValue

  • 当传入的key存在,并且传入的value为null时,computeIfPresent 会remove(key),把传入的key对应的映射关系移除;而computeIfAbsent不论何时都不会remove();

  • computeIfPresent 只有在key存在,并且传入的value不为空的时候,返回值是value,其他情况都是返回null;

    computeIfAbsent 只有在key不存在,并且传入的value不为null的时候才会返回value,其他情况都返回null;

4、函数式接口 Lambda 表达式

函数式接口是一种只有单一抽象方法的接口,使用 @FunctionalInterface 来描述,可以隐式地转换成 Lambda 表达式。使用 Lambda 表达式来实现函数式接口,不需要提供类名和方法定义,通过一行代码提供函数式接口的实例,就可以让函数成为程序中的头等公民,可以像普通数据一样作为参数传递,而不是作为一个固定的类中的固定方法。

那,函数式接口到底是什么样的呢?java.util.function 包中定义了各种函数式接口。

比如,用于提供数据的 Supplier 接口,就只有一个 get 抽象方法,没有任何入参、有一个返回值:

@FunctionalInterfacepublic interface Supplier { 
    /** * Gets a result. * * @return a result */
    T get();
}

我们可以使用 Lambda 表达式或方法引用,来得到 Supplier 接口的实例:

//使用Lambda表达式提供Supplier接口实现,返回OK字符串
Supplier stringSupplier = ()->"OK";

//使用方法引用提供Supplier接口实现,返回空字符串
Supplier supplier = String::new;

4.1、四大基本函数式接口

Function<T, R>
接受一个入参T,返回R类型对象,使用apply方法获取方法执行的内容

R apply(T t);

demo1:

//Function接口是输入一个数据,计算后输出一个数据。我们先把字符串转换为大写,然后通过andThen组合另一个Function实现字符串拼接
Function<String, String> upperCase = String::toUpperCase;
Function<String, String> duplicate = s -> s.concat(s);
upperCase.andThen(duplicate).apply("test");

demo2:

User user = new User(88, "bb");

String name = uft.apply(user);
System.out.println(name);


/**
* Function<T, R> lambda写法
*/
private static Function<User, String> uft = u -> u.getName();

Consumer<T>
接受一个参数T,没有返回值,使用accept方法对参数执行操作

oid accept(T t);

demo1:

//Consumer接口是消费一个数据。我们通过andThen方法组合调用两个Consumer,输出两行abcdefg
Consumer<String> println = System.out::println;
println.andThen(println).accept("abcdefg");

demo2:

User user = new User(88, "bb");

uc.accept(user);

/**
* Consumer<T> lambda写法
*/
private static Consumer<User> uc = u -> System.out.println(u.getName());

Supplier<T>
没有入参,返回T类型结果,使用get方法获取返回结果

T get();

demo1:

//Supplier是提供一个数据的接口。这里我们实现获取一个随机数
Supplier<Integer> random = ()->ThreadLocalRandom.current().nextInt();
System.out.println(random.get());

demo2:

User user1 = us.get();
System.out.println(user1.getName());

/**
* Supplier<T> lambda写法
*/
private static Supplier<User> us = () -> new User(1, "us");

Predicate<T>
接受一个入参,返回结果为true或者false,使用test方法进行测试并返回测试结果

boolean test(T t);

demo1:

//Predicate接口是输入一个参数,返回布尔值。我们通过and方法组合两个Predicate条件,判断是否值大于0并且是偶数
Predicate<Integer> positiveNumber = i -> i > 0;
Predicate<Integer> evenNumber = i -> i % 2 == 0;
assertTrue(positiveNumber.and(evenNumber).test(2));

demo2:

boolean test = up.test(user);
System.out.println(test);    

/**
* Predicate<T>
*/
private static Predicate<User> up = u -> !u.getName().isEmpty();

4.2、自定义函数式接口

参见Java 函数式编程实例_出世&入世的博客-CSDN博客

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/386350.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

springboot集成canal 实现mysql增量同步mongodb

一、canal官网https://kgithub.com/alibaba/canal/二、下载地址https://kgithub.com/alibaba/canal/releases三、细节1.6版本有bug&#xff08;如果只是部署deployer&#xff0c;那没问题&#xff0c;如果你想部署admin模块来监控&#xff0c;那就会报错&#xff1a;java.nio.B…

运算方法和运算电路

文章目录运算方法和运算电路基本运算部件定点数的移位运算算术移位逻辑移位循环移位定点数的加减运算原码的加减法补码的加减法原码的乘法补码的乘法原码的除法补码的除法符号扩展大小端和内存对齐刷题小结最后运算方法和运算电路 基本运算部件 运算器一般包含这么几部分&…

7 线性回归及Python实现

1 统计指标 随机变量XXX的理论平均值称为期望: μE(X)\mu E(X)μE(X)但现实中通常不知道μ\muμ, 因此使用已知样本来获取均值 X‾1n∑i1nXi.\overline{X} \frac{1}{n} \sum_{i 1}^n X_i. Xn1​i1∑n​Xi​.方差variance定义为&#xff1a; σ2E(∣X−μ∣2).\sigma^2 E(|…

STM32单片机的FLASH和RAM

STM32内置有Flash和RAM&#xff08;而RAM分为SRAM和DRAM&#xff0c;STM32内为SRAM&#xff09;&#xff0c;硬件上他们是不同的设备存储器、属于两个器件&#xff0c;但这两个存储器的寄存器输入输出端口被组织在同一个虚拟线性地址空间内。 MDK预处理、编译、汇编、链接后编…

月薪7k和月薪27k的测试人都有哪些区别?掌握这些,领导都要高看你...

了解软件测试这行的人都清楚&#xff0c;功能测试的天花板可能也就15k左右&#xff0c;而自动化的起点就在15k左右&#xff0c;当然两个岗位需要掌握的技能肯定是不一样的。 如果刚入门学习完软件测试&#xff0c;那么基本薪资会在7-8k左右&#xff0c;这个薪资不太高主要是因…

【存储】RAID0、RAID1、RAID3、RAID5、RAID6、混合RAID10、混合RAID50

存储RAID基本概念RAID数据组织形式RAID数据保护方式常用RAID级别与分类标准创建RAID组成员盘要求热备盘&#xff08;Hot Spare&#xff09;RAID 0的工作原理RAID 0的数据写入RAID 0的数据读取RAID 1的工作原理RAID 1的数据写入RAID 1的数据读取RAID 3的工作原理RAID 3的数据写入…

数据Python 异常处理

python标准异常异常名称描述BaseException所有异常的基类SystemExit解释器请求退出KeyboardInterrupt用户中断执行(通常是输入^C)Exception常规错误的基类StopIteration迭代器没有更多的值GeneratorExit生成器(generator)发生异常来通知退出StandardError所有的内建标准异常的基…

python学习之定制发送带附件的电子邮件

Python SMTP发送邮件 SMTP&#xff08;Simple Mail Transfer Protocol&#xff09;即简单邮件传输协议,它是一组用于由源地址到目的地址传送邮件的规则&#xff0c;由它来控制信件的中转方式。python的smtplib提供了一种很方便的途径发送电子邮件。它对smtp协议进行了简单的封…

数据结构与算法基础(王卓)(12):队列的定义及其基础操作(解决假上溢的方法,循环队列解决队满时判断方法,链队和循环队列的初始化等)

循环队列&#xff1a; 解决假上溢的方法&#xff1a;引入循环队列&#xff08;判断是否溢出&#xff09; 将入队操作由&#xff1a; base[rear]x; rear; 准确的来说&#xff0c;是&#xff1a; Q.base[Q.rear]x; Q.rear; 改为 Q.base[Q.rear]x; Q.rear(Q.rear1)% MAXQSIZ…

摘录一下Python列表和元组的学习笔记

1 基础概念 列表一个值&#xff0c;列表值指的是列表本身&#xff0c;而不是列表中的内容 列表用[]表示 列表中的内容称为 表项 len()函数可以显示列表中表项的个数&#xff0c;比如下面这个例子 spam [cat, bat, dog, rat]print(len(spam))列表的范围选取中&#xff0c;比…

96.【SpringBoot接入支付宝-thymeleaf-springBoot】

SpringBoot接入支付宝(一)、前提工作:1、进入支付宝开发平台—沙箱环境1.1、进入个人沙箱环境1.2、接下来进行几个密钥的生成1.3、拿到两个密钥后&#xff0c;进行自定义密钥配置1.4、至此&#xff0c;我们沙箱环境的配置和基本参数都已经获取到。(二)、Java代码-thymeleaf1.导…

蓝桥杯-超级质数

蓝桥杯-超级质数1、问题描述2、解题思路3、代码实现1、问题描述 如果一个质数 P 的每位数字都是质数, 而且每两个相邻的数字组成的两位 数是质数, 而且每三位相邻的数字组成的三位数是质数, 依次类推, 如果每相邻的 k 位数字组成的 k 位数都是质数, 则 P 称为超级质数。 如果把…

重型工业机械设备远程监控解决方案

随着企业的发展&#xff0c;各类机械设备日益增多&#xff0c;为了实现高效的管理和使用&#xff0c;保障安全生产&#xff0c;建立智能化、信息化的工业制造系统成为了必然趋势。 传统的机械设备管理大多采用人工方式&#xff0c;现场作业人员需要每天通过电话、或者E-mail等方…

利用反射实现通过读取配置文件对类进行实例化-课后程序(JAVA基础案例教程-黑马程序员编著-第十二章-课后作业)

【案例12-3】&#xff1a;利用反射实现通过读取配置文件对类进行实例化 【案例介绍】 1.案例描述 现在有一个项目&#xff0c;项目中创建了一个Person类&#xff0c;在Person类中定义了一个sleep()方法。在工程中还定义了一个Student类继承Person类&#xff0c;在Student类中…

项目管理PMP证书的含金量真的高吗?

含金量当然高的&#xff0c;特别是在项目管理这个领域&#xff0c;绝对是知名度最高的&#xff0c;行业内最受认可的相关证书&#xff0c;含金量等同于于MBA&#xff0c;那么&#xff0c;除去它本身专业知识价值&#xff0c;它的含金量高在哪里呢&#xff1f; &#xff08;含资…

linux离线状态下将视频mp4格式转换m3u8

因为业务需求&#xff0c;python文件生成的视频为MP4格式&#xff0c;无法在前端浏览器正常播放&#xff0c;需切换m3u8格式&#xff0c;但因为服务器是没有网络的&#xff0c;因袭需要在离线状态下进行完成。 1.离线安装ffmpeg 看网上许多教程&#xff0c;ffmpeg可完成视频格…

【工具】笔记软件测评(简单)

介绍 笔记软件测评 印象笔记 ── ❌开源、❌markdown&#xff08;不好用&#xff09;、❌本地存储 臭名昭著&#xff0c;被资本搞烂的理想。 notion ── ❌开源、❌本地存储、✔️数据导出&#xff1a;PDF、HTML、Markdown & CSV &#xff08;include subpages&#xf…

重回利润增长,涪陵榨菜为何能跑赢周期?

2022年消费市场持续低迷&#xff0c;疫情寒冬之下&#xff0c;不少食品快消企业均遭遇严重的业绩下滑&#xff0c;但一年里不断遭遇利空打击的“榨菜茅”涪陵榨菜&#xff0c;不仅安然躲过“酸菜劫”、走出“钠”争议&#xff0c;而且顺利将产品价格提起来&#xff0c;并在寒冬…

C++基础篇(一)-- 简单入门

C 语言是在优化 C 语言的基础上为支持面向对象的程序设计而研制的一个通用目的的程序设计语言。在后来的持续研究中&#xff0c;C 增加了许多新概念&#xff0c;例如虚函数、重载、继承、标准模板库、异常处理、命名空间等。 C 语言的特点主要表现在两个方面&#xff1a;全面兼…

CSS的背景版块知识

&#x1f60a;博主页面&#xff1a;鱿年年 &#x1f449;博主推荐专栏&#xff1a;《WEB前端》&#x1f448; ​&#x1f493;博主格言&#xff1a;追风赶月莫停留&#xff0c;平芜尽处是春山❤️ 目录 前言 一、背景颜色 二、背景图片 三、背景平铺 四、背景图片位置…