抱歉,您的浏览器无法访问本站
本页面需要浏览器支持(启用)JavaScript
了解详情 >

摘要:本文学习了流式处理,使用新增方法操作集合,以及通过Optional类判断空值。

环境

Windows 10 企业版 LTSC 21H2
Java 1.8

1 简介

1.1 定义

从JDK1.8开始,新增了一系列Stream相关的API用于流式处理,这些API都在java.util.stream包下,通过Stream可以编写函数式编程风格的代码,使代码更加高效简洁。

1.2 操作方式

流式处理可以分为三个部分:

  • 转换成流:将原始数据转换成流对象,以便进行后续操作。
  • 中间操作:将原始的Stream经过逻辑处理,生成新的Stream用于继续处理。
  • 终止操作:产生结果或者执行其他操作。

2 使用

2.1 转换成流

创建空的Stream对象:

java
1
Stream stream = Stream.empty();

通过集合类中的stream()方法或者parallelStream()方法创建Stream对象:

java
1
2
3
List<String> list = Arrays.asList("a", "b", "c", "d");
Stream stream = list.stream();// 获取串行的Stream对象
Stream parallelStream = list.parallelStream();// 获取并行的Stream对象

通过数组工具类Arrays类的stream()方法创建Stream对象:

java
1
2
String[] arr = {"a", "b", "c", "d"};
Stream<String> stream = Arrays.stream(arr);

通过Stream类中的of()方法创建Stream对象:

java
1
2
Stream stream = Stream.of("test");
Stream stream = Stream.of("a", "b", "c");

通过Stream类中的iterate()方法创建有序的Stream对象:

java
1
public static<T> Stream<T> iterate(final T seed, final UnaryOperator<T> f);

通过Stream类中的generate()方法创建无序的Stream对象:

java
1
public static<T> Stream<T> generate(Supplier<T> s);

2.2 中间操作

多个中间操作可以连接起来形成流水线,在终止操作时进行处理并返回结果,称为惰性求值,是函数式编程中的一种策略。

2.2.1 过滤

对Stream对象按指定的Predicate进行筛选,将符合条件的元素组成新的Stream对象返回。

语法:

java
1
Stream<T> filter(Predicate<? super T> predicate);

示例:

java
1
2
3
Stream<Integer> stream = Stream.of(1, 8, 5, 8).filter(i -> i > 5);
// 为了看到效果,使用终止操作forEach()方法进行打印
stream.forEach(e -> System.out.println(e));

结果:

java
1
2
8
8

2.2.2 截取

获取指定的前几个元素组成新的Stream对象返回。

语法:

java
1
Stream<T> limit(long maxSize);

示例:

java
1
2
3
Stream<Integer> stream = Stream.of(1, 8, 5, 8).limit(2);
// 为了看到效果,使用终止操作forEach()方法进行打印
stream.forEach(e -> System.out.println(e));

结果:

java
1
2
1
8

2.2.3 跳过

跳过指定的前几个元素,将剩下的元素组成新的Stream对象返回。

语法:

java
1
Stream<T> skip(long n);

示例:

java
1
2
3
Stream<Integer> stream = Stream.of(1, 8, 5, 8).skip(2);
// 为了看到效果,使用终止操作forEach()方法进行打印
stream.forEach(e -> System.out.println(e));

结果:

java
1
2
5
8

2.2.4 去重

调用元素的equals()方法进行比较,将去重后的元素组成新的Stream对象返回。

语法:

java
1
Stream<T> distinct();

示例:

java
1
2
3
Stream<Integer> stream = Stream.of(1, 8, 5, 8).distinct();
// 为了看到效果,使用终止操作forEach()方法进行打印
stream.forEach(e -> System.out.println(e));

结果:

java
1
2
3
1
8
5

2.2.5 排序

调用传入比较器进行比较,将排序后的元素组成新的Stream对象返回。

语法:

java
1
Stream<T> sorted(Comparator<? super T> comparator);

示例:

java
1
2
3
Stream<Integer> stream = Stream.of(1, 8, 5, 8).sorted((m, n) -> m - n);
// 为了看到效果,使用终止操作forEach()方法进行打印
stream.forEach(e -> System.out.println(e));

结果:

java
1
2
3
4
1
5
8
8

2.2.6 映射

