本文转载自微信公众号《Java极客技术》,作者鸭血范。转载本文请联系Java极客技术公众号。一、简介我们都知道从Java8开始,jdk新增了一个Stream类来补充集合类。它很强大。相信用过的朋友都能明显感受到,不用for循环也能处理集合。做好手术。Stream以类似于使用SQL语句从数据库中查询数据的直观方式为Java集合操作和表示提供了高级抽象。这种风格把待处理的元素集合看成一个流,在管道中传输,可以在管道的节点上进行处理,如过滤、排序、聚合等,元素流通过中间操作处理在流水线中,最后通过终端操作得到前面处理的结果。运行过程如下:+--------------------++-----++-----++---++-------+|streamofelements+----->|filter+->|sorted+->|map+->|collect|+-------------------++------++-----++---++--------+使用StreamAPI可以大大提高Java程序员的工作效率,让程序员写出高-质量高效、干净、简洁的代码。接下来我们来对比一下实际的日常开发编程风格。学完Stream的编程风格,保证你会爱上它!2.遍历操作2.1.在日常开发中,我们经常需要遍历集合对象中的元素,例如我们会通过以下方式遍历元素,然后筛选出某个集合的某些字段,jdk7的操作:/***jdk7从集合对象中获取用户ID集合*@paramuserList*@return*/publicListgetUserIds(ListuserList){ListuserIds=newArrayList<>();for(Useruser:userList){userIds.add(user.getUserId());}returnuserIds;}当使用Stream编程后,只需要一行代码即可实现:/***jdk8从集合对象中获取用户ID集合*@paramuserList*@return*/publicListgetUserIds(ListuserList){ListuserIds=userList.stream().map(User::getUserId).collect(Collectors.toList());returnuserIds;}2.2。筛选元素筛选元素在日常开发中经常会遇到。比如在jdk7中,我们会这样操作:/***jdk7从集合对象*@paramuserList*@return*/publicListgetUserIds7(ListuserList){ListuserIds=newArrayList<>();for(Useruser:userList){if(user.getUserId()!=null){userIds.add(user.getUserId());}}returnuserIds;}使用Streamapi,我们只需要通过filter方法来过滤出需要的数据,就可以过滤出用户ID不为空的数据。/***jdk8从集合对象中过滤掉用户ID不为空的数据*@paramuserList*@return*/publicListgetUserIds8(ListuserList){ListuserIds=userList.stream().filter(item->item.getUserId()!=null).map(User::getUserId).collect(Collectors.toList());returnuserIds;}2.3。删除重复内容如果要返回集合内容排除重复数据,操作也很简单,合并时使用Collectors.toSet()即可!/***jdk8从集合中过滤掉用户ID不为空的数据对象,并删除重复项*@paramuserList*@return*/publicSetgetUserIds(ListuserList){SetuserIds=userList.stream().filter(item->item.getUserId()!=null).map(User::getUserId).collect(Collectors.toSet());returnuserIds;}2.4.数据类型转换在实际开发过程中,经常会出现数据类型定义不一致的问题。比如有的系统用String接受,有的用Long。对于这种场景,我们需要进行转换,操作很简单/***jdk8将Long类型的数据转换成String类型*@paramuserIds*@return*/publicListgetUserIds10(ListuserIds){ListuserIdStrs=userIds.stream().map(x->x.toString()).collect(Collectors.toList());returnuserIdStrs;}2.5.我们也会遇到数组转为集合的时候,前端传给我们的是数组,但是我们需要转为集合,使用streamapi操作操作也很简单!publicstaticvoidmain(String[]args){//创建一个字符串数组String[]strArray=newString[]{"a","b","c"};//转换后的List属于java.util.ArrayList即可执行正常的增删改查操作ListstrList=Stream.of(strArray).collect(Collectors.toList());}3.在实际开发过程中,还有一个使用频率最高的操作是使用集合元素中的主键字段作为key,元素作为value,实现集合转map的需求。这个需求在数据组装中用的比较多,尤其是在禁止连接表的SQL查询中对于运营公司来说,视图数据的组装只能在代码层面实现。例如,在下面的代码中,在角色表中关联了角色组ID信息。查询角色信息时,需要显示和处理角色组名称,使用map方法。匹配,效率会很高。实战代码案例分享//角色组ID设置SetroleGroupIds=newHashSet<>();//查询所有角色信息ListdbList=roleInfoMapper.findByPage(request);for(RoleInfosource:dbList){roleGroupIds.add(source.getRoleGroupId());RoleInfoDtoresult=newRoleInfoDto();BeanUtils.copyProperties(source,result);resultList.add(result);}//查询角色组信息if(CollectionUtils.isNotEmpty(roleGroupIds)){ListroleGroupInfoList=roleGroupInfoMapper.selectByIds(newArrayList<>(roleGroupIds));if(CollectionUtils.isNotEmpty(roleGroupInfoList)){//将List转换成Map,其中id主键作为key,对象为作为valueMapsourceMap=newHashMap<>();for(RoleGroupInfoDtoroleGroupInfo:roleGroupInfoList){sourceMap.put(roleGroupInfo.getId(),roleGroupInfo);}//封装角色组名for(RoleInfoDtoresult:resultList){if(sourceMap.containsKey(result.getRoleGroupId())){result.setRoleGroupName(sourceMap.get(result.getRoleGroupId()).getName());}}}}3.1。Settomap(不分组)在jdk7中,set中的元素被转移到map中,我们通常使用下面的方法。/***jdk7将collection转为Map,其中用户ID作为主键key*@paramuserList*@return*/publicMapgetMap(ListuserList){MapuserMap=newHashMap<>();for(Useruser:userList){userMap.put(user.getUserId(),user);}returnuserMap;}在jdk8中,使用streamapi方式,只需要一行代码实现/***jdk8将集合转换为Map,其中用户ID作为主键key。如果集合对象有重复键,第一个匹配的是主键*@paramuserList*@return*/publicMapgetMap(ListuserList){MapuserMap=userList.stream().collect(Collectors.toMap(User::getUserId,v->v,(k1,k2)->k1));returnuserMap;}打开Collectors.toMap方法的源码,来看看这是什么。publicstaticCollector>toMap(FunctionkeyMapper,FunctionvalueMapper,BinaryOperatormergeFunction){returntoMap(keyMapper,valueMapper,mergeFunction,HashMap::new);}从参数表可以看出:第一个参数:表示key第二个参数:表示value第三个参数:表示上述规则中的Collectors。toMap(User::getUserId,v->v,(k1,k2)->k1),表示以userId的内容为key,v->v表示以元素user为value,其中(k1,k2)->k1表示如果有相同的key,则第一个匹配到的元素作为内容,第二个则丢弃!3.2、设置为map(分组)在实际操作中,有一些场景需要我们使用同一个key,Addingtoacollection而不是overwriting,怎么办?如果采用jdk7,我们大概会这样做。/***jdk7将collection转为Map,将同一个key添加到一个collection中,实现分组*@paramuserList*@return*/publicMap>getMapGroup(ListuserList){Map>userListMap=newHashMap<>();for(Useruser:userList){if(userListMap.containsKey(user.getUserId())){userListMap.get(user.getUserId()).add(user);}else{Listusers=newArrayList<>();users.add(user);userListMap.put(user.getUserId(),users);}}returnuserListMap;}在jdk8中,使用的方式streamapi,我们只需要一行代码就可以实现/***jdk8将集合转为Map,将同一个key添加到一个集合中,实现分组*@paramuserList*@return*/publicMap>getMapGroup(ListuserList){Map>userMap=userList.stream().collect(Collectors.groupingBy(User::getUserId));returnuserMap;}4.分页操作的威力Streamapi的实现不仅是对集合进行各种组合操作,还支持分页操作。例如,将以下数组从小到大排序。排序完成后,从第一行开始查询10条数据。操作如下://要查询的数据Listnumbers=Arrays.asList(3,2,2,3,7,3,5,10,6,20,30,40,50,60,100);ListdataList=numbers.stream().sorted((x,y)->x.compareTo(y)).skip(0).limit(10).collect(Collectors.toList());系统.out.println(dataList.toString());其中skip参数表示行数,limit表示查询次数,类似于页面容量!5.搜索匹配操作streamapi也支持搜索集合,也支持正则匹配方式。allMatch(检查所有元素是否匹配)Listlist=Arrays.asList(10,5,7,3);booleanallMatch=list.stream()//.allMatch(x->x>2);//是否所有元素都大于2System.out.println(allMatch);findFirst(返回第一个元素)Listlist=Arrays.asList(10,5,7,3);Optionalfirst=list.stream()//.findFirst();Integerval=first.get();System.out.println(val);//输出10reduce(流中的元素可以重复组合得到一个值)Listlist=Arrays.asList(10,5,7,3);Integerresult=list.stream()//.reduce(2,Integer::sum);System.out.println(result);//输出27,其实相当于2+10+5+7+3,即,一个累计流api支持的操作方法很多,这里只列出几种,具体使用可以参考官网接口文档说明!6.并行运行所谓并行是指多个任务同时发生在不同的cpu上,不互相抢占资源;而并发是指多个任务同时发生在同一个时间点,但由同一个cpu处理。互相抢夺资源。这一点大家一定要分清楚,千万不要搞糊涂了!Streamapi的并行操作和串行操作只有一处不同,其他都是一样的。例如,我们使用parallelStream输出以下空字符串的个数:Liststrings=Arrays.asList("abc","","bc","efg","abcd","","jkl");//采用并行计算的方法得到空字符串的个数longcount=strings.parallelStream().filter(string->string.isEmpty()).count();在实际使用中,并行操作不一定比串行操作快!对于简单的操作,数量很大,如果服务器是多核,建议并行使用Stream!相反,使用串行操作更可靠!7.总结本文主要针对jdkstreamapi操作,结合实际日常开发需求,做一个简单的总结和分享。鉴于笔者知识的不足,可能会有一些理解不好的地方,欢迎广大网友批评指正!