前言
在没有接触Stream API之前,在遇到集合类操作,比如提取集合中满足条件的某些元素或者对两个集合进行数据对
比和筛选等,总是避免不了各种循环操作,代码量总是很大。我就在想既然集合类一般都是存贮的一种类型的数据
类似数据库中的一张表,每个对象的就是一条数据,每个属性就是一个字段,既然针对数据库表有SQL这种语言来
进行数据的筛选操作,那么在java代码中有没有类似的API来处理对集合类型中数据的处理呢?后来发现还真有,这
就是Stream API.
Stream是什么
是一种数据渠道,用来操作数据源比如集合,数组等。
Stream只是一种操作,本身并不会修改原有的数据源。而是返回新的Stream。
Stream的操作是延迟的。
如何使用Stream
1 创建stream
1.1 通过Collection集合中的stream()活parallestream()。
List<String> lists = new ArrayList<>();
list.stream();
list.parallelStream()
1.2 通过Arrays中的静态方法stream()获取数组流。
Student[] studentss = new Student[5];
Stream<Student> streams = Arrays.stream(studentss);
1.3 通过stream类中的ofStream()方法。
Stream<String> stringStream = Stream.of("yyf", "zrf", "zxc");
2 中间操作
2.1 筛选与切片
filter --接收Lambda,从流中排除某些元素。
limit --从流中选中几条。
distinct --通过hashCode()和equals()方法去重。
skip(n) --返回一个扔掉了前n个元素的流。
@Slf4j
public class TestStream {
public static void main(String[] args) {
List<User> list = Arrays.asList(
new User(32, "yyf", "河南"),
new User(34, "abc","北京"),
new User(65, "yyf", "天津"),
new User(31, "ewe","上海"),
new User(22, "yyf", "广东"),
new User(32, "yyf", "河南")
);
log.info("打印出年龄大于32的用户");
list.stream().filter(e->e.getAge().compareTo(32)>=0).forEach(System.out::println);
log.info("打印出前三个用户");
list.stream().limit(3L).forEach(System.out::println);
log.info("跳过前三个用户");
list.stream().skip(3L).forEach(System.out::println);
log.info("去重相同用户");
list.stream().distinct().forEach(System.out::println);
log.info("合并两个stream ======union all");
Stream.concat(list.stream().limit(3L),list.stream().skip(4L)).forEach(System.out::println);
}
}
输出如下:
[main] INFO com.sjbl.TestStream - 打印出年龄大于32的用户
User(age=32, name=yyf, birthAddress=河南)
User(age=34, name=abc, birthAddress=北京)
User(age=65, name=yyf, birthAddress=天津)
User(age=32, name=yyf, birthAddress=河南)
[main] INFO com.sjbl.TestStream - 打印出前三个用户
User(age=32, name=yyf, birthAddress=河南)
User(age=34, name=abc, birthAddress=北京)
User(age=65, name=yyf, birthAddress=天津)
[main] INFO com.sjbl.TestStream - 跳过前三个用户
User(age=31, name=ewe, birthAddress=上海)
User(age=22, name=yyf, birthAddress=广东)
User(age=32, name=yyf, birthAddress=河南)
[main] INFO com.sjbl.TestStream - 去重相同用户
User(age=32, name=yyf, birthAddress=河南)
User(age=34, name=abc, birthAddress=北京)
User(age=65, name=yyf, birthAddress=天津)
User(age=31, name=ewe, birthAddress=上海)
User(age=22, name=yyf, birthAddress=广东)
[main] INFO com.sjbl.TestStream - 合并两个stream ======union all
User(age=32, name=yyf, birthAddress=河南)
User(age=34, name=abc, birthAddress=北京)
User(age=65, name=yyf, birthAddress=天津)
User(age=22, name=yyf, birthAddress=广东)
User(age=32, name=yyf, birthAddress=河南)
2.2 映射操作
<R> Stream<R> map(Function<? super T,? extends R> mapper)返回由给定函数应用于此流的元素的结果组成的流。
log.info("给每一个年龄都加上10");
list.stream().map(e -> e.getAge() + 10).forEach(System.out::println);
log.info("将名称提取出来");
list.stream().map(User::getName).forEach(System.out::println);
int ageSum = list.stream().mapToInt(User::getAge).sum();
System.out.println("年龄总和为:" + ageSum);
double ageAve = list.stream().mapToInt(User::getAge).average().getAsDouble();
System.out.println("年龄平均值为:" + ageAve);
int ageMax = list.stream().mapToInt(User::getAge).max().getAsInt();
System.out.println("年龄最大值为:" + ageMax);
int ageMin = list.stream().mapToInt(User::getAge).min().getAsInt();
System.out.println("年龄最大值为:" + ageMin);
输出如下:
[main] INFO com.sjbl.TestStream - 给每一个年龄都加上10
42
44
75
41
32
42
[main] INFO com.sjbl.TestStream - 将名称提取出来
yyf
abc
yyf
ewe
yyf
yyf
年龄总和为:216
年龄平均值为:36.0
年龄最大值为:65
年龄最大值为:22
2.3排序操作
log.info("按年龄升序排方式一");
list.stream().sorted(Comparator.comparing(User::getAge)).forEach(System.out::println);
log.info("按年龄升序排方式二");
list.stream().sorted((a,b)->a.getAge().compareTo(b.getAge())).forEach(System.out::println);
log.info("按年龄降序排");
list.stream().sorted((a,b)->b.getAge().compareTo(a.getAge())).forEach(System.out::println);
输出如下:
[main] INFO com.sjbl.TestStream - 按年龄升序排方式一
User(age=22, name=yyf, birthAddress=广东)
User(age=31, name=ewe, birthAddress=上海)
User(age=32, name=yyf, birthAddress=河南)
User(age=32, name=yyf, birthAddress=河南)
User(age=34, name=abc, birthAddress=北京)
User(age=65, name=yyf, birthAddress=天津)
[main] INFO com.sjbl.TestStream - 按年龄升序排方式二
User(age=22, name=yyf, birthAddress=广东)
User(age=31, name=ewe, birthAddress=上海)
User(age=32, name=yyf, birthAddress=河南)
User(age=32, name=yyf, birthAddress=河南)
User(age=34, name=abc, birthAddress=北京)
User(age=65, name=yyf, birthAddress=天津)
[main] INFO com.sjbl.TestStream - 按年龄降序排
User(age=65, name=yyf, birthAddress=天津)
User(age=34, name=abc, birthAddress=北京)
User(age=32, name=yyf, birthAddress=河南)
User(age=32, name=yyf, birthAddress=河南)
User(age=31, name=ewe, birthAddress=上海)
3 终止操作
3.1 查找与匹配
allMatch--检查是否匹配所有元素
anyMatch--检查是否匹配至少一个元素
noneMatch --检查是否没有匹配所有元素
findFirst --返回第一个元素
findAny --返回当前流中的任意元素
boolean abcResult = list.stream().anyMatch(e -> e.getName().equals("abc"));
log.info("集合中是否有一个名称为abc:" + abcResult);
boolean addSizeResult = list.stream().allMatch(e -> e.getBirthAddress().toCharArray().length == 2);
log.info("集合中是否所有人地址都是两个字符:" + addSizeResult);
boolean noZheJiang = list.stream().noneMatch(e -> e.getBirthAddress().equals("浙江"));
log.info("集合中是否所有人都不来自浙江:" + noZheJiang);
Optional<User> any = list.stream().findAny();
log.info("集合中随机抽取一个元素:" + any.toString());
输出如下:
[main] INFO com.sjbl.TestStream - 集合中是否有一个名称为abc:true
[main] INFO com.sjbl.TestStream - 集合中是否所有人地址都是两个字符:true
[main] INFO com.sjbl.TestStream - 集合中是否所有人都不来自浙江:true
[main] INFO com.sjbl.TestStream - 集合中随机抽取一个元素:Optional[User(age=32, name=yyf, birthAddress=河南)]
3.2 收集
List<Integer> ageList = list.stream().map(User::getAge).collect(Collectors.toList());
log.info("ageList为" + ageList.toString());
Set<Integer> ageSet = list.stream().map(User::getAge).collect(Collectors.toSet());
log.info("ageSet为" + ageSet.toString());
Set<Integer> ageHashSet = list.stream().map(User::getAge).collect(Collectors.toCollection(HashSet::new));
log.info("ageHashSet为" + ageHashSet.toString());
输出如下:
[main] INFO com.sjbl.TestStream - ageList为[32, 34, 65, 31, 22, 32]
[main] INFO com.sjbl.TestStream - ageSet为[32, 65, 34, 22, 31]
[main] INFO com.sjbl.TestStream - ageHashSet为[32, 65, 34, 22, 31]
3.3 分组
Map<String, List<User>> addMap = list.stream().collect(Collectors.groupingBy(User::getBirthAddress));
log.info("按照出生地分组" + addMap.toString());
Map<String, Map<String, List<User>>> addAndNameMap = list.stream()
.collect(Collectors.groupingBy(User::getBirthAddress, Collectors.groupingBy(a->{
if(a.getAge()<=25){
return "少年";
}else if(a.getAge()>25&&a.getAge()<=50){
return "壮年";
}else {
return "老年";
}
})));
log.info("按照出生地和年龄段分组" + addAndNameMap.toString());
输出如下:
[main] INFO com.sjbl.TestStream - 按照出生地分组{上海=[User(age=31, name=ewe, birthAddress=上海)], 广东=[User(age=22, name=yyf, birthAddress=广东)], 天津=[User(age=65, name=yyf, birthAddress=天津)], 河南=[User(age=32, name=yyf, birthAddress=河南), User(age=32, name=yyf2, birthAddress=河南)], 北京=[User(age=34, name=abc, birthAddress=北京)]}
[main] INFO com.sjbl.TestStream - 按照出生地和年龄段分组{上海={壮年=[User(age=31, name=ewe, birthAddress=上海)]}, 广东={少年=[User(age=22, name=yyf, birthAddress=广东)]}, 天津={老年=[User(age=65, name=yyf, birthAddress=天津)]}, 河南={壮年=[User(age=32, name=yyf, birthAddress=河南), User(age=32, name=yyf2, birthAddress=河南)]}, 北京={壮年=[User(age=34, name=abc, birthAddress=北京)]}}
总结
结合上篇文章中对于sql的使用建议,在SQL中尽量避免复杂的查询,如果需要关联多个表的数据,可以使用单表
查出几个较为粗略的List,然后在java代码中通过Stream API来进行真正的结果的“封装拼接”。既避免了复杂
查询时导致的数据库的查询等待,又通过使用Stream API避免了java代码中的令人眼花缭乱的各种循环代码。