将Stream中的元素转换成其他元素或者提取信息,调用传入的函数处理每个元素,将处理后的元素组成新的Stream对象返回。

语法:

java
1
<R> Stream<R> map(Function<? super T, ? extends R> mapper);

示例:

java
1
2
3
Stream<Integer> stream = Stream.of(1, 8, 5, 8).map(i -> i + 1);
// 为了看到效果,使用了终止操作forEach()方法进行打印
stream.forEach(e -> System.out.println(e));

结果:

java
1
2
3
4
2
9
6
9

2.2.7 扁平映射

将Stream中的元素转换成Stream对象,调用传入的函数处理每个Steam对象,将处理后的Stream对象组成新的Stream对象返回。

语法:

java
1
<R> Stream<R> flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);

示例:

java
1
2
3
Stream<String> stream = Stream.of("1 8", "5 8").flatMap(s -> Stream.of(s.split(" ")));
// 为了看到效果,使用了终止操作forEach()方法进行打印
stream.forEach(e -> System.out.println(e));

结果:

java
1
2
3
4
1
8
5
8

2.3 终止操作

终端操作会使用流水线生成结果,一旦执行终端操作,流就被使用使用了,无法再被使用。

2.3.1 遍历元素

语法:

java
1
void forEach(Consumer<? super T> action);

示例:

java
1
2
Stream<Integer> stream = Stream.of(1, 8, 5, 8);
stream.forEach(e -> System.out.println(e));

结果:

java
1
2
3
4
1
8
5
8

2.3.2 检查是否所有元素匹配

如果所有元素都满足条件,那么返回true,否则返回false。

语法:

java
1
boolean allMatch(Predicate<? super T> predicate);

示例:

java
1
2
boolean match = Stream.of("aa11", "bb11").allMatch(e -> e.contains("11"));
System.out.println(match);// true

2.3.3 检查是否任意元素匹配

如果任意元素满足条件,就返回true,否则返回false。

语法:

java
1
boolean anyMatch(Predicate<? super T> predicate);

示例:

java
1
2
boolean match = Stream.of("aa11", "bb11").anyMatch(e -> e.contains("aa"));
System.out.println(match);// true

2.3.4 检查是否没有元素匹配

如果没有元素满足条件,返回true,否则返回false。

语法:

java
1
boolean noneMatch(Predicate<? super T> predicate);

示例:

java
1
2
boolean match = Stream.of("aa11", "bb11").noneMatch(e -> e.contains("cc"));
System.out.println(match);// true

2.3.5 返回首个元素

返回首个元素,使用Optional对象封装。

语法:

java
1
Optional<T> findFirst();

示例:

java
1
2
Optional<String> find = Stream.of("aa11", "bb11").findFirst();
System.out.println(find);// Optional[aa11]

2.3.6 返回任意元素

返回任意元素,通常是处理最快的那个元素,使用Optional对象封装。

语法:

java
1
Optional<T> findAny();

示例:

java
1
2
Optional<String> find = Stream.of("aa11", "bb11").findAny();
System.out.println(find);// Optional[aa11]

2.3.7 返回元素个数

语法:

java
1
long count();

示例:

java
1
2
long count = Stream.of("aa11", "bb11").count();
System.out.println(count);// 2

2.3.8 返回元素最大值

语法:

java
1
Optional<T> max(Comparator<? super T> comparator);

示例:

java
1
2
Optional<Integer> max = Stream.of(1, 8, 5, 8).max((m, n) -> m - n);
System.out.println(max);// Optional[8]

2.3.9 返回元素最小值

语法:

java
1
Optional<T> min(Comparator<? super T> comparator);

示例:

java
1
2
Optional<Integer> min = Stream.of(1, 8, 5, 8).min((m, n) -> m - n);
System.out.println(min);// Optional[1]

2.3.10 收集元素

将流中的元素进行汇总,使用集合收集元素。

可以使用Collectors类提供的静态方法,将流中的元素通过集合收集。

语法:

java
1
<R, A> R collect(Collector<? super T, A, R> collector);

示例:

java
1
2
3
List<Integer> list = Arrays.asList(1, 8, 5, 8);
Set<Integer> set = list.stream().collect(Collectors.toSet());
System.out.println(set);// [1, 5, 8]

2.3.11 规约元素

将流中的元素反复结合,最终生成单一的结果。

