【技术积累】Java 8 新特性
一、Lambda表达式
Lambda 是一个匿名函数,我们可以把 Lambda表达式理解为是一段可以传递的代码(将代码像数据一样进行传递)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,使Java的语言表达能力得到了提升
1、举例
( o1 , o2 ) -> Integer.compare( o1 , o2 )
2、格式
- -> : lambda 操作符或箭头操作符
- -> 左边 :lambda 形参列表(其实就是抽象中的抽象方法的形参列表)
- -> 右边 :lambda 体(其实就是重写的抽象方法的方法体)
3、lambda 表达式的使用(6种情况)
Runnable r = () -> System.out.println("hello");
Consumer<String> consumer = (args) -> System.out.println(args);
Consumer<String> consumer2 = args -> System.out.println(args);
BinaryOperator<Long> bo = (Long x,Long y) -> {
System.out.println("实现函数接口方法!");
return x + y;
};
BinaryOperator<Long> bio = (Long x,Long y) -> x + y;
BinaryOperator<Long> bio = (x, y) -> x + y;
4、lambda 表达式的本质:作为接口的实例
二、函数式接口
1、什么是函数式接口
- 只包含一个抽象方法的接口,称为函数式接口。
- 你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda 表达式抛出一个受检异常,那么该异常需要在目标接口的抽象方法上进行声明)。
- 我们可以在任意函数式接口上使用 @FunctionalInterface 注解,这样做可以检查它是否是一个函数式接口,同时 javadoc 也会包含一条声明,说明这个接口是一个函数式接口。
2、自定义函数式接口
@FunctionalInterface
public interface MyInterface {
public String getValue();
}
@FunctionalInterface
public interface MyInterface<T> {
public T getValue(T t);
}
3、Lambda 表达式作为参数传递
注意:为了将 Lambda 表达式作为参数传递,接收Lambda 表达式的参数类型必须是与该 Lambda 表达式兼容的函数式接口的类型。
4、Java 内置四大核心函数式接口
消费型接口Consumer void accept(T t)
@Test
public void testConsumer() {
// 消费型接口Consumer<T> void accept(T t)
buyCar(265000.00, new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
System.out.println("买车花费了:" + aDouble);
}
});
System.out.println("使用lambda表达式如下");
buyCar(99999.99, money -> System.out.println("买车花费了:" + money));
}
/**
* 利用Consumer 实现消费
* @param money 金额
* @param con 所消费金额
*/
public void buyCar(Double money, Consumer<Double> con) {
con.accept(money);
}
断定型接口Predicate boolean test(T t)
@Test
public void testPredicate() {
// 断定型接口Predicate<T> boolean test(T t)
List<String> list = Arrays.asList("aabb", "bbcc", "ccdd", "aadd");
List<String> res = filterString(list, new Predicate<String>() {
@Override
public boolean test(String s) {
return s.contains("aa");
}
});
System.out.println("过滤后:" + res);
System.out.println("使用lambda表达式如下");
List<String> res2 = filterString(list, s -> s.contains("aa"));
System.out.println("过滤后:" + res2);
}
/**
* 利用Predicate 实现过滤
* @param list 原数组
* @param pre 约束条件
* @return 过滤后数组
*/
public List<String> filterString(List<String> list, Predicate<String> pre) {
List<String> result = new ArrayList<>();
for (String s : list) {
if (pre.test(s)) {
result.add(s);
}
}
return result;
}
供给型接口Supplier T get()
函数型接口Function<T,R> R apply(T t)
5、其他接口
三、函数式引用(方法引用与构造器引用)
使用场景
当要传递给 Lambda 体的操作,已经有实现的方法了,可以使用方法引用!
方法引用,本质上也是 Lambda 表达式,而 Lambda 表达式作为函数式接口的实例。所以,方法引用也是函数式接口的实例。
使用格式
方法引用使用的要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的形参列表和返回值类型相同!(情况三特殊)
情况一 对象 :: 非静态方法
情况二 类 :: 静态方法
情况三 类 :: 非静态方法
示例代码
package com.mv.java8.basic;
import org.junit.Test;
import java.io.PrintStream;
import java.util.Arrays;
import java.util.Comparator;
import java.util.Date;
import java.util.function.Consumer;
import java.util.function.Function;
import java.util.function.Supplier;
/**
* 方法引用
* 1.使用情景:当要传递给 Lambda 体的操作,已经有实现的方法了,可以使用方法引用!
* 2.方法引用,本质上也是 Lambda 表达式,而 Lambda 表达式作为函数式接口的实例。所以,方法引用也是函数式接口的实例。
* 3.使用格式: 类 (或对象) :: 方法名
* 4.具体分为以下三种情况
* 5.方法引用使用的要求:要求接口中的抽象方法的形参列表和返回值类型与方法引用的方法的形参列表和返回值类型相同!
* 对象 :: 非静态方法
* 类 :: 静态方法
* 类 :: 非静态方法
*
* @author wv
* @version V1.0
* @date 2023/8/3 19:45
*/
public class MethodRefTest {
@Test
public void test01() {
// 情况一 : 对象 :: 实例方法
// Consumer 中的 void accept(T t)
// PrintStream 中的 void println(T t)
Consumer<String> con1 = str -> System.out.println(str);
con1.accept("hello");
System.out.println("使用方法引用如下:");
// PrintStream printStream = System.out;
// Consumer<String> con2 = printStream::println;
Consumer<String> con2 = System.out::println;
con2.accept("world");
}
@Test
public void test02() {
// 情况二 : 类 :: 静态方法
// Comparator 中的 int compare(T t1, T t2)
// Integer 中的 int compare(T t1, T t2)
Comparator<Integer> com1 = (a, b) -> Integer.compare(a, b);
System.out.println(com1.compare(10, 5));
System.out.println("使用方法引用如下:");
Comparator<Integer> com2 = Integer::compare;
System.out.println(com1.compare(5, 10));
// Function 中的 apply(T t)
// Math 中的 Long round(Double d)
Function<Double, Long> func = new Function<Double, Long>() {
@Override
public Long apply(Double aDouble) {
return Math.round(aDouble);
}
};
System.out.println(func.apply(3.14));
System.out.println("使用方法引用如下:");
Function<Double, Long> func1 = Math::round;
System.out.println(func1.apply(3.14));
}
@Test
public void test03() {
// 情况二 : 类 :: 非静态方法
// Comparator 中的 int compare(T t1, T t2)
// String 中的 int t1.compareTo(t2)
Comparator<String> com1 = (a, b) -> a.compareTo(b);
System.out.println(com1.compare("a", "b"));
System.out.println("使用方法引用如下:");
Comparator<String> com2 = String::compareTo;
System.out.println(com1.compare("a", "b"));
}
@Test
public void test04() {
// 构造器引用
// Supplier 中的 T get()
Supplier<Object> supplier1 = new Supplier<Object>() {
@Override
public Object get() {
return new Object();
}
};
System.out.println(supplier1.get());
System.out.println("使用方法引用如下:");
Supplier<Object> supplier2 = Object::new;
System.out.println(supplier2.get());
// 数组引用
// Function 中的 R apply(T t)
Function<Integer, String[]> func1 = length -> new String[length];
String[] strings = func1.apply(6);
System.out.println(Arrays.toString(strings));
System.out.println("使用方法引用如下:");
Function<Integer, String[]> func2 = String[] ::new;
String[] strings2 = func1.apply(6);
System.out.println(Arrays.toString(strings2));
}
}
四、Stream 流
1、概念
Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一个则是 Stream API(java.util.stream.*)。
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数据库查询。也可以使用 Stream API 来并行执行操作。简而言之,Stream API 提供了一种高效且易于使用的处理数据的方式。
stream 流是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
集合讲究的是数据,流讲究的是计算!
2、操作步骤
- 创建 Stream :一个数据源(如:集合、数组),获取一个流
- 中间操作:一个中间操作链,对数据源的数据进行处理
- 终止操作(终端操作)
3、特点
- Stream 关注的是对数据的运算,CPU 打交道;集合关注的是数据的存储,与内存打交道。
- Stream 流
- 不会自己存储元素
- 不会改变数据源。相反,他们会返回一个持有结果的 Stream 流
- 操作是延迟执行的,这意味着他们会等到需要结果的时候执行
- Stream 执行流程
- stream 的实例化
- 一系列的中间操作(过滤、映射、......)
- 终止操作
- 说明
- 一个中间操作链,对数据源的数据进行处理
- 一旦执行终止操作,就执行中间操作链,并产生结果。之后,不会再被使用。(需重新执行)
4、Stream 流创建方式
创建方式一:通过集合创建 Stream 流
创建方式一:通过数组创建 Stream 流
创建方式一:通过 Stream 流的静态方法 of( ) 创建
创建方式一:由函数创建流:创建无限流
/**
* 通过集合创建 Stream 流
*/
@Test
public void createStreamByCollection() {
List<String> list = Arrays.asList("aaa","bbb","ccc");
// 1.串行流
Stream<String> stream = list.stream();
// 2.并行流
Stream<String> parallelStream = list.parallelStream();
}
/**
* 通过数组创建 Stream 流
*/
@Test
public void createStreamByArray() {
int[] arr = new int[]{1,2,3,4,5,6};
// 调用 Arrays 类的静态方法 stream
IntStream stream = Arrays.stream(arr);
}
/**
* 通过 Stream 流的 静态方法of() 创建
*/
@Test
public void createStreamByOf() {
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5);
}
/**
* 由函数创建流:创建无限流
*/
@Test
public void createStream() {
Stream<Integer> integerStream = Stream.of(1, 2, 3, 4, 5);
// 迭代
//public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f)
Stream.iterate(0,t -> t + 2).limit(10).forEach(System.out::println);
// 生成
// public static<T> Stream<T> generate(Supplier<T> s)
Stream.generate(Math::random).limit(10).forEach(System.out::println);
}
5、Stream 流的中间操作
筛选与切片
- filter(Predicate p) 接收Lambda,从流中排除某些元素。
- limit(n) 截断流,使其元素不超过给定数量。
- skip(n) 跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit()互补。
- distinct() 筛选,通过流所生成元素的hashCode()和equals()去除重复元素。
public class StreamAPITest2 {
/**
* 筛选与切片
*/
@Test
public void test() {
List<String> list = Arrays.asList("李华","小明","小红","小王","李华");
System.out.println(list);
System.out.println("*************************");
// filter(Predicate p) 接收Lambda,从流中排除某些元素。
List<String> filterList = list.stream().filter(e -> !e.equals("李华")).collect(Collectors.toList());
System.out.println(filterList);
System.out.println("*************************");
// limit(n) 截断流,使其元素不超过给定数量。
System.out.println(list.stream().limit(2).collect(Collectors.toList()));
System.out.println("*************************");
// skip(n) 跳过元素,返回一个扔掉了前n个元素的流。若流中元素不足n个,则返回一个空流。与limit()互补.
System.out.println(list.stream().skip(2).collect(Collectors.toList()));
System.out.println("*************************");
// distinct() 筛选,通过流所生成元素的hashCode()和equals()去除重复元素
System.out.println(list);
System.out.println(list.stream().distinct().collect(Collectors.toList()));
System.out.println("*************************");
}
}
映射
- **map(Function f) **:接收一个函数作为参数,将元素转换成其他形式或提取信息,该函数会被应用到每个元素上,并将其映射成一个新的元素。
- flatMap(Function f) :接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
- mapToDouble(ToDoubleFunction f):接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 DoubleStream。
- mapToInt(ToIntFunction f):接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 IntStream。
- mapToLong(ToLongFunction f):接收一个函数作为参数,该函数会被应用到每个元素上,产生一个新的 LongStream。
/**
* map 与 flatMap映射
*/
@Test
public void testMap() {
List<String> list = Arrays.asList("aa", "bb", "cc", "dd");
// map(Function f) 接收一个函数作为参数,将元素转换成其他形式或提取信息,
// 该函数会被应用到每个元素上,并将其映射成一个新的元素。
// System.out.println(list.stream().map(str -> str.toUpperCase()).collect(Collectors.toList()));
System.out.println(list.stream().map(String::toUpperCase).collect(Collectors.toList()));
// flatMap(Function f) 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有流连接成一个流。
// 未使用 flatMap
Stream<Stream<Character>> streamStream = list.stream().map(StreamAPITest2::formStringToStream);
streamStream.forEach(s -> {
s.forEach(System.out::println);
});
System.out.println("****************************");
// 使用 flatMap
Stream<Character> characterStream = list.stream().flatMap(StreamAPITest2::formStringToStream);
characterStream.collect(Collectors.toList()).forEach(System.out::println);
}
public static Stream<Character> formStringToStream(String str) {
List<Character> list = new ArrayList<>();
char[] chars = str.toCharArray();
for (char c : chars) {
list.add(c);
}
return list.stream();
}
排序
- sorted() 自然排序
- sorted(Comparator com) 定制排序
/**
* 排序
*/
@Test
public void testSort() {
// sorted() 自然排序
List<Integer> integerList = Arrays.asList(12, 23, -12, 32, 55, 66, 11);
integerList.stream().sorted().forEach(System.out::println);
System.out.println("***************************");
// sorted(Comparator com) 定制排序
integerList.stream().sorted( (x,y) -> Integer.compare(y,x)).forEach(System.out::println);
}
6、Stream 的终止操作
查找与匹配
- allMatch(Predicate p) :检查是否匹配所有元素
- anyMatch(Predicate p):检查是否至少匹配一个元素
- noneMatch(Predicate p):检查是否没有匹配所有元素
- findFirst():返回第一个元素
- findAny():返回当前流中的任意元素
- count():返回流中元素总数
- max(Comparator c):返回流中最大值
- min(Comparator c):返回流中最小值
- forEach(Consumer c) 内部迭代(使用 Collection 接口需要用户去做迭代,称为外部迭代。相反,Stream API 使用内部迭代——它帮你把迭代做了)
归约
- reduce(T t, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 T
- reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 Optional
/**
* 终止操作 : 归约
*/
@Test
public void testReduce() {
List<Integer> integerList = Arrays.asList(1, 2, 3, 4, 5, 6, 7);
// reduce(T t, BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 T
System.out.println(integerList.stream().reduce(0, Integer::sum));
// reduce(BinaryOperator b) 可以将流中元素反复结合起来,得到一个值。返回 Optional<T>
// System.out.println(integerList.stream().reduce((a, b) -> a + b));
System.out.println(integerList.stream().reduce(Integer::sum));
}
收集
- collect(Collector c):将流转换为其他形式。接收一个 Collector接口的实现,用于给Stream中元素做汇总的方法
/**
* 终止操作 : 收集
*/
@Test
public void testCollect() {
List<Integer> integerList = Arrays.asList(1, 2, 2, 2, 3, 4);
// toList
System.out.println(integerList.stream().skip(1).collect(Collectors.toList()));
// toSet
System.out.println(integerList.stream().skip(1).collect(Collectors.toSet()));
}
五、Optional 类
Optional 类(java.util.Optional) 是一个容器类,代表一个值存在或不存在,原来用 null 表示一个值不存在,现在 Optional 可以更好的表达这个概念。并且可以避免空指针异常。
常用方法
Optional.of(T t) : 创建一个 Optional 实例
Optional.empty() : 创建一个空的 Optional 实例
Optional.ofNullable(T t):若 t 不为 null,创建 Optional 实例,否则创建空实例
isPresent() : 判断是否包含值
orElse(T t) : 如果调用对象包含值,返回该值,否则返回t
orElseGet(Supplier s) :如果调用对象包含值,返回该值,否则返回 s 获取的值
map(Function f): 如果有值对其处理,并返回处理后的Optional,否则返回 Optional.empty()
flatMap(Function mapper):与 map 类似,要求返回值必须是Optional
【技术积累】Java 8 新特性的更多相关文章
- Java 8 新特性终极版
声明:本文翻译自Java 8 Features Tutorial – The ULTIMATE Guide,翻译过程中发现并发编程网已经有同学翻译过了:Java 8 特性 – 终极手册,我还是坚持自己 ...
- Java 8新特性探究(八)精简的JRE详解
http://www.importnew.com/14926.html 首页 所有文章 资讯 Web 架构 基础技术 书籍 教程 Java小组 工具资源 - 导航条 - 首页 所有文章 资讯 ...
- Java 8 新特性终极指南
1.前言 毫无疑问,Java 8的发布是自从Java5以来Java世界中最重大的事件,它在编译器.工具类和Java虚拟机等方面为Java语言带来的很多新特性.在本文中我们將一起关注下这些新变化,使用实 ...
- Java 11 新特性介绍
Java 11 已于 2018 年 9 月 25 日正式发布,之前在Java 10 新特性介绍中介绍过,为了加快的版本迭代.跟进社区反馈,Java 的版本发布周期调整为每六个月一次——即每半年发布一个 ...
- Java 8 新特性——实践篇
Java 8 新特性--实践篇 参考 Java8新特性 重要更新:Lambda 表达式和Stream API Lambda 表达式 Lambda 表达式引入之前: 举个场景例子:当我们要对一个班级里的 ...
- JAVA 8 新特性实用总JAVA 8 新特性实用总结结
JAVA 8 新特性实用总结 作为一个工作两年多的 老 程序猿,虽然一开始就使用 jdk1.8 作为学习和使用的版本,随着技术的迭代,现有的 JDK 版本从两年前到现在,已经飞速发展到了 JDK 15 ...
- Java 18 新特性:使用Java代码启动jwebserver
前几天分享了Java 18 新特性:简单Web服务器的jwebserver命令行功能. 今天换一种方式,使用Java代码来实现一个静态资源服务器. 详细步骤我录了个视频放到B站了,感兴趣的小伙伴可以点 ...
- Java 15 新特性:文本块
大家好,我是DD,今天继续来学点Java的新特性! 假设有这样一个场景,我们需要做一个工具.用来自动生成项目文档,文档可以通过浏览器查看,所以最后产出物肯定是一堆html文件.为了让这些html文件更 ...
- Java 16 新特性:record类
以前我们定义类都是用class关键词,但从Java 16开始,我们将多一个关键词record,它也可以用来定义类.record关键词的引入,主要是为了提供一种更为简洁.紧凑的final类的定义方式. ...
- Java 17 新特性:switch的模式匹配(Preview)
还记得Java 16中的instanceof增强吗? 通过下面这个例子再回忆一下: Map<String, Object> data = new HashMap<>(); da ...
随机推荐
- lua开发和调试环境
Lua开发环境搭建 Lua官网提供源码下载需要自己编译,Lua官网:https://www.lua.org/ftp/ lua for windows.exe(占二十多MB那个) 目前在网络上没有找到 ...
- 6.0 Python 使用函数装饰器
装饰器可以使函数执行前和执行后分别执行其他的附加功能,这种在代码运行期间动态增加功能的方式,称之为"装饰器"(Decorator),装饰器的功能非常强大,装饰器一般接受一个函数对象 ...
- Walrus 实用教程|Walrus + Gitlab,打通CI/CD 自动化交付!
Walrus file 是 Walrus 0.5 版本推出的新功能,用户可以通过一个非常简洁的 YAML 描述应用或基础设施资源的部署配置,然后通过 Walrus CLI 执行 walrus appl ...
- JDK8新特性Stream流操作
1 package stream; 2 3 import java.util.ArrayList; 4 import java.util.function.Function; 5 import jav ...
- 理论与实战:一篇看懂Python词云
理论与实战:一篇看懂Python词云 后宫王镇贴 前言:本文初编辑于2024年2月2日 该项目代码的仓库地址:https://github.com/A-Piece-Of-Maple/WordCloud ...
- KB0003.申请和加载DoraCloud的软件许可
KB0003.申请和加载DoraCloud的软件许可 DoraCloud安装后,默认处于30天试用状态.如果您购买了软件授权,可以申请许可证. 在[系统][License管理][获取License文件 ...
- YOLO数据集划分(测试集和验证集)
在目标检测任务中,数据集的划分通常分为训练集和验证集,以便在训练模型时评估模型的性能.这个过程对于有效训练和评估目标检测模型非常重要.下面是划分目标检测数据集的一般步骤:`` 数据集组织: 确保你的数 ...
- postman application/json;
看来以后需要都统一使用这个json比较方便. 首先看下 spring boot 项目接口的返回: 当然若不加,他默认可能也是 UTF-8,还是加一下吧.这样就可以了,保证以后 请求和响应的 conte ...
- JS leetcode 两数之和解答思路分析
壹 ❀ 引 在学习算法基础的同时,我还是继续捡起leetcode的算法题了,珍惜时间,算法每天进步一点点.不得不说,在了解了一些算法概念后,至少有些答案是能看懂了......(惭愧)虽然我很菜,但是多 ...
- Linux中查看dmesg中 ata1对应的盘符,以及SATA/NVME SSD的rescan/delete操作方法
1.查看dmesg 中ata1 对应的盘符: Step1; lsscsi -s 查看盘符(bdf) 对应的host Id: Step2: ll /sys/class/scsi_host 可以找到不 ...