文章目录
简要定义
官网简介:A sequence of elements supporting sequential and parallel aggregate operations
(支持顺序和并行聚合操作的元素序列)
To perform a computation, stream operations are composed into a stream pipeline.
A stream pipeline consists of a source (which might be an array, a collection, a generator function, an I/O channel, etc),
zero or more intermediate operations (which transform a stream into another stream, such as filter(Predicate)),
and a terminal operation (which produces a result or side-effect, such as count() or forEach(Consumer)).
Streams are lazy; computation on the source data is only performed when the terminal operation is initiated, and source elements are consumed only as needed.
stream流程分为3个步骤(单向):
- source,构建stream
- Intermediate,中间操作,可以有0个或者多个
- Terminal,结束操作,仅有一个
中间操作只是一种标记,只有结束操作才会触发实际计算(中间操作是懒惰的,也就是中间操作不会对数据做任何操作)
构建stream
可以由数组、集合等构建,举个栗子:
int[] array = new int[] {1,2,3,4};
Stream.of(array);
中间操作
可以有0个或者多个中间操作~
中间操作 分类:Stateless(无状态)和Stateful(有状态)。
- 无状态中间操作是指元素的处理不受前面元素的影响
- 有状态的中间操作必须等到所有元素处理之后才知道最终结果
状态 | 操作名称 | 说明 |
---|---|---|
无状态 | - | |
– | unordered() | |
– | filter() | |
– | map() | |
– | mapToInt() | |
– | mapToLong() | |
– | mapToDouble() | |
– | flatMap() | |
– | flatMapToInt() | |
– | flatMapToLong() | |
– | flatMapToDouble() | |
– | peek() | |
有状态 | ||
– | distinct() | |
– | sorted() | |
– | limit() | |
– | skip() |
结束操作
结束操作又可以分为短路操作(short-circuiting)和非短路操作。
状态 | 操作名称 | 说明 |
---|---|---|
非短路操作 | 不用处理全部元素就可以返回结果,比如找到第一个满足条件的元素 | |
– | forEach() | |
– | forEachOrdered() | |
– | toArray() | |
– | reduce() | |
– | collect() | |
– | max() | |
– | min() | |
– | count() | |
短路操作 | ||
– | anyMatch() | |
– | allMatch() | |
– | noneMatch() | |
– | findFirst() | |
– | findAny() |
简单实践
List转String(逗号分隔)
List<Integer> list = new ArrayList<>();
list.add(1);
list.add(2);
list.add(3);
String str = list.stream().map(String::valueOf).collect(Collectors.joining(","));
String数组转int数组
int[] array = Arrays.asList(strings).stream().mapToInt(Integer::parseInt).toArray();
逗号分割数字字符串转List
String commaStr = "1,2,3,4,5";
List<Integer> intList = Stream.of(commaStr.split(",")).mapToInt(e -> Integer.valueOf(e)).boxed().collect(Collectors.toList());
分解上面的步骤:
- 将
逗号分割字符串
转成stream
- Stream.of(String[] commaArray)
- 将
stream
转换为IntStream
- stream.mapToInt(e -> Integer.valueOf(e))
- 将
IntStream
转换为List<Integer>
- intStream.boxed().collect(Collectors.toList())
groupingBy 分组
groupingBy分组后列表是无序的,若结果要保持排序,则需要多两个传参
结果无序
例子:按type字段分组
// 分组无序
Map<Integer, List<User>> map =
list.stream().collect(Collectors.groupingBy(User::getType));
结果有序
例子:按type字段分组,并保证分组后结果与原始排序一致
// 分组有序
Map<Integer, List<User>> map =
list.stream().collect(
Collectors.groupingBy(User::getType, LinkedHashMap::new, Collectors.toList()));
filter
去重
有个List列表,Goods(属性:种类,数量,名称等),现需要对种类,数量,名称进行分组去重
// 联合key去重
list.stream().filter(distinctByKey(e -> e.getCategory() + e.getName() + e.getCount() + ""));
private <T> Predicate<T> distinctByKey(Function<? super T, ?> keyExtractor) {
Map<Object, Boolean> seen = new ConcurrentHashMap<>();
return t -> seen.putIfAbsent(keyExtractor.apply(t), Boolean.TRUE) == null;
}
filter(Predicate)
将结果为false的元素过滤掉,如下是过滤掉列表中的偶数
List<Integer> list = new ArrayList<Integer>();
list.add(1);
list.add(2);
list.add(3);
list.add(4);
list = list.stream().filter(new Predicate<Integer>() {
@Override
public boolean test(Integer t) {
if (t % 2 != 0) {
return true;
}
return false;
}
}).collect(Collectors.toList());
System.out.println(list.toString()); //1,3
map
对象转换,将对象User里的出生年月转换为年龄输出
// User实体
@Data
public class User {
private Date birthday;
private String name;
private Integer gender;
}
伪代码
List<User> list = new ArrayList<User>();
initUsers(list); //初始化user列表
list = list.stream.map(e -> {
int age = DateUtils.calAge(e.getBirthday());
sysout(e);
}).collect(Collectors.toList());