语法:

java
1
2
T reduce(T identity, BinaryOperator<T> accumulator);
Optional<T> reduce(BinaryOperator<T> accumulator);

示例:

java
1
2
3
4
5
6
7
List<Integer> list = Arrays.asList(1, 8, 5, 8);
Optional<Integer> sum = list.stream().reduce((x, y) -> x + y);
System.out.println(sum);// Optional[22]
Integer sumWith0 = list.stream().reduce(0, (x, y) -> x + y);
System.out.println(sumWith0);// 22
Integer sumWith5 = list.stream().reduce(5, (x, y) -> x + y);
System.out.println(sumWith5);// 27

3 Optional类

为了避免产生NullPointException异常,通常的做法是进行if条件判断,但如果代码里面充满了大量的null判断会让程序变的不再优雅。

从JDK1.8开始,可以使用Optional类处理可能为null的变量,在减少NullPointException的同时,也提升了代码的美观度。

3.1 获取

创建空的Optional对象:

java
1
Optional<String> name = Optional.empty();

包装不为null的对象,如果对象为null,则抛出NullPointException异常:

java
1
Optional<String> name = Optional.of(str);

包装可以为null的对象,如果对象为null,则创建空的Optional对象:

java
1
Optional<String> name = Optional.ofNullable(str);

3.2 常用方法

3.2.1 判断是否为空

使用isPresent()方法判断Optional对象是否为空,如果为空则返回true,如果不为空则返回false。

示例:

java
1
2
Optional<String> name = Optional.ofNullable(null);
System.out.println(name.isPresent());// true

3.2.2 安全执行方法

使用ifPresent()方法安全的执行传入的方法,如果不为空则执行方法,如果为空则什么也不做。

示例:

java
1
2
Optional<String> name = Optional.ofNullable("name");
name.ifPresent(System.out::println);// name

3.2.3 获取值

使用get()方法获取Optional对象的值,如果不为空则返回值,如果为空则抛出NoSuchElementException异常。

示例:

java
1
2
Optional<String> name = Optional.ofNullable("name");
System.out.println(name.get());// name

3.2.4 获取值或默认值

使用orElse()方法获取Optional对象的值,如果不为空则返回值,如果为空则返回指定的值。

示例:

java
1
2
Optional<String> name = Optional.ofNullable(null);
System.out.println(name.orElse("null"));// null

3.2.5 获取值或执行方法

使用orElseGet()方法获取Optional对象的值,如果不为空则返回值,如果为空则返回执行方法得到的值。

示例:

java
1
2
Optional<String> name = Optional.ofNullable(null);
System.out.println(name.orElseGet(() -> "null"));// null

3.2.6 获取值或抛出异常

使用orElseThrow()方法获取Optional对象的值,如果不为空则返回值,如果为空则抛出指定的异常,默认抛出NoSuchElementException异常。

示例:

java
1
2
Optional<String> name = Optional.ofNullable(null);
System.out.println(name.orElseThrow(() -> new NullPointerException()));// java.lang.NullPointerException

3.2.7 过滤

使用filter()方法过滤Optional对象的值,判断是否满足传入的条件,如果满足则返回原来的Optional对象,如果不满足则创建空的Optional对象返回。

示例:

java
1
2
Optional<String> name = Optional.ofNullable("name").filter(e -> e != null);
System.out.println(name);// Optional[name]

3.2.8 映射

使用map()方法处理Optional对象的值,如果不为空则使用传入的方法处理并将返回值封装为Optional对象返回,如果为空则创建空的Optional对象返回。

示例:

java
1
2
Optional<Integer> length = Optional.ofNullable("name").map(e -> e.length());
System.out.println(length);// Optional[4]

3.2.9 扁平映射

使用flatMap()方法处理Optional对象的值,如果不为空则使用传入的方法处理并将Optional对象返回,如果为空则创建空的Optional对象返回。

示例:

java
1
2
Optional<Integer> length = Optional.ofNullable("name").flatMap(e -> Optional.ofNullable(e.length()));
System.out.println(length);// Optional[4]

3.3 注意事项

比较orElse()方法和orElseGet()方法:

  • 当Optional对象不为空时,返回的都是原对象。当Optional对象为空时,orElse()方法返回指定的值,orElseGet()方法返回的是接口调用的返回值。
  • 并且当Optional对象不为空时,orElse()方法也会执行,orElseGet()方法不会执行。如果两个方法都返回新对象,orElse()方法会执行初始化,orElseGet()方法不会执行初始化。

