1. 前言
Java8
提供了Stream
操作,针对集合可以完成复杂的查找、过滤以及数据映射等操作。
完成一个完整的Stream
操作,可分为三步走:
(1) 流的创建:从数据源(集合或数组)中获取一个流;
(2) 中间操作:对流进行过滤、筛选等操作;
(3) 终止操作:对流进行最后的统计或收集等操作。
注意:中间操作需要等到终止操作调用才会执行。
2. 流的创建
2.1 Collection
Collection
接口提供两种方法来创建流。
-
Stream<E> stream()
返回一个顺序流; -
Stream<E> parallelStream()
返回一个并行流。
注意:数据量不大的情况下,使用顺序流便足够了。
栗子:
static void testParallel() {
// 程序开始时间
Instant startTime = Instant.now();
List<String> list = Arrays.asList("java", "python", "javascript", "html");
//顺序流
Stream<String> stream = list.stream().filter(i -> i.startsWith("ja"));
stream.forEach(System.out::println);
Instant middleTime = Instant.now();
//并行流
Stream<String> stream2 = list.parallelStream().filter(i -> i.startsWith("ja"));
stream2.forEach(System.out::println);
// 程序结束时间
Instant endTime = Instant.now();
long millis1 = ChronoUnit.MILLIS.between(startTime, middleTime);
long millis2 = ChronoUnit.MILLIS.between(middleTime, endTime);
System.out.println("stream程序消耗时间(毫秒)" + millis1);
System.out.println("parallelStream程序消耗时间(毫秒)" + millis2);
}
输出结果:
javascript
java
javascript
java
stream程序消耗时间(毫秒)73
parallelStream程序消耗时间(毫秒)82
2.2 Arrays
数组可以通过Arrays.stream(arr)
创建一个流。
栗子:
static void testArrayStream(){
String[] arr = {"java", "python", "javascript", "html"};
Stream<String> stream = Arrays.stream(arr).filter(i -> i.startsWith("ja"));
stream.forEach(System.out::println);
}
2.3 Stream
通过Stream.of()
可以直接创建一个流。
栗子:
static void testStreamDirect() {
Stream<String> stream = Stream.of("java", "python", "javascript", "html") //根据多个Value值直接创建一个流
.filter(i -> i.startsWith("ja")); //半路拦截,只留下以“ja”开头的字符串
stream.forEach(System.out::println);
}
通过Stream.iterate()
和Stream.generate()
创建无限流。
栗子:
static void testInfiniteStream(){
//Stream.iterate()
Stream<Integer> stream = Stream.iterate(10, x -> x * 2); //从10开始,每次翻倍
stream.limit(3).forEach(System.out::println);
System.out.println("----------- 我是最靓分割线 ------------");
//Stream.generate()
Stream.generate(Math::random).limit(5).forEach(System.out::println); //整5个随机数玩玩
}
输出结果:
10
20
40
----------- 我是最靓分割线 ------------
0.28733224175718597
0.37745872228126487
0.5575898714168762
0.8904672894488914
0.27626278982879915
3. 中间操作
3.1 筛选
3.1.1 filter
对流中的元素进行过滤,满足条件的留下,不满足的全部Game Over
!
栗子:
static void testFilter(){
List<Integer> list = Arrays.asList(1, 3, 4, 2, 7, 10);
list.stream().filter(i -> i % 2 == 0) // 偶数者留,奇数者去也
.forEach(System.out::println);
}
// result: 4 2 10
3.1.2 limit
限流,只允许指定个数的元素通过,后面的元素一边凉快去吧。
栗子:
static void testLimit(){
List<Integer> list = Arrays.asList(1, 3, 4, 2, 7, 10);
list.stream().limit(3) // 只保留前3个元素
.forEach(System.out::println);
}
// result: 1 3 4
3.1.3 skip
跳过,抛弃前面的指定个数的元素,直接跳跃到后面进行流处理。
栗子:
static void testSkip(){
List<Integer> list = Arrays.asList(1, 3, 4, 2, 7, 10);
list.stream().skip(3) // 只保留前3个元素
.forEach(System.out::println);
}
// result: 2 7 10
3.1.4 distinct
去重,过滤重复的元素,相同元素只保留一个。
栗子:
static void testDistinct(){
List<String> list = Arrays.asList("java","java","java","javascript");
list.stream().distinct() // 请尝尝传说中的归一大法吧
.forEach(System.out::println);
}
// result: java javascript
3.2 排序
3.2.1 默认排序
栗子:
static void testSorted(){
List<String> list = Arrays.asList("short","int","char","byte");
list.stream().sorted() //自然排序
.forEach(System.out::println);
}
// result: byte char int short
3.2.2 自定义排序
栗子:
static void testDefinedSorted(){
List<Integer> list = Arrays.asList(1, 3, 4, 2, 7, 10);
list.stream().sorted((a, b) -> b - a) //倒序
.forEach(System.out::println);
}
// result: 10 7 4 3 2 1
3.3 映射
3.3.1 map
将流中的元素的值经过一番变化映射成另外一个值。
栗子:
static void testSimpleMap(){
List<Integer> list = Arrays.asList(1, 2, 3, 4);
list.stream().map(i -> i * 10)
.forEach(System.out::println);
}
//result: 10 20 30 40
3.3.2 flatMap
map
和flatMap
有点类似于List
中的add
和addAll
。
add
添加一个List
只会当做原来的List
中一个普通成员,而如果是addAll
,则会将新添加的List
和原来的List
合并成一个List
。
static void testAddAll(){
List<Object> oldList = new ArrayList<>();
oldList.add(1); //添加单个元素
oldList.add(Arrays.asList(2, 3)); //添加 List
System.out.println(Arrays.toString(oldList.toArray()));
oldList.clear(); //清空
oldList.add(1); //添加单个元素
oldList.addAll(Arrays.asList(2, 3)); //添加 List
System.out.println(Arrays.toString(oldList.toArray()));
}
// result:
// [1, [2, 3]] //纵横交错深万里
// [1, 2, 3] //水平扩展一线牵
map
处理中如果返回的是Stream
,那么只会在原来的Stream
中添加Stream
作为普通成员,最终的结果是Stream
中包含Stream
。
而如果flapMap
处理中如果返回的是Stream
,则会将其合并到原来的Stream
中去,最终的结果只有一个Stream
。
栗子:
//比 FlatMap 多了一层 foreach
static void testMap(){
List<Integer[]> list = Arrays.asList(new Integer[]{1 , 2}, new Integer[]{3, 4});
list.stream().map(integers -> Arrays.stream(integers).map(i -> i * 10))
.forEach(integerStream -> {integerStream.forEach(System.out::println);});
}
//result: 10 20 30 40
static void testFlatMap(){
List<Integer[]> list = Arrays.asList(new Integer[]{1 , 2}, new Integer[]{3, 4});
list.stream().flatMap(integers -> Arrays.stream(integers).map(i -> i * 10))
.forEach(System.out::println);
}
//result: 10 20 30 40
4. 终止操作
看来是藏不住杀招了,上面的中间操作方法返回类型都
Stream
,下面的终止操作返回类型都非Stream
!
4.1 匹配
4.1.1 anyMatch
只要任意一个满足匹配条件,便返回true
。
static void testAnyMatch(){
List<String> list = Arrays.asList("java", "javascript", "python", "html");
boolean b = list.stream().anyMatch(i -> i.startsWith("p"));
System.out.println("所有元素字符串中,只要有一个元素是以p开头,便会返回" + b);
}
// result: 所有元素字符串中,只要有一个元素是以p开头,便会返回true
4.1.2 allMatch
所有元素满足匹配条件,才会返回true
。
栗子:
static void testAllMatch(){
List<String> list = Arrays.asList("java", "javascript", "python", "html");
boolean b = list.stream().skip(1) //跳过不包含 t 的 java
.allMatch(i -> i.contains("t"));
System.out.println("需要全部元素都包含t,便会返回" + b);
}
// result: 需要全部元素都包含t,便会返回true
4.1.3 noneMatch
没有一个元素能够满足匹配条件,便会返回true
。
栗子:
static void testNoneMatch(){
List<String> list = Arrays.asList("java", "javascript", "python", "html");
boolean b = list.stream().noneMatch(i -> i.contains("z"));
System.out.println("没有一个元素都包含z,便会返回" + b);
}
// result: 没有一个元素都包含z,便会返回true
4.2 查找
4.2.1 findFirst
返回流中的第一个元素。
栗子:
static void testFindFirst(){
List<String> list = Arrays.asList("java", "javascript", "python", "html");
Optional<String> s = list.stream().filter(i -> i.contains("t"))
.findFirst();
System.out.println("返回第一个元素:" + s);
}
// result: 返回第一个元素:Optional[javascript]
4.2.2 findAny
返回流中任意一个元素。
栗子:
static void testFindAny(){
List<String> list = Arrays.asList("java", "javascript", "python", "html");
Optional<String> s = list.stream().filter(i -> i.contains("a"))
.findAny();
System.out.println("返回任意一个元素:" + s);
}
// result: 返回任意一个元素:Optional[java]
4.3 统计
4.3.1 `count
static void testCount(){
List<Integer> list = Arrays.asList(1, 3, 4, 2, 7, 10);
long count = list.stream().filter(i -> i % 2 == 0).count();
System.out.println("集合中偶数的个数:" + count);
}
// result: 集合中偶数的个数:3
4.3.2 max
static void testMax(){
List<Integer> list = Arrays.asList(1, 3, 4, 2, 7, 10);
Optional<Integer> integer = list.stream().filter(i -> i % 2 == 0)
.max(Integer::compareTo);
System.out.println("集合中偶数的最大值:" + (integer.orElse(null)));
}
// result: 集合中偶数的最大值:10
4.3.3 min
static void testMin(){
List<Integer> list = Arrays.asList(1, 3, 4, 2, 7, 10);
Optional<Integer> integer = list.stream().filter(i -> i % 2 == 0)
.min(Integer::compareTo);
System.out.println("集合中偶数的最小值:" + (integer.orElse(null)));
}
// result: 集合中偶数的最小值:2
4.4 归约
static void testReduce(){
List<Integer> list = Arrays.asList(1, 3, 4);
Integer integer = list.stream().reduce(100, (a, b) -> a + b * 2);
System.out.println("初始值为100,然后将集合中的数值乘以2,再与初始值累加。");
System.out.println("最终结果:100 + (1 * 2) + (3 * 2) + (4 * 2) = " + integer);
}
// result:
// 初始值为100,然后将集合中的数值乘以2,再与初始值累加。
// 最终结果:100 + (1 * 2) + (3 * 2) + (4 * 2) = 116
4.5 收集
主要介绍Collectors
工具类的使用。
4.5.1 返回Collection
将流中的元素装入List
集合。
List<Integer> list = Stream.of(1, 2, 3).collect(Collectors.toList());
将流中的元素装入Set
集合。
Set<Integer> set = Stream.of(1, 2, 3).collect(Collectors.toSet());
将流中元素装入Map
集合。
// key: x , value: x * x
Map<Integer, Integer> map = Stream.of(1, 2, 3).collect(Collectors.toMap(x -> x, x -> x * x));
Optional<String> s = map.entrySet().stream()
.map(x -> "{" + x.getKey() + ":" + x.getValue() + "}")
.reduce((x, y) -> x + "," + y);
System.out.println("打印Map新方式,result = [" + s.orElse(null) + "]");
// result: 打印Map新方式,result = [{1:1},{2:4},{3:9}]
将流中的元素装入指定集合。
HashSet<Integer> hashSet = Stream.of(1, 2, 3).collect(Collectors.toCollection(HashSet::new));
4.5.2 拼接字符串
static void testJoinStr(){
List<String> list = Arrays.asList("java","javascript");
String collect1 = list.stream().collect(Collectors.joining());
String collect2 = list.stream().collect(Collectors.joining(","));
String collect3 = list.stream().collect(Collectors.joining(",", "{", "}"));
System.out.println(collect1);
System.out.println(collect2);
System.out.println(collect3);
}
// result:
// javajavascript
// java,javascript
// {java,javascript}
4.5.2 统计
统计流中元素的个数
Long count = Stream.of(1, 2, 3).collect(Collectors.counting());
统计流中元素的最大值
Optional<Integer> max = Stream.of(1, 2, 3).collect(Collectors.maxBy(Integer::compareTo));
统计流中元素的最小值
Optional<Integer> min = Stream.of(1, 2, 3).collect(Collectors.minBy(Integer::compareTo));
统计流中元素的平均值
// 除了 averagingInt ,还有 averagingLong , averagingDouble
Double avg = Stream.of(1, 1, 2, 3).collect(Collectors.averagingInt(x -> x));
统计流中元素的总和
// 除了 summingInt ,还有 summingLong , summingDouble
Integer sum = Stream.of(1, 1, 2, 3).collect(Collectors.summingInt(x -> x));
统计汇总
// 除了 summarizingInt ,还有 summarizingLong , summarizingDouble
IntSummaryStatistics statistics = Stream.of(1, 1, 2, 3).collect(Collectors.summarizingInt(x -> x));
long count1 = statistics.getCount();
int max1 = statistics.getMax();
int min1 = statistics.getMin();
double average = statistics.getAverage();
long sum1 = statistics.getSum();
4.5.3 其他用法
Collectors.reducing
与Stream.reduce
类似,进行累计运算。
// 100 + ((1 + 2) + 3)
Integer collect = Stream.of(1, 2, 3).collect(Collectors.reducing(100, (x, y) -> x + y));
// 100 + (((1 + 1) + (2 + 1)) + (3 + 1))
Integer collect = Stream.of(1, 2, 3).collect(Collectors.reducing(100, x -> x + 1, (x, y) -> x + y));
Collectors.mapping
与Stream.map
类似,进行数据映射。
Integer integer = Stream.of(1, 2, 3).collect(Collectors.mapping(x -> x * 2, Collectors.summingInt(x -> x)));
String collect = Stream.of("a", "b", "c").collect(Collectors.mapping(x -> x.toUpperCase(), Collectors.joining(",")));
Collectors.collectingAndThen
String s = Stream.of("a", "b", "c").collect(Collectors.collectingAndThen(Collectors.joining(","), x -> x.toUpperCase()));
// result: A,B,C