使用java 8 Stream
java8中stream是对集合(Collection)对象的功能上的增强。提供对集合高效的聚合和过滤操作。支持串行和并行两种模式进行汇聚操作。stream并非集合元素,并不会保存数据,如同迭代器一样,单向的不可往复的,遍历一次即用尽。而与迭代器不同的是,stream可以进行并行操作。
stream的构成:
使用流的过程基本分为:数据来源(source)->数据转换->执行结果。每次执行转换后原有的stream不变,返回一个新的stream,致使操作形成链,变成一个管道。
数据源的来源:
- 集合对象Collection和数组
- Collection.stream()
- Collection.parallelStream()
- Arrays.stream(T array)
- Stream.of()
- …
流的三种操作:
- Intermediate:一个流可以进行零个或多个转换操作,目的就是打开流,执行某种过滤或map映射等返回一个新流,交给下一个操作使用。此类操作都是惰性化的,就是说仅仅调用这个方法并没有开始流的遍历。map/filter/distinct/sorted/peek/limit/skip/parallel/sequential/unordered
- Terminal:结果操作,一个流只能有一个该操作,当执行该操作,流就相当于被使用光了,不会再产生其他流,并且会生成一个结果。forEach/toArray/reduce/collect/min/max/count/anyMatch/allMatch/noneMatch/findFirst/findAny/limit
- short-circuiting:对于一个无限大的stream,在有限的时间内计算出结果或返回一个新的stream。anyMatch/allMatch/noneMatch/findFirst/findAny/limit
常见操作:
map:映射,map生成的是1:1的映射,每次输入的元素都会按照规则转换成另外一种规则的元素。
/** * 简单收集,收集某属性集合 */ public Set<String> collectDate(List<UserVO> userList){ return userList.stream().map(UserVO::getUserName).collect(Collectors.toSet()); }
flatMap:一对多映射,通常用于合并流操作
Stream<List<Integer>> inputStream = Stream.of( Arrays.asList(1), Arrays.asList(2, 3), Arrays.asList(4, 5, 6) ); Stream<Integer> outputStream = inputStream. flatMap((childList) -> childList.stream());
filter:filter对原始的stream进行某测试,通过测试的元素形成一个新的stream
/** * 简单过滤查找列表 * @param userList * @param age * @return */ public List<UserVO> simpleFilterFindList(List<UserVO> userList,int age){ return userList.stream().filter((userVO)->userVO.getAge()==age).collect(Collectors.toList()); }
forEach:接受lambda表达式,然后在流的每个元素执行表达式
findFirst:该操作是Terminal操作又是short-circuiting操作。总是返回集合的第一个元素的Optional
/** * 查找第一个 * @param userList * @return */ public UserVO simpleFilter(List<UserVO> userList,String userName){ return userList.stream().filter((userVO)->userVO.getUserName().equals(userName)).findFirst().orElse(null); }
reduce:该操作主要是将stream元素组合起来,提供一个起始值,然后依照规则跟前面的stream的各个元素的组合。
// 字符串连接,concat = "ABCD" String concat = Stream.of("A", "B", "C", "D").reduce("", String::concat); // 求最小值,minValue = -3.0 double minValue = Stream.of(-1.5, 1.0, -3.0, -2.0).reduce(Double.MAX_VALUE, Double::min); // 求和,sumValue = 10, 有起始值 int sumValue = Stream.of(1, 2, 3, 4).reduce(0, Integer::sum); // 求和,sumValue = 10, 无起始值 sumValue = Stream.of(1, 2, 3, 4).reduce(Integer::sum).get(); // 过滤,字符串连接,concat = "ace" concat = Stream.of("a", "B", "c", "D", "e", "F"). filter(x -> x.compareTo("Z") > 0). reduce("", String::concat);
limit/skip:limit是返回stream的前n个元素,而skip则是对其stream的前n个元素
/** * limit 是取前n个元素 * skip是丢弃前n个元素 * @param userList * @return */ public List<UserVO> limitAndSkip(List<UserVO> userList){ return userList.stream().limit(10).skip(2).collect(Collectors.toList()); }
sorted:对stream的排序,好处是可以先对stream进行其他操作如map,filter,limit等操作再进行排序
/** * 排序集合 * @param userList * @return */ public List<UserVO> sortList(List<UserVO> userList){ return userList.stream().sorted((t1,t2) -> t1.getUserName().compareTo(t2.getUserName())).collect(Collectors.toList()); }
min/max/distinct:常见的取最大值最小值和去重操作
Match:allMatch/anyMatch/noneMatch,这些都是不需要遍历完所有元素就可以返回结果的,例如allMatch有一个不满足就会返回false。
/** * 检查是否所有元素都匹配条件 * @param userList * @return */ public boolean matchAllUser(List<UserVO> userList){ return userList.stream().allMatch((userVO)->userVO.getAge()>=0); }
自定义生成流:Stream.generate
通过Supplier接口可以自定义流的生成,常用于随机数,常量或者前后保持某种关系的流。必须使用limit控制流的大小。/** * 自定义流 */ public void generateStream(){ Random ran = new Random(); Supplier<Integer> stream = ran::nextInt; Stream.generate(stream).limit(10).forEach((value) -> System.out.println(value)); }
Stream.iterate:操作与reduce类似,接受一个初始值,和一个操作,依次执行下去。也需要使用limit进行控制流的大小。
groupingBy:分组
/** * 将集合分组 * 根据年龄分组 * @param userList * @return */ public Map<Integer,List<UserVO>> groupUserVO(List<UserVO> userList){ return userList.stream().collect(Collectors.groupingBy(userVO -> userVO.getAge())); }
partitioningBy:分割
/** * 分割数据集,用年龄分割 * @param userList * @return */ public Map<Boolean,List<UserVO>> splitUserList(List<UserVO> userList){ return userList.stream().collect(Collectors.partitioningBy(userVO -> userVO.getAge()>15)); }
总结
- 流不进行存储,不是一种数据结构,也不会修改底层的数据源,只会产生新的流
- Intermediate操作永远是惰性的,很多操作是向后延迟,直到知到多少数据才会开始
- stream可以并行处理,无需编写多线程代码
- stream可以是无限的,但一般需要limit和findFirst等short-curcutiting方法快速得到结果