比较map()方法和flatMap()方法:

  • 如果Optional对象为空,都会创建空对象返回。
  • 如果Optional对象不为空,map()方法会将执行方法得到的返回值包装为Optional对象,flatMap()方法会直接返回执行方法得到的Optional对象。

4 集合增强

4.1 工具类

4.1.1 Collector

可以将Collector接口看做是用来处理流的工具,在Collectors类里面封装了很多Collector工具。

4.1.2 Collectors

Collectors类是一个提供了多种Collector接口的工具类。

4.1.2.1 集合

示例:

java
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
List<String> list = Arrays.asList("123", "521", "100", "228", "838", "250", "345");
System.out.println(list);// [123, 521, 100, 228, 838, 250, 345]
// 使用toCollection()方法收集到Collection返回
LinkedList<String> newCollection = list.stream().collect(Collectors.toCollection(LinkedList::new));
System.out.println(newCollection);// [123, 521, 100, 228, 838, 250, 345]
// 使用toList()方法收集到List返回,默认为ArrayList
List<String> newList = list.stream().collect(Collectors.toList());
System.out.println(newList);// [123, 521, 100, 228, 838, 250, 345]
// 使用toSet()方法收集到Set返回,默认为HashSet
Set<String> newSet = list.stream().collect(Collectors.toSet());
System.out.println(newSet);// [100, 123, 521, 345, 228, 838, 250]
// 使用toMap()方法收集到Map返回,主键冲突时默认抛出异常,默认为HashMap
Map<String, String> newMap = null;
// 传入主键冲突时的处理方法,保留先插入的值,默认对主键由小到大排序
newMap = list.stream().collect(Collectors.toMap(e -> e.substring(0, 1), e -> e, (m, n) -> m));
System.out.println(newMap);// {1=123, 2=228, 3=345, 5=521, 8=838}
// 传入主键冲突时的处理方法,保留后插入的值,设置对主键按照插入顺序排序
newMap = list.stream().collect(Collectors.toMap(e -> e.substring(0, 1), e -> e, (m, n) -> n, LinkedHashMap::new));
System.out.println(newMap);// {1=100, 5=521, 2=250, 8=838, 3=345}
4.1.2.2 拼接

示例:

java
1
2
3
4
5
6
7
8
9
10
List<String> list = Arrays.asList("123", "521", "100", "228", "838", "250", "345");
System.out.println(list);// [123, 521, 100, 228, 838, 250, 345]
// 使用joining()方法将流中的元素拼接为字符串,可以指定连接符,也可以指定前后缀
String str = null;
str = list.stream().collect(Collectors.joining());
System.out.println(str);// 123521100228838250345
str = list.stream().collect(Collectors.joining("-"));
System.out.println(str);// 123-521-100-228-838-250-345
str = list.stream().collect(Collectors.joining("-", "<", ">"));
System.out.println(str);// <123-521-100-228-838-250-345>
4.1.2.3 数学

示例:

java
1
2
3
4
5
6
7
8
9
10
11
List<String> list = Arrays.asList("123", "521", "100", "228", "838", "250", "345");
System.out.println(list);// [123, 521, 100, 228, 838, 250, 345]
// 使用counting()方法统计流中元素的个数
Long count = list.stream().collect(Collectors.counting());
System.out.println(count);// 7
// 使用maxBy()方法获取流中元素的最大值
Optional<String> max = list.stream().collect(Collectors.maxBy((m, n) -> Integer.valueOf(m) - Integer.valueOf(n)));
System.out.println(max);// Optional[838]
// 使用minBy()方法获取流中元素的最小值
Optional<String> min = list.stream().collect(Collectors.minBy((m, n) -> Integer.valueOf(m) - Integer.valueOf(n)));
System.out.println(min);// Optional[100]
4.1.2.4 映射

示例:

java
1
2
3
4
5
6
7
List<Score> scoreList = new ArrayList<Score>();
scoreList.add(new Score("201010", "张三"));
scoreList.add(new Score("201011", "李四"));
scoreList.add(new Score("201012", "王五"));
// 使用mapping()方法将流中的元素进行处理,按照指定格式返回结果
List<String> names = scoreList.stream().collect(Collectors.mapping(Score::getName, Collectors.toList()));
System.out.println(names);// [张三, 李四, 王五]
4.1.2.5 收集处理

