文章目录
09 Java8新特性
- 速度更快(如HashMap底层加入红黑树)
- 代码更少(Lambda表达式)
- 便于并行(Stream API)
- 最大化减少空指针异常(Optional)
- Nashorn引擎,允许在JVM上运行JS应用
9-1 Lambda表达式
lambda表达式应用举例:
(o1, o2) -> Integer.compare(o1, o2)
- ->: lambda操作符
- (o1, o2): lambda形参列表(即接口中抽象方法的形参列表)
- Integer.compare(o1, o2): lambda体(即重写抽象方法的方法体)
lambda表达式的本质:作为接口的一个实例,此接口需要为函数式接口。
lambda表达式基本格式:
@Test
public void testLambda() {
//语法格式一:无形参,无返回值
Runnable r = () -> System.out.println("This is a test for lambda function");
r.run();
//语法格式二:Lambda需要一个参数,但无返回值
Consumer<String> con = (String t) -> System.out.println(t);
con.accept("What is the difference between an oath and a lie?");
//语法格式三: 数据类型可以省略,因为可由编译器推断得出,称为“类型推断”
Consumer<String> con1 = (t) -> System.out.println(t);
con1.accept("The former who says it believes it's real and the latter who listens to do.");
//语法格式四:Lambda只需要一个参数时,参数列表的小括号可以省略
Consumer<String> con2 = t -> System.out.println(t);
con2.accept("The former who says it believes it's real and the latter who listens to do.");
//语法格式五:Lambda需要两个或以上的参数,多条执行语句,并且可以有返回值
Comparator<Integer> com = (o1, o2) -> {
System.out.println(o1);
System.out.println(o2);
return o1.compareTo(o2);
};
System.out.println(com.compare(12, 21));
//语法格式六:当Lambda体只有一条执行语句时,return与大括号可以省
Comparator<Integer> com1 = (o1, o2) -> -o1.compareTo(o2);
System.out.println(com1.compare(12, 21));
}
9-2 Functional函数式接口
如果一个接口中只声明了一个抽象方法,则此接口就称为函数式接口。
我们可以通过Lambda表达式来创建该接口的对象。如果lambda表达式抛出一个非运行时异常,那么该异常需要在目标接口的抽象方法上进行声明。
可以在一个接口上使用@FunctionalInterface注解,这样做可以检查它是否是一个函数式接口。
java.util.function包下定义了Java8的丰富的函数式接口。
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> | T | boolean | 确定类型为T的对象是否满足某约束 | boolean test(T t) |
public class FunInterfaceTest {
@Test
public void test1() {
happyTime(500, new Consumer<Double>() {
@Override
public void accept(Double aDouble) {
System.out.println("Let's have some fun and spend the " + aDouble + " dollars");
}
});
System.out.println("*****************");
happyTime(400, money -> System.out.println("Let's have some fun and spend the " + money + " dollars"));
}
public void happyTime(double money, Consumer<Double> con) {
con.accept(money);
}
@Test
public void test2() {
List<String> strList = Arrays.asList("AB", "AC", "CD", "DS");
ArrayList<String> c = filterString(strList, s -> s.contains("C"));
System.out.println(c);
}
//根据给定的规则过滤集合中的字符串
public ArrayList<String> filterString(List<String> list, Predicate<String> pre) {
ArrayList<String> filtered = new ArrayList<>();
for (String s: list) {
if (pre.test(s)) {
filtered.add(s);
}
}
return filtered;
}
}
9-3 方法引用与构造器引用
方法引用:当要传递给lambda体的操作,已经有实现的方法了,亦可使用方法引用。方法引用可以看作是Lambda表达式深层次的表达。
换句话说,方法引用就是lambda表达式,也就是函数式接口的一个实例,通过方法的名字来指向一个方法,
可以认为是lambda表达式的一个语法糖。
方法引用的使用情境:
- 对象::实例方法(非静态方法)
- 类::静态方法
- 类::实例方法(非静态方法)
public class MethodRefTest {
/**
* 方式一
* 对象::实例方法
*/
@Test
public void test1() {
Consumer<String> con1 = str -> System.out.println(str);
con1.accept("General lambda test here");
System.out.println("*********");
Consumer<String> con2 = System.out::println;
con2.accept("Method reference test here");
}
@Test
public void test2() {
Person p = new Person("Tom", 12);
Supplier<String> sup1 = () -> p.getName();
System.out.println(sup1.get());
Supplier<String> sup2 = p::getName;
System.out.println(sup2.get());
}
/**
* 方式二:
* 类::静态方法
*/
@Test
public void test3() {
Comparator<Integer> com = Integer::compare;
System.out.println(com.compare(12, 21));
System.out.println("******************");
Function<Double, Long> func1 = d -> Math.round(d);
System.out.println(func1.apply(5.31));
System.out.println("******************");
Function<Double, Long> func2 = Math::round;
System.out.println(func2.apply(6.58));
}
/**
* 方式三
* 类::实例方法(非静态方法)
*
* 接口: Comparator
* 重写的抽象方法: int compare(T t1, T t2)
* 引用的方法: String中的实例方法 int t1.compareTo(t2)
*/
@Test
public void test4() {
Comparator<String> com1 = (s1, s2) -> s1.compareTo(s2);
System.out.println(com1.compare("abc", "abd"));
System.out.println("*****************");
Comparator<String> com2 = String::compareTo;
System.out.println(com2.compare("abc", "abe"));
}
/**
* 方式三
* 类::实例方法(非静态方法)
*
* 接口: BiPredicate
* 重写的抽象方法: test(T t1, T t2)
* 引用的方法: String中的实例方法 boolean t1.equals(t2)
*/
@Test
public void test5() {
String str1 = "abc";
String str2 = new String("abc");
System.out.println(str1 == str2);//false
BiPredicate<String, String> pre1 = (s1, s2) -> s1.equals(s2);
System.out.println(pre1.test(str1, str2));//true
System.out.println("*************");
BiPredicate<String, String> pre2 = String::equals;
System.out.println(pre2.test(str1, str2));//true
}
}
构造器引用:和方法引用类似,函数式接口的抽象方法的形参列表和构造器的形参列表一致;抽象方法的返回值类型即为构造器所属的类。
Class::new
数组也可以被看作一个特殊的类,因此用构造器引用又引出了数组引用。数据引用的方式与构造器引用相同,在对象类型处用"基本数据类型[]"替换即可。
int[]::new
/**
* 构造器引用
* 接口: BiFunction
* 抽象方法: R apply(T t, U u)
* 返回值: new Person
*/
@Test
public void test() {
BiFunction<String, Integer, Person> func1 = (name, age) -> new Person(name, age);
System.out.println(func1.apply("Tom", 12));
System.out.println("******************");
BiFunction<String, Integer, Person> func2 = Person::new;
System.out.println(func2.apply("Alice", 15));
}
9-4 Stream API
Stream API(java.util.stream)是Java8中处理集合的关键抽象概念,它可以指定你希望对集合进行的操作,可以执行非常复杂的查找、过滤和映射数据等操作。使用Stream API对集合数据进行操作,就类似于使用SQL执行的数据库查询。
Stream API把真正的函数式编程风格引入到Java中,这是目前为止对Java类库最好的补充。
为什么要使用Stream API:
- 实际开发中,项目中多数数据源都来自于MySQL/Oracle等;
但目前的数据源更多,有MongDB/Redis等,而这些NoSQL的数据就需要在Java层面进行处理。 - Stream和Collection的区别:
- Collection是一种静态的内存数据结构,而Stream是有相关计算的。
- 前者主要是面向内存,存储在内存中;后者主要是面向CPU,通过CPU计算实现。
Stream执行流程:
- 创建Stream: 从数据源获取一个流
- 中间操作: 对数据源的数据进行处理
- 终止操作: 一旦执行终止操作,就执行中间操作链,并产生结果
1. 创建Stream
- 方式一:通过Collection创建
- default Stream stream(): 返回一个顺序流
- default Stream parallelStream(): 返回一个并行流
- 方式二:通过Arrays中的静态方法stream()
- static Stream stream(T[] arr)
- 方式三:通过Stream的of()方法
- 方式四:创建无限流
- 迭代 - public static Stream iterate(final T seed, final UnaryOperator f)
- 生成 - public static Stream generate(Supplier s)
public class StreamAPITest {
//1. 创建Stream: 从数据源获取一个流
@Test
public void test() {
//方式一:通过Collection创建
List<Integer> list = Arrays.asList(5, 2, 3, 10, 21);
Stream<Integer> stream = list.stream();//返回一个顺序流
Stream<Integer> paraStream = list.parallelStream();//返回一个并行流
//方式二:通过Arrays中的静态方法stream()
int[] arr = new int[]{5, 2, 3, 10, 21};
IntStream stream1 = Arrays.stream(arr);
//方式三:通过Stream的of()方法
Stream<Integer> stream2 = Stream.of(5, 2, 3, 10, 21);
//方式四:创建无限流
//例1:遍历前十个偶数
Stream.iterate(0, t -> t + 2).limit(10).forEach(System.out::println);//迭代
//例2:生成随机数
Stream.generate(Math::random).limit(10).forEach(System.out::println);//生成
}
}
2. 中间操作
- 筛选与切片
- 映射
- 排序
| method | description |
|---|---|
| 筛选与切片 | |
| filter(Predicate p) | 接收Lambda,从流中排除某些元素 |
| limit(n) | 截断流,使其元素数量不超过n |
| skip(n) | 跳过元素,返回一个扔掉了前n个元素的流 |
| distinct() | 筛选,通过流所生成元素的hashCode()和equals()去除重复元素 |
| 映射 | |
| map(Function f) | 接收一个函数作为参数,返回映射后的值 |
| flatMap(Function f) | 接收一个函数作为参数,将流中的每个值都换成另一个流,然后把所有的流返回成一个流 |
| 排序 | |
| sorted() | 产生一个新流,其中按自然顺序排序 |
| sorted(Comparator com) | 产生一个新流,其中按比较器顺序进行排序 |
应用示例:
public class StreamAPITest {
//筛选与切片
@Test
public void test1() {
Stream<Integer> stream = Stream.iterate(0, t -> t + 2).limit(10);
//输出前20中大于10的偶数
stream.filter(i -> i >10).forEach(System.out::println);
System.out.println("***************");
//输出前3个偶数
Stream.iterate(0, t -> t + 2).limit(3).forEach(System.out::println);
System.out.println("***************");
//输出跳过前3个之后的流
Stream.iterate(0, t -> t + 2).limit(10).skip(3).forEach(System.out::println);
System.out.println("***************");
//去除重复元素
Stream<Integer> stream1 = Arrays.asList(5, 6, 5, 15, 98, 6, 32, 5).stream();
stream1.distinct().forEach(System.out::println);
}
//映射
@Test
public void test2() {
List<String> strList = Arrays.asList("alice", "tom", "jack", "octavia", "louis");
strList.stream().map(str -> str.toUpperCase()).forEach(System.out::println);
System.out.println("********************");
//嵌套Stream的输出
Stream<Stream<Character>> streamStream = strList.stream().map(StreamAPITest::fromStringToString);
//方式一:嵌套输出
streamStream.forEach(s -> s.filter(c -> c > 100).forEach(System.out::println));
System.out.println("********************");
//方式二:使用flatMap
Stream<Character> characterStream = strList.stream().flatMap(StreamAPITest::fromStringToString);
characterStream.filter(c -> c > 100).forEach(System.out::println);
}
//自定义静态方法:将字符串中多个字符构成的集合转换为对应的Stream实例
public static Stream<Character> fromStringToString(String str) {
ArrayList<Character> list = new ArrayList<>();
for (Character c: str.toCharArray()) {
list.add(c);
}
return list.stream();
}
//排序
@Test
public void test3() {
List<String> strList = Arrays.asList("alice", "tom", "jack", "octavia", "louis");
//自然排序
strList.stream().sorted().forEach(System.out::println);
System.out.println("******************");
//比较器排序
strList.stream().sorted(Comparator.comparingInt(o -> o.charAt(o.length() - 1))).forEach(System.out::println);
}
}
3. 终止操作
- 匹配与查找
- 归约(reduce)
- 收集(collect)
| method | description |
|---|---|
| 匹配与查找 | |
| allMatch(Predicate p) | 检查是否匹配所有元素 |
| anyMatch(Predicate p) | 检查是否至少匹配一个元素 |
| noneMatch(Predicate p) | 检查是否没有匹配所有元素 |
| findFirst() | 返回第一个元素 |
| findAny() | 返回当前流中任意元素 |
| count() | 返回流中元素的总个数 |
| max(Comparator c) | 返回比较器定义的最大值 |
| min(Comparator c) | 返回比较器定义的最小值 |
| forEach(Consumer c) | 内部迭代 |
| 归约(reduce) | |
| reduce(T identity, BinaryOperator) | 可以将流中的元素反复结合起来,得到一个值 |
| 收集(collect) | |
| collect(Collector c) | 将流转换为其他形式(Collectors类中定义了很多静态方法获取Collector) |
归约和收集示例:
public void StreamAPITest {
//归约(reduce)
@Test
public void test1() {
//练习1:计算1-10自然数的和
Optional<Integer> reduce = Stream.iterate(1, t -> t + 1).limit(10).reduce(Integer::sum);
System.out.println(reduce);
//练习2:计算所有姓名中字母a出现的次数
List<String> strList = Arrays.asList("alice", "tom", "jack", "octavia", "louis");
Optional<Integer> reduce1 = strList.stream().map(StreamAPITest::countLetter).reduce(Integer::sum);
System.out.println(reduce1);
}
public static int countLetter(String str) {
int count = 0;
char[] chars = str.toCharArray();
for (Character c: chars) {
if (c == 'a') {
count++;
}
}
return count;
}
//收集(collect)
@Test
public void test2() {
//练习3:将长度大于4的名字返回为一个Set
List<String> strList = Arrays.asList("alice", "tom", "jack", "octavia", "louis", "alice");
Set<String> collect = strList.stream().filter(str -> str.length() > 4).collect(Collectors.toSet());
System.out.println(collect);//[alice, louis, octavia]
}
}
9-5 Optional类
空指针异常是导致Java应用程序失败的最常见的原因。为了解决空指针异常,Google的Guava项目引入了Optional类,它通过使用检查空值的方式来防止代码污染。
Optional<T>类(java.util.Optional)是一个容器类,它可以保存类型T的值,代表这个值存在,或者仅仅保存null,表示这个值不存在。原来用null表示一个值不存在,现在Optional可以更好地表达这个概念,同时也可避免空指针异常。
Optional类常用方法:
| method | description |
|---|---|
| 创建Optional对象 | |
| Optional.of(T t) | 创建一个Optional实例,t必须非空 |
| Optional.empty() | 创建一个空的Optional实例 |
| Optional.ofNullable(T t) | 创建一个Optional实例,t可以为null |
| 判断Optional容器中是否包含对象 | |
| boolean isPresent() | 判断是否包含对象 |
| void ifPresent(Consumer<? super T> consumer) | 如果有值,就执行Consumer接口实现的代码 |
| 获取Optional容器的对象 | |
| T get() | 如果调用对象非空,则返回该值,否则抛出异常 |
| T orElse(T other) | 如果调用对象非空,则返回该值,否则返回指定的other对象 |
| T orElseGet(Supplier<? extends T> other) | 如果调用对象非空,则返回该值,否则返回Supplier接口实现提供的对象 |
| T orElseThrow(Supplier<? extends X> exceptionSupplier) | 如果调用对象非空,则返回该值,否则抛出异常 |
本文详细介绍了Java8的重要新特性,包括Lambda表达式、函数式接口、方法引用、Stream API和Optional类。深入探讨了这些特性的使用方法及优势,如简化代码、提高程序效率、方便并行处理等。
770

被折叠的 条评论
为什么被折叠?



