前言
早已仰慕 Stream 流久已,终于有机会彻底的了解其特性以及用法了,关于源码的深度理解可能还需要继续增加功底,在学 Stream 的时候,同时认识了强大的 Optional,奈斯!
接下来的博文直接上用法了,想了解更多,建议阅读以下两篇博文:
一文带你入门Java Stream流,太强了
Java 8 Optional 最佳指南
Stream
流的操作可以分为两种类型:
1)中间操作,可以有多个,每次返回一个新的流,可进行链式操作。
2)终端操作,只能有一个,每次执行完,这个流也就用光光了,无法执行下一个操作,因此只能放在最后。
中间操作不会立即执行,只有等到终端操作的时候,流才开始真正地遍历,用于映射、过滤等。通俗点说,就是一次遍历执行多个操作,性能就大大提高了。
0x1. 创建流
如果是数组的话,可以使用 Arrays.stream()
或者 Stream.of()
创建流;如果是集合的话,可以直接使用 stream()
方法创建流,因为该方法已经添加到 Collection
接口中。
public static void main(String[] args) {
String[] arr = new String[]{"a", "b", "c"};
Stream<String> stream = Arrays.stream(arr);
stream = Stream.of("a", "b", "c");
List<String> list = new ArrayList<>();
list.add("a");
list.add("b");
list.add("c");
stream = list.stream();
}
查看 Stream 源码的话,你会发现 of()
方法内部其实调用了 Arrays.stream()
方法:
public static<T> Stream<T> of(T... values) {
return Arrays.stream(values);
}
另外,集合还可以调用 parallelStream()
方法创建并发流,默认使用的是 ForkJoinPool.commonPool()
线程池。
List<Long> aList = new ArrayList<>();
Stream<Long> parallelStream = aList.parallelStream();
0x2. 操作流
0x2.1. 过滤
通过 filter()
方法可以从流中筛选出我们想要的元素。
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("周杰伦");
list.add("王力宏");
list.add("陶喆");
list.add("林俊杰");
Stream<String> stream = list.stream().filter(element -> element.contains("王"));
stream.forEach(System.out::println);
}
运行结果:
代码语言:javascript复制王力宏
0x2.2. 映射
如果想通过某种操作把一个流中的元素转化成新的流中的元素,可以使用 map()
方法。
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("周杰伦");
list.add("王力宏");
list.add("陶喆");
list.add("林俊杰");
Stream<Integer> stream = list.stream().map(String::length);
stream.forEach(System.out::println);
}
map()
方法接收的是一个 Function(Java 8 新增的一个函数式接口,接受一个输入参数 T,返回一个结果 R)类型的参数,此时参数 为 String 类的 length
方法,也就是把 Stream<String>
的流转成一个 Stream<Integer>
的流。
运行结果:
代码语言:javascript复制3
3
2
3
0x2.3. 匹配
Stream 类提供了三个方法可供进行元素匹配,它们分别是:
-
anyMatch()
,只要有一个元素匹配传入的条件,就返回true
。 -
allMatch()
,只有有一个元素不匹配传入的条件,就返回false
;如果全部匹配,则返回true
。 -
noneMatch()
,只要有一个元素匹配传入的条件,就返回false
;如果全部不匹配,则返回true
。
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("周杰伦");
list.add("王力宏");
list.add("陶喆");
list.add("林俊杰");
boolean anyMatchFlag = list.stream().anyMatch(element -> element.contains("王"));
boolean allMatchFlag = list.stream().allMatch(element -> element.length() > 1);
boolean noneMatchFlag = list.stream().noneMatch(element -> element.endsWith("沉"));
System.out.println(anyMatchFlag);
System.out.println(allMatchFlag);
System.out.println(noneMatchFlag);
}
运行结果:
代码语言:javascript复制true
true
true
0x2.4. 组合
reduce()
方法的主要作用是把 Stream 中的元素组合起来,它有两种用法:
Optional<T> reduce(BinaryOperator<T> accumulator)
没有起始值,只有一个参数,就是运算规则,此时返回 Optional
。
T reduce(T identity, BinaryOperator<T> accumulator)
有起始值,有运算规则,两个参数,此时返回的类型和起始值类型一致。
public static void main(String[] args) {
Integer[] ints = {0, 1, 2, 3};
List<Integer> list = Arrays.asList(ints);
Optional<Integer> optional = list.stream().reduce((a, b) -> a b);
Optional<Integer> optional1 = list.stream().reduce(Integer::sum);
System.out.println(optional.orElse(0));
System.out.println(optional1.orElse(0));
int reduce = list.stream().reduce(6, (a, b) -> a b);
System.out.println(reduce);
int reduce1 = list.stream().reduce(6, Integer::sum);
System.out.println(reduce1);
}
orElse()
方法用于返回包裹在 Optional
对象中的值,如果该值不为 null,则返回;否则返回默认值(上述代码的默认值就是0)。该方法的参数类型和值得类型一致。
运行结果:
代码语言:javascript复制6
6
12
12
0x3. 转换流
既然可以把集合或者数组转成流,那么也应该有对应的方法,将流转换回去——collect()
方法就满足了这种需求。
public static void main(String[] args) {
List<String> list = new ArrayList<>();
list.add("周杰伦");
list.add("王力宏");
list.add("陶喆");
list.add("林俊杰");
String[] strArray = list.stream().toArray(String[]::new);
System.out.println(Arrays.toString(strArray));
List<Integer> list1 = list.stream().map(String::length).collect(Collectors.toList());
List<String> list2 = list.stream().collect(Collectors.toCollection(ArrayList::new));
System.out.println(list1);
System.out.println(list2);
String str = list.stream().collect(Collectors.joining(", ")).toString();
System.out.println(str);
}
Collectors
是一个收集器的工具类,内置了一系列收集器实现,比如说 toList()
方法将元素收集到一个新的 java.util.List
中;比如说 toCollection()
方法将元素收集到一个新的 java.util.ArrayList
中;比如说 joining()
方法将元素收集到一个可以用分隔符指定的字符串中。
运行结果:
代码语言:javascript复制[周杰伦, 王力宏, 陶喆, 林俊杰]
[3, 3, 2, 3]
[周杰伦, 王力宏, 陶喆, 林俊杰]
周杰伦, 王力宏, 陶喆, 林俊杰
0x4. 完整实例
代码语言:javascript复制import java.util.ArrayList;
import java.util.Arrays;
import java.util.IntSummaryStatistics;
import java.util.List;
import java.util.Random;
import java.util.stream.Collectors;
import java.util.Map;
public class Java8Tester {
public static void main(String args[]){
// 计算空字符串
List<String> strings = Arrays.asList("abc", "", "bc", "efg", "abcd","", "jkl");
System.out.println("列表: " strings);
count = strings.stream().filter(string->string.isEmpty()).count();
System.out.println("空字符串数量为: " count);
count = strings.stream().filter(string -> string.length() == 3).count();
System.out.println("字符串长度为 3 的数量为: " count);
filtered = strings.stream().filter(string ->!string.isEmpty()).collect(Collectors.toList());
System.out.println("筛选后的列表: " filtered);
mergedString = strings.stream().filter(string ->!string.isEmpty()).collect(Collectors.joining(", "));
System.out.println("合并字符串: " mergedString);
squaresList = numbers.stream().map( i ->i*i).distinct().collect(Collectors.toList());
System.out.println("Squares List: " squaresList);
System.out.println("列表: " integers);
IntSummaryStatistics stats = integers.stream().mapToInt((x) ->x).summaryStatistics();
System.out.println("列表中最大的数 : " stats.getMax());
System.out.println("列表中最小的数 : " stats.getMin());
System.out.println("所有数之和 : " stats.getSum());
System.out.println("平均数 : " stats.getAverage());
System.out.println("随机数: ");
random.ints().limit(10).sorted().forEach(System.out::println);
// 并行处理
count = strings.parallelStream().filter(string -> string.isEmpty()).count();
System.out.println("空字符串的数量为: " count);
}
}
运行结果:
代码语言:javascript复制列表: [abc, , bc, efg, abcd, , jkl]
空字符串数量为: 2
字符串长度为 3 的数量为: 3
筛选后的列表: [abc, bc, efg, abcd, jkl]
合并字符串: abc, bc, efg, abcd, jkl
Squares List: [9, 4, 49, 25]
列表: [1, 2, 13, 4, 15, 6, 17, 8, 19]
列表中最大的数 : 19
列表中最小的数 : 1
所有数之和 : 85
平均数 : 9.444444444444445
随机数:
-1743813696
-1301974944
-1299484995
-779981186
136544902
555792023
1243315896
1264920849
1472077135
1706423674
空字符串的数量为: 2
Optional
Optional 提供了一种用于表示可选值而非空引用的类级别解决方案;
0x1. 创建 Optional 对象
1)可以使用静态方法 empty()
创建一个空的 Optional 对象:
Optional<String> empty = Optional.empty();
System.out.println(empty); // 输出:Optional.empty
2)可以使用静态方法 of()
创建一个非空的 Optional 对象
Optional<String> opt = Optional.of("id10t.");
System.out.println(opt); // 输出:Optional[id10t.]
当然了,传递给 of()
方法的参数必须是非空的,也就是说不能为 null
,否则仍然会抛出 NullPointerException
。
3)可以使用静态方法 ofNullable()
创建一个即可空又可非空的 Optional 对象:
String name = "id10t.";
Optional<String> optOrNull = Optional.ofNullable(name);
System.out.println(optOrNull); // 输出:Optional[id10t.]
optOrNull = Optional.ofNullable(null);
System.out.println(optOrNull); // 输出:Optional.empty
ofNullable()
方法内部有一个三元表达式,如果为参数为 null
,则返回私有常量 EMPTY
;否则使用 new
关键字创建了一个新的 Optional 对象——不会再抛出 NPE 异常了。
0x2. 判断值是否存在
可以通过方法 isPresent()
判断一个 Optional 对象是否存在,如果存在,该方法返回 true
,否则返回 false
,取代了 obj != null
的判断。
Optional<String> opt = Optional.of("id10t.");
System.out.println(opt.isPresent()); // 输出:true
Optional<String> optOrNull = Optional.ofNullable(null);
System.out.println(opt.isPresent()); // 输出:false
Java 11 后还可以通过方法 isEmpty()
判断与 isPresent()
相反的结果。
0x3. 非空表达式
Optional 类有一个非常现代化的方法 ifPresent()
,允许我们使用函数式编程的方式执行一些代码,因此,我把它称为非空表达式。
Optional<String> opt = Optional.of("id10t.");
opt.ifPresent(str -> System.out.println(str.length()));
Java 9 后还可以通过方法 ifPresentOrElse(action, emptyAction)
执行两种结果,非空时执行 action
,空时执行 emptyAction
。
Optional<String> opt = Optional.ofNullable("id10t.");
// Optional<String> opt = Optional.ofNullable(null);
opt.ifPresentOrElse(
str -> System.out.println(str.length()),
() -> System.out.println("null"));
0x4. 设置(获取)默认值
有时候,我们在创建(获取) Optional 对象的时候,需要一个默认值,orElse()
和 orElseGet()
方法就派上用场了。
orElse()
方法用于返回包裹在 Optional 对象中的值,如果该值不为 null
,则返回;否则返回默认值。该方法的参数类型和值得类型一致。
String nullName = null;
String name = Optional.ofNullable(nullName).orElse("id10t.");
System.out.println(name); // 输出:id10t.
orElseGet()
方法与 orElse()
方法类似,但参数类型不同。如果 Optional 对象中的值为 null
,则执行参数中的函数。
String nullName = null;
String name = Optional.ofNullable(nullName).orElseGet(()->"id10t.");
System.out.println(name); // 输出:id10t.
从输出结果以及代码的形式上来看,这两个方法极其相似,这不免引起我们的怀疑,Java 类库的设计者有必要这样做吗?
当 Optional 对象的值不为 null 时:
代码语言:javascript复制public class A {
public static void main(String[] args) {
String name = "id10t.";
System.out.println("orElse");
String name2 = Optional.ofNullable(name).orElse(getDefaultValue());
System.out.println("orElseGet");
String name3 = Optional.ofNullable(name).orElseGet(A::getDefaultValue);
}
public static String getDefaultValue() {
System.out.println("getDefaultValue");
return "idiot";
}
}
运行结果:
代码语言:javascript复制orElse
getDefaultValue
orElseGet
orElseGet()
没有去调用 getDefaultValue()
,性能更佳;
0x5. 过滤值
filter()
方法的参数类型为 Predicate(Java 8 新增的一个函数式接口),也就是说可以将一个 Lambda 表达式传递给该方法作为条件,如果表达式的结果为 false
,则返回一个 EMPTY
的 Optional 对象,否则返回过滤后的 Optional 对象。
public static void main(String[] args) {
String password = "123456";
Optional<String> opt = Optional.ofNullable(password);
System.out.println(opt.filter(pwd -> pwd.length() > 6).isPresent());
}
进阶:
代码语言:javascript复制Predicate<String> len6 = pwd -> pwd.length() > 6;
Predicate<String> len10 = pwd -> pwd.length() < 10;
password = "1234567";
opt = Optional.ofNullable(password);
boolean result = opt.filter(len6.and(len10)).isPresent();
System.out.println(result);
0x6. 转换值
map()
方法,该方法可以按照一定的规则将原有 Optional 对象转换为一个新的 Optional 对象,原有的 Optional 对象不会更改。
public staticvoid main(String[] args) {
String name = "id10t.";
Optional<String> nameOptional = Optional.of(name);
Optional<Integer> intOpt = nameOptional
.map(String::length);
System.out.println(intOpt.orElse(0));
}
进阶:
代码语言:javascript复制public static void main(String[] args) {
String password = "password";
Optional<String> opt = Optional.ofNullable(password);
Predicate<String> len6 = pwd -> pwd.length() > 6;
Predicate<String> len10 = pwd -> pwd.length() < 10;
Predicate<String> eq = pwd -> pwd.equals("password");
boolean result = opt.map(String::toLowerCase).filter(len6.and(len10 ).and(eq)).isPresent();
System.out.println(result);
}
后记
感谢大佬沉默王二的文章,参考链接如下:
一文带你入门Java Stream流,太强了
Java 8 Optional 最佳指南
现在就处于查漏补缺的状态,缺啥补啥,夯实基础!