Java8新特性
一,简介
新特点简介:
? 速度更快
? 代码较少(添加了新语法) )
? 强大的
? 便于并行
? 最大限度地减少空指针异常 Optional
最核心的是 Lambda 表达式与Stream API
二. Lambda表达式
1.简介
为什么使用Lambda 表达式:
Lambda 是一个,我们可以把 Lambda表达理解为是(像数据一样传输代码)。可以写出更简洁、更灵活的代码。作为一种更紧凑的代码风格,它使Java语言表达能力有所提高。
2.从匿名到匿名 Lambda 的转换
3.语法
Lambda 表达式在Java 语言中引入了新的语法元素和操作符。这个操作符合 “” , 操作符被称为 Lambda 操作符或剪头操作符。 Lambda 分为两部分:
**左侧:**指定了 Lambda 所有参数表达式所需的所有参数
**右侧:**指定了 Lambda 体,即 Lambda 要执行表达式的功能
语法格式1:无参,无返回值,Lambda身体只需要一个句子
语法格式2:Lambda需要参数
语法格式三:Lambda当只需要一个参数时,可以省略参数的小括号
语法格式四:Lambda需要两个参数,并且有返回值
Comparator<Integer> comparator = (x , y)->{
System.out.println("包含多个句子的大括号"); System.out.println("比较x,y的大小"); return Integer.compare(x,y); };
语法格式五:当Lambda当身体只有一个句子时,return可以省略大括号
Comparator<Integer> comparator = (x,y)->Integer.compare(x,y);
语法格式六:Lambda表达式参数列表的数据类型可以省略,因为JVM编译器通过上下文推断出数据类型,即数据推断
上述 Lambda 编译器推断了表达式中的参数类型。Lambda 由于表达式中没有指定类型,程序仍然可以编译 javac 根据程序的上下文,在后台推断出参数的类型。Lambda 表达式的类型出的表达式类型依赖于上下文环境。这就是所谓的 类型推断
三.函数式接口
1.定义
? 只包含一个抽象接口,称为。
? 你可以通过 Lambda 表达式来创建该接口的对象。(若 Lambda
如果表达式抛出异常,则需要在目标界面抽象方
法律声明)。
? 我们可以在任何函数接口上使用它 注解,
这样做可以同时检查它是否是函数接口 javadoc 也会包
包含一个声明,表明该接口是函数接口。
2.自定义函数接口
3.作为参数传递Lambda 表达式
作为参数传递 Lambda 表达式:为了将 Lambda 表达式作为参数传递,接 收Lambda 表达式的参数类型必须是与该 Lambda 表达式兼容的函数式接口的类型。
*练习:
MyFunction
package Test;
@FunctionalInterface
public interface MyFunction {
public String getValue(String str);
}
Test2
package Test;
import org.testng.annotations.Test;
public class Test2 {
@Test
public void test2(){
//先去掉首尾空格
String strtrim = strHandler("\t\t\t Java8",(str) -> str.trim());
System.out.println(strtrim);
//截取字符
String newstr = strHandler("abcdefghijklmn",str -> str.substring(2,5));
System.out.println(newstr);
}
//用于处理字符串的方法
public String strHandler(String str,MyFunction myFunction){
return myFunction.getValue(str);
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-4atDP0r0-1649521532194)(https://gitee.com/yan-chaochao/typora-library/raw/master/typora-library/image-20220406193939471.png)]
四.Java四大内置的函数接口
函数式接口 | 参数类型 | 用途 | |
---|---|---|---|
Consumer< T >消费型接口 | T | void | 对类型为T的对象应用操作:void accept(T t) |
Supplier< T >供给型接口 | 无 | T | 返回类型为T的对象:T get() |
Function<T, R>函数型接口 | T | R | 对类型为T的对象应用操作,并返回结果为R类型的对象:R apply(T t) |
Predicate 断言型接口 | T | boolean | 确定类型为T的对象是否满足某约束,并返回boolean值:boolean test(T t) |
1.消费型接口
@Test
public void test01(){
//Consumer
Consumer<Integer> consumer = (x) -> System.out.println("消费型接口" + x);
//test
consumer.accept(100);
}
2.供给型接口
@Test
public void test02(){
List<Integer> list = new ArrayList<>();
List<Integer> integers = Arrays.asList(1,2,3);
list.addAll(integers);
//Supplier<T>
Supplier<Integer> supplier = () -> (int)(Math.random() * 10);
list.add(supplier.get());
System.out.println(supplier);
for (Integer integer : list) {
System.out.println(integer);
}
}
3.函数型接口
@Test
public void test03(){
//Function<T, R>
String oldStr = "abc123456xyz";
Function<String, String> function = (s) -> s.substring(1, s.length()-1);
//test
System.out.println(function.apply(oldStr));
}
4.断言型接口
@Test
public void test04(){
//Predicate<T>
Integer age = 35;
Predicate<Integer> predicate = (i) -> i >= 35;
if (predicate.test(age)){
System.out.println("你该退休了");
} else {
System.out.println("我觉得还OK啦");
}
}
其他接口
五.引用
1.方法引用
定义:
若 Lambda 表达式体中的内容已有方法实现,则我们可以使用“方法引用”
语法格式:
- 对象 :: 实例方法
- 类 :: 静态方法
- 类 :: 实例方法
对象::实例方法
package study;
import org.testng.annotations.Test;
import java.io.PrintStream;
import java.util.function.Consumer;
public class Test3 {
@Test
public void test01(){
PrintStream ps = System.out;
Consumer<String> con1 = (s) -> ps.println(s);
con1.accept("aaa");
Consumer<String> con2 = ps::println;
con2.accept("bbb");
}
}
**注意:**Lambda 表达实体中调用方法的参数列表、返回类型必须和函数式接口中抽象方法保持一致
类::静态方法
package study;
import org.testng.annotations.Test;
import java.io.PrintStream;
import java.util.Comparator;
import java.util.function.Consumer;
public class Test3 {
@Test
public void test02(){
Comparator<Integer> com1 = (x, y) -> Integer.compare(x, y);
System.out.println(com1.compare(1, 2));
Comparator<Integer> com2 = Integer::compare;
System.out.println(com2.compare(2, 1));
}
}
类::实例方法
package study;
import org.testng.annotations.Test;
import java.io.PrintStream;
import java.util.Comparator;
import java.util.function.BiPredicate;
import java.util.function.Consumer;
public class Test3 {
@Test
public void test03(){
BiPredicate<String, String> bp1 = (x, y) -> x.equals(y);
System.out.println(bp1.test("a","b"));
BiPredicate<String, String> bp2 = String::equals;
System.out.println(bp2.test("c","c"));
}
}
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-9yibnowR-1649521532197)(C:\Users\lenovo\AppData\Roaming\Typora\typora-user-images\image-20220406221640623.png)]
**条件:**Lambda 参数列表中的第一个参数是方法的调用者,第二个参数是方法的参数时,才能使用 ClassName :: Method
2.构造器引用
格式:
ClassName :: new
@Test
public void test04(){
Supplier<List> sup1 = () -> new ArrayList();
//构造器引用方式
Supplier<List> sup2 = ArrayList::new;
}
**注意:**需要调用的构造器的参数列表要与函数时接口中抽象方法的参数列表保持一致
3.数组引用
@Test
public void test05(){
Function<Integer,String[]> fun1 = (x)->new String[x];
String[] str1 = fun1.apply(10);
System.out.println(str1.length);
Function<Integer,String[]> fun2 = String[]::new;
String[] str2 = fun2.apply(20);
System.out.println(str2.length);
}
六.Stream API
1.了解Stream
(1)简介
Java8中有两大最为重要的改变。第一个是 Lambda 表达式;另外一
个则是 。
Stream 是 Java8 中处理集合的关键抽象概念,它可以指定你希望对
集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。
使用Stream API 对集合数据进行操作,就类似于使用 SQL 执行的数
据库查询。也可以使用 Stream API 来并行执行操作。简而言之,
Stream API 提供了一种高效且易于使用的处理数据的方式。
(2)什么是Stream
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
①Stream 自己不会存储元素。
②Stream 不会改变源对象。相反,他们会返回一个持有结果的新Stream。
③Stream 操作是延迟执行的。这意味着他们会等到需要结果的时候才执行。
2.Stream的操作三个步骤
⚫
一个数据源(如:集合、数组),获取一个流
⚫
一个中间操作链,对数据源的数据进行处理
⚫
一个终止操作,执行中间操作链,并产生结果
3.创建Stream
Java8 中的 Collection 接口被扩展,提供了两个获取流的方法:
⚫ default Stream stream() : 返回一个顺序流
⚫ default Stream parallelStream() : 返回一个并行流
(1)由数组创建流
Java8 中的 Arrays 的静态方法 stream() 可以获取数组流:
⚫ static Stream stream(T[] array): 返回一个流重载形式,能够处理对应基本类型的数组:
⚫ public static IntStream stream(int[] array)
⚫ public static LongStream stream(long[] array)
⚫ public static DoubleStream stream(double[] array)
(2)由值创建流
可以使用静态方法 Stream.of(), 通过显示值创建一个流。它可以接收任意数量的参数。
⚫ public static Stream of(T… values) : 返回一个流
(3)由函数创建流:创建无限流
可以使用静态方法 Stream.iterate() 和Stream.generate(), 创建无限流。
⚫ 迭代
public static Stream iterate(final T seed, final
UnaryOperator f)
⚫ 生成
public static Stream generate(Supplier s) :
代码举例:
@Test
public void test06(){
//1.可以通过Collection系列集合提供的stream()或parallelStream()
//集合流:
// - Collection.stream() 穿行流
// - Collection.parallelStream() 并行流
List<String> list = new ArrayList<>();
Stream<String> stream1 = list.stream();
//2.通过Arrays中的静态方法stream()获取数组连接
//Arrays.stream(array)
String[] strings = new String[10];
Stream<String> stream2 = Arrays.stream(strings);
//3.通过Stream 类中静态方法of()
//Stream.of(...)
Stream<Integer> stream3 = Stream.of(1, 2, 3);
//4.创建无限流
//迭代
Stream<Integer> stream4 = Stream.iterate(0, (i) -> i+2);
stream4.limit(10).forEach(System.out::println);
//生成
Stream.generate(() -> Math.random())
.limit(5)
.forEach(System.out::println);
}
4.Stream中间操作
多个可以连接起来形成一个,除非流水线上触发终止操作,否则!而在。
(1)筛选与切片
-
内部迭代:迭代操作由 Stream API 完成
-
外部迭代:我们通过迭代器完成
(2)映射
- map:接收 Lambda ,将元素转换为其他形式或提取信息;接受一个函数作为参数,该函数会被应用到每个元素上,并将其映射成一个新的元素
@Test
public void test08(){
List<String> list = Arrays.asList("a", "b", "c");
list.stream()
.map((str) -> str.toUpperCase())
.forEach(System.out::println);
}
- flatMap:接收一个函数作为参数,将流中每一个值都换成另一个流,然后把所有流重新连接成一个流
public class Tests {
public Stream<Character> filterCharacter(String str){
List<Character> list = new ArrayList<>();
for (char c : str.toCharArray()) {
list.add(c);
}
return list.stream();
}
}
@Test
public void test09(){
List<String> list = Arrays.asList("aaa", "bbb", "ccc");
Tests tests = new Tests();
list.stream()
.flatMap(tests::filterCharacter)
.forEach(System.out::println);
}
结果:
a
a
a
b
b
b
c
c
c
===============================================
Default Suite
Total tests run: 1, Passes: 1, Failures: 0, Skips: 0
===============================================
(3)排序
方法 | 描述 |
---|---|
产生一个新流,其中按自然顺序排序 | |
产生一个新流,其中按比较器顺序排序 |
**sorted()**自然排序
@Test
public void test10(){
List<String> list = Arrays.asList("c", "a", "b");
list.stream()
.sorted()
.forEach(System.out::println);
}
结果:
a
b
c
===============================================
Default Suite
Total tests run: 1, Passes: 1, Failures: 0, Skips: 0
===============================================
进程已结束,退出代码0
比较器顺序排序
@Test
public void test05(){
emps.stream()
.sorted((e1, e2) -> {
//compara()
if (e1.getAge().equals(e2.getAge())){
return e1.getName().compareTo(e2.getName());
} else {
return e1.getAge().compareTo(e2.getAge());
}
})
.forEach(System.out::println);
}
5.Stream的终止操作
终端操作会从流的流水线生成结果。其结果可以是任何不是流的值,例如:List、Integer,甚至是 void 。
(1)查找与匹配
方法 | 描述 |
---|---|
检查是否匹配所有元素 | |
() | 检查是否至少匹配一个元素 |
检查是否没有匹配所有元素 | |
返回第一个元素 | |
返回当前流中的任意元素 |
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zpJIIb7C-1649521532199)(https://gitee.com/yan-chaochao/typora-library/raw/master/typora-library/image-20220407154558408.png)]
-
allMatch:检查是否匹配所有元素
-
anyMatch:检查是否至少匹配一个元素
-
noneMatch:检查是否没有匹配所有元素
-
findFirst:返回第一个元素
-
findAny:返回当前流中的任意元素
-
count:返回流中元素的总个数
-
max:返回流中最大值
-
min:返回流中最小值
@Test
public void test01(){
List<Status> list = Arrays.asList(Status.FREE, Status.BUSY, Status.VOCATION);
boolean flag1 = list.stream()
.allMatch((s) -> s.equals(Status.BUSY));
System.out.println(flag1);
boolean flag2 = list.stream()
.anyMatch((s) -> s.equals(Status.BUSY));
System.out.println(flag2);
boolean flag3 = list.stream()
.noneMatch((s) -> s.equals(Status.BUSY));
System.out.println(flag3);
// 避免空指针异常
Optional<Status> op1 = list.stream()
.findFirst();
// 如果Optional为空 找一个替代的对象
Status s1 = op1.orElse(Status.BUSY);
System.out.println(s1);
Optional<Status> op2 = list.stream()
.findAny();
System.out.println(op2);
long count = list.stream()
.count();
System.out.println(count);
}
(2)归约
备注:map 和 reduce 的连接通常称为map-reduce 模式,因 Google 用它来进行网络搜索而出名。
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来