流(Stream) 是什么?
是数据渠道,用于操作数据源(集合、数组等)所生成的元素序列。
“集合讲的是数据,流讲的是计算!”
特点
1)、Stream 自己不会存储元素
2)、Stream 不会改变源对象。相反, 他们会返回一个持有结果的新Stream。
3)、Stream 操作是延迟执行的。这意味着 他们会等到需要结果的时候才执行。
使用
创建一个Stream:一个数据源(数组、集合)
中间操作:一个中间操作,处理数据源数据
终止操作:一个终止操作,执行中间操作链,产生结果
public class TestStream {
public static void main(String[] args) {
User u1 = new User(1, "name1", 18);
User u2 = new User(2, "name2", 20);
User u3 = new User(3, "name3", 22);
User u4 = new User(4, "name4", 23);
User u5 = new User(5, "name5", 25);
User u6 = new User(6, "name6", 26);
//数据源
List<User> list = Arrays.asList(u1, u2, u3, u4, u5, u6);
//filter过滤,找出id为偶数的数据
list.stream().filter((t) -> {
return t.getId() % 2 == 0;
}).filter(t -> {
return t.getAge() >= 22;
//map操作数据
}).map(m -> {
return m.getName().toUpperCase();
//sort进行排序
}).sorted((o1, o2) -> {
return o2.length() - o1.length();
//limit现在数据条数
}).limit(1)
//进行foreach输出
.forEach((b) -> {
System.out.println(b);
});
}
}
常用的方法
1、Stream filter(Predicate<? super T> predicate):过滤,返回一个boolean值
我们希望过滤赛选处所有学校是清华大学的user:
System.out.println("学校是清华大学的user");
List<User> userList1 = list.stream()
.filter(user -> "清华大学".equals(user.getSchool()))
.collect(Collectors.toList());
userList1.forEach(user -> System.out.print(user.name + '、'));
2、Stream distinct(); 去重
3、Stream limit(long maxSize); 传入输出的个数
如获取年龄是偶数的前2名user:
System.out.println("年龄是偶数的前两位user");
List<User> userList3 = list.stream()
.filter(user -> user.getAge() % 2 == 0)
.limit(2)
.collect(Collectors.toList());
userList3.forEach(user -> System.out.print(user.name + '、'));
4、Stream sorted(Comparator<? super T> comparator); 排序,可自定义排序
如现在我想将所有user按照age从大到小排序:
System.out.println("按年龄从大到小排序");
List<User> userList4 = list.stream()
.sorted((s1,s2) -> s2.age - s1.age)
.collect(Collectors.toList());
userList4.forEach(user -> System.out.print(user.name + '、'));
5、Stream skip(long n); 跳过n个输出
如输出list集合跳过前两个元素后的list
System.out.println("跳过前面两个user的其他所有user");
List<User> userList5 = list.stream().
skip(2).
collect(Collectors.toList());
userList5.forEach(user -> System.out.print(user.name + '、'));
6、 Stream map(Function<? super T, ? extends R> mapper);:获取某个字段的集合
如获取学校是清华大学的所有学生的名字
System.out.println("学校是清华大学的user的名字");
List<String> userList6 = list.stream()
.filter(user -> "清华大学".equals(user.school))
.map(User::getName)
.collect(Collectors.toList());
userList6.forEach(user -> System.out.print(user + '、'));
7、 Stream flatMap(Function<? super T, ? extends Stream<? extends R>> mapper);
一个流中的每个值都转成一个个流,然后再将这些流扁平化成为一个流
String[] strings = {"Hello", "World"};
List l11 = Arrays.stream(strings)
.map(str -> str.split(""))
.map(str2->Arrays.stream(str2))
.distinct().collect(Collectors.toList());
List l2 = Arrays.asList(strings)
.stream()
.map(s -> s.split(""))
.flatMap(Arrays::stream)
.distinct().collect(Collectors.toList());
System.out.println(l11.toString());
System.out.println(l2.toString());
输出结果如下:
[java.util.stream.ReferencePipeline$Head@4c203ea1, java.util.stream.ReferencePipeline$Head@27f674d]
[H, e, l, o, W, r, d]
8、boolean allMatch(Predicate<? super T> predicate);
用于检测是否全部都满足指定的参数行为,如果全部满足则返回true
System.out.println("判断是否所有user的年龄都大于9岁");
Boolean b = list.stream().
allMatch(user -> user.age >9);
System.out.println(b);
9、boolean anyMatch(Predicate<? super T> predicate);
anyMatch则是检测是否存在一个或多个满足指定的参数行为,如果满足则返回true
System.out.println("判断是否有user的年龄是大于15岁");
Boolean bo = list.stream().anyMatch(user -> user.age >15);
System.out.println(bo);
10、boolean noneMatch(Predicate<? super T> predicate);
用于检测是否不存在满足指定行为的元素
System.out.println("判断是否不存在年龄是15岁的user");
Boolean boo = list.stream().
noneMatch(user -> user.age == 15);
System.out.println(boo);
11、Optional findFirst();
findFirst用于返回满足条件的第一个元素,
System.out.println("返回年龄大于12岁的user中的第一个");
Optional<User> first = list.stream()
.filter(u -> u.age > 10)
.findFirst();
User user = first.get();
System.out.println(user.toString());
12、Optional findAny();
findAny相对于findFirst的区别在于,findAny不一定返回第一个,而是返回任意一个
System.out.println("返回年龄大于12岁的user中的任意一个");
Optional<User> anyOne = list.stream()
.filter(u -> u.age > 10)
.findAny();
User user2 = anyOne.get();
System.out.println(user2.toString());
收集
前面利用collect(Collectors.toList())是一个简单的收集操作,是对处理结果的封装,对应的还有toSet、toMap,以满足我们对于结果组织的需求。这些方法均来自于java.util.stream.Collectors,我们可以称之为收集器。
收集器也提供了相应的归约操作,但是与reduce在内部实现上是有区别的,收集器更加适用于可变容器上的归约操作,这些收集器广义上均基于Collectors.reducing()实现。
1 counting
计算个数
如我现在计算user的总人数,实现如下:
System.out.println("user的总人数");
long COUNT = list.stream().count();//简化版本
long COUNT2 = list.stream()
.collect(Collectors.counting());//原始版本
System.out.println(COUNT);
System.out.println(COUNT2);
2 maxBy、minBy
maxBy(Comparator<? super T> comparator)
minBy(Comparator<? super T> comparator)
计算最大值和最小值
如我现在计算user的年龄最大值和最小值:
System.out.println("user的年龄最大值和最小值");
Integer maxAge =list.stream()
.collect(
Collectors.maxBy((s1, s2) -> s1.getAge() - s2.getAge()))
.get().age;
Integer maxAge2 = list.stream().collect(
Collectors.maxBy(Comparator.comparing(User::getAge)))
.get().age;
Integer minAge = list.stream().collect(
Collectors.minBy((S1,S2) -> S1.getAge()- S2.getAge()))
.get().age;
Integer minAge2 = list.stream().collect(
Collectors.minBy(Comparator.comparing(User::getAge)))
.get().age;
System.out.println("maxAge = " + maxAge);
System.out.println("maxAge2 = " + maxAge2);
System.out.println("minAge = " + minAge);
System.out.println("minAge2 = " + minAge2);
3 summingInt、summingLong、summingDouble
ummingInt(ToIntFunction<? super T> mapper)
总和
如计算user的年龄总和:
System.out.println("user的年龄总和");
Integer sumAge =list.stream().collect(Collectors.summingInt(User::getAge));
System.out.println("sumAge = " + sumAge)
4 averageInt、averageLong、averageDouble
averagingInt(ToIntFunction<? super T> mapper)
平均值
如计算user的年龄平均值:
System.out.println("user的年龄平均值");
double averageAge = list.stream().collect(Collectors.averagingDouble(User::getAge));
System.out.println("averageAge = " + averageAge);
5 summarizingInt、summarizingLong、summarizingDouble
一次性查询元素个数、总和、最大值、最小值和平均值
System.out.println("一次性得到元素个数、总和、均值、最大值、最小值");
long l1 = System.currentTimeMillis();
IntSummaryStatistics summaryStatistics = list.stream().collect(Collectors.summarizingInt(User::getAge));
long l111 = System.currentTimeMillis();
System.out.println("计算这5个值消耗时间为" + (l111-l1));
System.out.println("summaryStatistics = " + summaryStatistics);
输出结果为:
一次性得到元素个数、总和、均值、最大值、最小值
计算这5个值消耗时间为3
summaryStatistics = IntSummaryStatistics{count=10, sum=145, min=10, average=14.500000, max=25}
6 joining
Collector<CharSequence, ?, String> joining(CharSequence delimiter)
字符串拼接
如输出所有user的名字,用“,”隔开
System.out.println("字符串拼接");
String names = list.stream()
.map(User::getName).collect(Collectors.joining(","));
System.out.println("names = " + names);
输出结果为:
字符串拼接
names = 张三,李四,王五,赵六,田七,小明,小红,小华,小丽,小何
7 groupingBy
分组
Collector<T, ?, Map<K, List>> groupingBy(Function<? super T, ? extends K> classifier)
如将user根据学校分组、先按学校分再按年龄分、每个大学的user人数、每个大学不同年龄的人数:
System.out.println("分组");
Map<String, List<User>> collect1 = list.stream()
.collect(Collectors.groupingBy(User::getSchool));
Map<String, Map<Integer, Long>> collect2 = list.stream()
.collect(Collectors.groupingBy(User::getSchool, Collectors.groupingBy(User::getAge, Collectors.counting())));
Map<String, Map<Integer, Map<String, Long>>> collect4 =
list.stream()
.collect(Collectors.groupingBy(User::getSchool, Collectors.groupingBy(User::getAge, Collectors.groupingBy(User::getName,Collectors.counting()))));
Map<String, Long> collect3 = list.stream()
.collect(Collectors.groupingBy(User::getSchool, Collectors.counting()));
System.out.println("collect1 = " + collect1);
System.out.println("collect2 = " + collect2);
System.out.println("collect3 = " + collect3);
System.out.println("collect4 = " + collect4);
输出结果为:
分组
collect1 = {浙江大学=[User{id=8, name='小华', age=14, school='浙江大学'}, User{id=9, name='小丽', age=17, school='浙江大学'}, User{id=10, name='小何', age=10, school='浙江大学'}], 北京大学=[User{id=5, name='田七', age=25, school='北京大学'}, User{id=6, name='小明', age=16, school='北京大学'}, User{id=7, name='小红', age=14, school='北京大学'}], 清华大学=[User{id=1, name='张三', age=10, school='清华大学'}, User{id=2, name='李四', age=12, school='清华大学'}, User{id=3, name='王五', age=15, school='清华大学'}, User{id=4, name='赵六', age=12, school='清华大学'}]}
collect2 = {浙江大学={17=1, 10=1, 14=1}, 北京大学={16=1, 25=1, 14=1}, 清华大学={10=1, 12=2, 15=1}}
collect3 = {浙江大学=3, 北京大学=3, 清华大学=4}
collect4 = {浙江大学={17={小丽=1}, 10={小何=1}, 14={小华=1}}, 北京大学={16={小明=1}, 25={田七=1}, 14={小红=1}}, 清华大学={10={张三=1}, 12={李四=1, 赵六=1}, 15={王五=1}}}
8 partitioningBy
分区,分区可以看做是分组的一种特殊情况,在分区中key只有两种情况:true或false,目的是将待分区集合按照条件一分为二,java8的流式处理利用ollectors.partitioningBy()方法实现分区。
如按照是否是清华大学的user将左右user分为两个部分:
System.out.println("分区");
Map<Boolean, List<User>> collect5 = list.stream().collect(Collectors.partitioningBy(user1 -> "清华大学".equals(user1.school)));
System.out.println("collect5 = " + collect5);
输出结果为:
分区
collect5 = {
false=[User{id=5, name='田七', age=25, school='北京大学'}, User{id=6, name='小明', age=16, school='北京大学'}, User{id=7, name='小红', age=14, school='北京大学'}, User{id=8, name='小华', age=14, school='浙江大学'}, User{id=9, name='小丽', age=17, school='浙江大学'}, User{id=10, name='小何', age=10, school='浙江大学'}],
true=[User{id=1, name='张三', age=10, school='清华大学'}, User{id=2, name='李四', age=12, school='清华大学'}, User{id=3, name='王五', age=15, school='清华大学'}, User{id=4, name='赵六', age=12, school='清华大学'}]
}
题外话
小潘的个人微信公众号【小潘学程序】,有兴趣可给个关注~
一起学习,一起成长