示例:

java
1
2
3
4
5
List<String> list = Arrays.asList("123", "521", "100", "228", "838", "250", "345");
System.out.println(list);// [123, 521, 100, 228, 838, 250, 345]
// 使用collectingAndThen()方法对收集后的结构进行进一步处理
Integer size = list.stream().collect(Collectors.collectingAndThen(Collectors.toList(), List::size));
System.out.println(size);// 7
4.1.2.6 统计归纳

示例:

java
1
2
3
4
5
6
7
8
9
List<String> list = Arrays.asList("123", "521", "100", "228", "838", "250", "345");
System.out.println(list);// [123, 521, 100, 228, 838, 250, 345]
// 使用reducing()方法对流中的元素做统计归纳,有三个重载方法,和Stream里的三个方法对应,二者作用完全一致
Optional<Integer> sum = list.stream().map(String::length).collect(Collectors.reducing(Integer::sum));
System.out.println(sum);// Optional[21]
Integer sumWith0 = list.stream().map(String::length).collect(Collectors.reducing(0, Integer::sum));
System.out.println(sumWith0);// 21
Integer sumWith5 = list.stream().collect(Collectors.reducing(0, String::length, Integer::sum));
System.out.println(sumWith5);// 26
4.2.1.7 分组

示例:

java
1
2
3
4
5
6
7
8
9
List<String> list = Arrays.asList("123", "521", "100", "228", "838", "250", "345");
System.out.println(list);// [123, 521, 100, 228, 838, 250, 345]
// 使用groupingBy()方法按照规则将流中的数据分为多个组,有三个重载方法
Map<String, List<String>> groupByFirst = list.stream().collect(Collectors.groupingBy(e -> e.substring(0, 1)));
System.out.println(groupByFirst);// {1=[123, 100], 2=[228, 250], 3=[345], 5=[521], 8=[838]}
Map<String, Set<String>> groupByLast = list.stream().collect(Collectors.groupingBy(e -> e.substring(e.length() - 1), Collectors.toSet()));
System.out.println(groupByLast);// {0=[100, 250], 1=[521], 3=[123], 5=[345], 8=[228, 838]}
HashMap<Integer, Set<String>> groupByLength = list.stream().collect(Collectors.groupingBy(String::length, HashMap::new, Collectors.toSet()));
System.out.println(groupByLength);// {3=[100, 123, 521, 345, 228, 838, 250]}
4.1.2.8 分区

示例:

java
1
2
3
4
5
6
7
List<String> list = Arrays.asList("123", "521", "100", "228", "838", "250", "345");
System.out.println(list);// [123, 521, 100, 228, 838, 250, 345]
// 使用partitioningBy()方法按照规则将流中的数据分为两个区,有两个重载方法
Map<Boolean, List<String>> moreThan = list.stream().collect(Collectors.partitioningBy(e -> Integer.parseInt(e) > 300));
System.out.println(moreThan);// {false=[123, 100, 228, 250], true=[521, 838, 345]}
Map<Boolean, Set<String>> lessThan = list.stream().collect(Collectors.partitioningBy(e -> Integer.parseInt(e) < 300, Collectors.toSet()));
System.out.println(lessThan);// {false=[521, 345, 838], true=[100, 123, 228, 250]}

4.1 遍历集合

4.1.1 遍历List

示例:

java
1
2
3
4
5
6
List<String> list = new ArrayList<String>();
list.add("张三");
list.add("李四");
list.add("王五");
list.add("赵六");
list.forEach(e -> System.out.println(e));

4.1.2 遍历Set

示例:

java
1
2
3
4
5
6
Set<String> set = new HashSet<String>();
set.add("张三");
set.add("李四");
set.add("王五");
set.add("赵六");
set.forEach(e -> System.out.println(e));

4.1.3 遍历Map

示例:

java
1
2
3
4
5
6
Map<Integer, String> map = new HashMap<Integer, String>();
map.put(101, "张三");
map.put(102, "李四");
map.put(103, "王五");
map.put(104, "赵六");
map.forEach((key, value) -> System.out.println(key+"->"+value));

评论