0


Stream 流式编程

优质博文:IT-BLOG-CN

在这里插入图片描述

大家都知道可以将

  1. Collection

类转化成流

  1. Stream

进行操作(

  1. Map

并不能创建流),代码变得简约流畅。我们先看下流的几个特点:
1、流并不存储元素。这些元素可能存储在底层的集合中,或者是按需生成。
2、流的操作不会修改其数据元素,而是生成一个新的流。
3、流的操作是尽可能惰性执行的。这意味着直至需要其结果时,操作才会执行。

一、创建流

负责新建一个

  1. Stream

流,大多数都是基于现有的数组、

  1. List

  1. Set

  1. Map

等集合创建新的

  1. Stream

  1. stream()

创建一个

  1. stream

串行流对象。

**

  1. CR

时可优化的代码片段:**

  1. publicList<SFltStudent>toListByOldTicketNo(List<SFltStudent> sourceList){List<SFltStudent> targetList =Lists.newArrayListWithExpectedSize(sourceList.size());for(SFltStudent source : sourceList){SFltStudent target =newSFltStudent();
  2. target.setTicketNo(source.getOldTicketNo());
  3. target.setFlightAgency(source.getFlightAgency());
  4. targetList.add(target);}return targetList;}

代码优化:这里

  1. sourceList

如果数据量很大时,也可以考虑

  1. parallel stream

。这里主要是想通过

  1. stream

提高代码简洁性和可读性。

  1. publicList<SFltStudent>toListByOldTicketNo(List<SFltStudent> sourceList){return sourceList.stream().map(source ->{SFltStudent target =newSFltStudent();
  2. target.setTicketNo(source.getOldTicketNo());
  3. target.setFlightAgency(source.getFlightAgency());return target;}).collect(Collectors.toList());}

  1. parallelStream()

创建一个可并行执行的

  1. stream

流对象。可以有效利用计算机的多

  1. CPU

硬件,提升逻辑的执行速度。将一整个

  1. stream

划分为多个片段,然后对各个分片流并行执行处理逻辑,最后将各个分片流的执行结果汇总为一个整体流。

::: tip
如果遇到耗时的操作,或者大量

  1. IO

的操作,或者有线程

  1. sleep

的操作一定要避免使用并行流。

并行流场景效率会比迭代器逐个循环更高。
:::

查看

  1. parallelStream

的源码发现

  1. parallel Stream

底层是将任务进行了切分,最终将任务传递给了

  1. jdk8

自带的“全局”

  1. ForkJoinPool

线程池。在

  1. Fork-Join

中,比如一个拥有

  1. 4

个线程的ForkJoinPool线程池,有一个任务队列,一个大的任务切分出的子任务会提交到线程池的任务队列中,

  1. 4

个线程从任务队列中获取任务执行,哪个线程执行的任务快,哪个线程执行的任务就多,只有队列中没有任务线程才是空闲的,这就是工作窃取。

  1. /**
  2. * @return a possibly parallel {@code Stream} over the elements in this == parallelStream()并不一定返回一个并行流,有可能parallelStream()全是由主线程顺序执行的。
  3. * collection
  4. * @since 1.8
  5. */defaultStream<E>parallelStream(){returnStreamSupport.stream(spliterator(),true);}

注意:

  1. parallelStream

和整个

  1. java

进程共用

  1. ForkJoinPool

:如果直接使用

  1. parallelStream().foreach

会默认使用全局的

  1. ForkJoinPool

,而这样就会导致当前程序很多地方共用同一个线程池,包括

  1. gc

相关操作在内,所以一旦任务队列中满了之后,就会出现阻塞的情况,导致整个程序的只要当前使用

  1. ForkJoinPool

的地方都会出现问题。

**

  1. CR

时可优化的代码片段:** :并发获取接口数据,进行业务处理,对共享数据的修改需要考虑多线程安全问题。

  1. List<String> errorMessageList =Collections.synchronizedList(newArrayList<>());List<String> errorProductOrderIds =Collections.synchronizedList(newArrayList<>());
  2. infos.parallelStream().filter(XStudentOrderInfo::getChecked).map(XStudentOrderInfo::getProductOrderID).filter(StringUtils::isNotBlank).distinct().allMatch(productOrderId ->{XRefundResponse response = xStudentCancelSoa.xStudentClassOrder(getXStudentRequest(eid, refundInfo, productOrderId));boolean isSuccess = response.getResponseStatus()!=null&& response.getResponseStatus().ack ==AckCodeType.Success&& response.isIsSuccess()!=null&& response.isIsSuccess();if(!isSuccess &&StringUtils.isNotBlank(response.getMessage())){
  3. errorMessageList.add(response.getMessage());
  4. errorProductOrderIds.add(productOrderId);}return isSuccess;}));

代码优化:将复杂的条件判断提取到

  1. processOrder

方法中,使主流处理逻辑更加简洁和易读。

  1. List<String> errorMessageList =Collections.synchronizedList(newArrayList<>());List<String> errorProductOrderIds =Collections.synchronizedList(newArrayList<>());boolean allSuccess = infos.parallelStream().filter(XStudentOrderInfo::getChecked).map(XStudentOrderInfo::getProductOrderID).filter(StringUtils::isNotBlank).distinct().allMatch(productOrderId ->processOrder(productOrderId, errorMessageList, errorProductOrderIds));privatebooleanprocessOrder(String productOrderId,List<String> errorMessageList,List<String> errorProductOrderIds){XRefundResponse response = xStudentCancelSoa.xStudentClassOrder(getXStudentRequest(eid, refundInfo, productOrderId));boolean isSuccess = response.getResponseStatus()!=null&& response.getResponseStatus().ack ==AckCodeType.Success&&Boolean.TRUE.equals(response.isIsSuccess());if(!isSuccess &&StringUtils.isNotBlank(response.getMessage())){
  2. errorMessageList.add(response.getMessage());
  3. errorProductOrderIds.add(productOrderId);}return isSuccess;}

  1. Stream.of()

通过给定的一系列元素创建一个新的

  1. stream

串行流对象。

二、Stream 中间处理

输入

  1. Stream

对象,输出一个新的

  1. Stream

对象,中间管道操作可以进行叠加。

规范

  1. CR

时发现不规范的流式编程如下:

  1. issueBillList.stream().map(IssueBillDO::getIssueBillId).collect(Collectors.toList());

根据代码规范,在代码中使用链式调用时,为了提高代码的可读性和维护性,建议在方法链的每个方法调用之间进行换行。这样可以使代码更容易阅读和理解。

  1. List<Long> issueBillIds = issueBillList.stream().map(IssueBillDO::getIssueBillId).collect(Collectors.toList());

  1. filter()

按照条件过滤符合要求的元素,返回新的

  1. stream

流。

**

  1. CR

时可优化的代码片段:**

  1. .filter

多个过滤条件并存,存在一定的优化空间。编程如下:

  1. .filter(r ->StringUtilsExt.compareIgnoreSpaceAndCaps(r.getPassengerName(), trace.getPassengerName())&&StringUtilsExt.compareIgnoreSpaceAndCaps(r.getFlight(), trace.getFlightNo())&&StringUtilsExt.compareIgnoreSpaceAndCaps(r.getDPort(), trace.getDport()))......

建议根据业务将它们拆分为多个

  1. .filter

方法调用可以提高代码的可读性和可维护性。但是需要注意每个

  1. .filter

调用都会遍历一次流中的元素。如果流非常大,多个

  1. .filter

调用可能会带来性能开销。同时如果条件之间存在逻辑依赖关系,拆分成多个

  1. .filter

调用可能会导致逻辑错误。例如,如果某个条件的结果会影响另一个条件的判断,拆分可能会破坏这种依赖关系。虽然拆分可以提高某些情况下的可读性,但如果条件本身很简单,拆分反而会使代码显得冗长和复杂。

具体大家根据自己的业务特点进行选择

方案一:如果条件非常复杂,或者你希望每个条件都能单独清晰地表达,可以拆分成多个

  1. .filter

方法

  1. .filter(r ->StringUtilsExt.compareIgnoreSpaceAndCaps(r.getPassengerName(), trace.getTripInfo().getPassengerName())).filter(r ->StringUtilsExt.compareIgnoreSpaceAndCaps(r.getFlight(), trace.getTripInfo().getFlightNo())).filter(r ->StringUtilsExt.compareIgnoreSpaceAndCaps(r.getDPort(), trace.getTripInfo().getDport()))

方案二:如果条件逻辑非常复杂,考虑将条件封装到一个辅助方法中,这样代码会更加清晰

  1. .filter(r ->matchesTraceInfo(r, trace.getTripInfo()))privatebooleanmatchesTraceInfo(Record r,TripInfo tripInfo){returnStringUtilsExt.compareIgnoreSpaceAndCaps(r.getPassengerName(), tripInfo.getPassengerName())&&StringUtilsExt.compareIgnoreSpaceAndCaps(r.getFlight(), tripInfo.getFlightNo())&&StringUtilsExt.compareIgnoreSpaceAndCaps(r.getDPort(), tripInfo.getDport());}

  1. map()

将已有元素转换为另一个对象类型,一对一逻辑,返回新的

  1. stream

流。

  1. List<String> ids =Arrays.asList("A1","A2","A3");// 使用流操作List<String> results = ids.stream().map(id ->{
  2. id.replace("A","B");return id;}).collect(Collectors.toList());System.out.println(results);

执行之后,会发现每一个元素都被转换为对应新的元素,但是前后总元素个数是一致的:

  1. B1B2B3

下面的代码因对

  1. map

  1. filter

功能的混淆,导致代码执行解决与预期不符,最终出现生产故障。

  1. if(response !=null&& response.isPresent()&& response.isPresent().get().getResult()!=null){ResultType resultType = response.isPresent().get().getResult();
  2. resultType.getResultList().stream().map(p ->matchChildResult(p)&& p.getCode ==CODE_404).findFirst().ifPresent(result ->{
  3. logger.build("childdata", "fail:).info();if(ConfigFunc.getBoolean("childIntercept",false)){thrownewResultException("fail);}});

原因:如果使用

  1. map

这段代码会返回一个

  1. List<boolean>

的列表,应该不是开发者想要的。而且,只要

  1. respose

返回了结果,那么

  1. map

就会返回一个

  1. List<boolean>

列表,这个列表可能为:

  1. [true,false,......]

等等,开发者应该要的是满足条件才抛出错误的,但是生产应该是只要

  1. respose

返回了结果

  1. code

无论是不是

  1. 404

都会抛错。导致线上系统异常,订单下跌。

  1. flatMap()

将已有元素转换为另一个对象类型,一对多逻辑,即原来一个元素对象可能会转换为

  1. 1

个或者多个新类型的元素,返回新的

  1. stream

流。

案例:

  1. List<String> sentences =Arrays.asList("B1 B2","B3 B4");// 使用流操作List<String> results2 = sentences.stream().flatMap(sentence ->Arrays.stream(sentence.split(" "))).collect(Collectors.toList());System.out.println(results2);

执行之后,会发现每一个元素都被转换为多个新的元素:

  1. B1B2B3B4
  1. flatMap

操作是先将每个元素进行处理并返回一个新的

  1. Stream

,然后将多个

  1. Stream

展开合并为了一个完整的新的

  1. Stream

,如下:

**

  1. CR

时可优化的代码片段:** 应用场景为

  1. List

中的对象中包含

  1. List

列表

  1. List<SpecialEventMaterialInfo> allMaterialList = specialEventInfoForPageList.stream().filter(Objects::nonNull).filter(p ->CollectionUtils.isNotEmpty(p.getMaterialInfoList())).flatMap(p -> p.getMaterialInfoList().stream().filter(Objects::nonNull)).collect(Collectors.toList());

代码优化:提前检查

  1. p.getMaterialInfoList()

是否为空的处理,

  1. CollectionUtils

  1. Collectors

被频繁使用,可以进行静态导入以简化代码。

  1. List<SpecialEventMaterialInfo> allMaterialList = specialEventInfoForPageList.stream().filter(p -> p !=null&&isNotEmpty(p.getMaterialInfoList())).flatMap(p -> p.getMaterialInfoList().stream()).filter(Objects::nonNull).collect(toList());

  1. limit()

仅保留集合前面指定个数的元素,返回新的

  1. stream

流。

  1. Stream<Integer> integerStream =Arrays.stream({1,2,3}).limit(2);System.out.println(Arrays.toString(integerStream.toArray()));// [1, 2]

  1. skip()

跳过集合前面指定个数的元素,返回新的

  1. stream

流。

  1. Stream<Integer> integerStream =Arrays.stream({1,2,3});.skip(2);System.out.println(Arrays.toString(integerStream.toArray()));// [3]

  1. concat()

将两个流的数据合并起来为

  1. 1

个新的流,返回新的

  1. stream

流。

  1. distinct()

  1. Stream

中所有元素进行去重,返回新的

  1. stream

流。

  1. **

CR`时可优化的代码片段:**

  1. submitReiEntityList = model.getReibursementInfo().getSubmitReiEntityList().stream().map(ReibursementApplyOrderInfo::getOrderId).distinct().collect(Collectors.toList());

这里主要说一个思想,是否可以将需要

  1. distinct

的集合转换为

  1. Set

进行存储,提高查找效率。

  1. sorted()

  1. stream

中所有的元素按照指定规则进行排序,返回新的

  1. stream

流。

这里主要看一下目前存在的写法

  1. CR

片段一

  1. wordSet1 = wordSet.stream().sorted(newComparator<String>(){@Overridepublicintcompare(String o1,String o2){return o2.length()- o1.length();}}).collect(Collectors.toList());
  1. CR

片段二

  1. List<RescheduleLog> sortedLogs = logs.stream().sorted((RescheduleLog i1,RescheduleLog i2)-> i2.getRecordTime().compareTo(i1.getRecordTime())).collect(Collectors.toList());
  1. CR

片段三:上面的片段可以按照该规范,简化代码。

  1. List<RescheduleIssueBill> orderedDescList = rescheduleIssueBills.stream().sorted(Comparator.comparing(RescheduleIssueBill::getIssueBillID).reversed()).collect(Collectors.toList());
  1. CR

片段四

  1. List<RescheduleIssueBill> orderedDescList = rescheduleIssueBills
  2. .stream().sorted(Comparator.comparing(RescheduleIssueBill::getIssueBillID).reversed()).collect(Collectors.toList());

代码优化:如果不需要保留原始列表的顺序,可以直接对

  1. original

进行排序,避免创建额外的心列表。

  1. original.sort(Comparator.comparing(SegmentInfo::getSortedSequence));

  1. peek()

  1. stream

流中的每个元素进行逐个遍历处理,返回处理后的

  1. stream

流。意味着

  1. peek

只能作为管道中途的一个处理步骤,而没法直接执行得到结果,其后面必须还要有其它终止操作的时候才会被执行;而

  1. foreach

作为无返回值的终止方法,则可以直接执行相关操作。

  1. CR

过程中使用

  1. peek

的代码,

  1. peek

么有问题,但是代码还是有一定的优化空间。

  1. List<AllianceAuditInfo> auditSuccessList = auditInfoList.stream().filter(auditInfo ->AllianceAuditStatusEnum.AUDIT_SUCCESS.getValue().equals(auditInfo.getAuditStatus())).peek(auditInfo ->{Integer customKey = idxAtomic.getAndUpdate(idx -> idx +NumberUtils.INTEGER_ONE);
  2. auditInfo.setCustomKey(customKey);}).collect(Collectors.toList());

我们给一个更优雅的代码:

  1. List<AllianceAuditInfo> auditSuccessList = auditInfoList.stream().filter(auditInfo ->AllianceAuditStatusEnum.AUDIT_SUCCESS.getValue().equals(auditInfo.getAuditStatus())).peek(auditInfo -> auditInfo.setCustomKey(idxAtomic.getAndIncrement())).collect(Collectors.toList());

三、终止Stream

通过终止管道操作之后,

  1. Stream

流将会结束,最后可能会执行某些逻辑处理,或者是按照要求返回某些执行后的结果数据。

  1. count()

返回

  1. stream

处理后最终的元素个数。

**

  1. CR

时可优化的代码片段:**

  1. groupByDataType.entrySet().stream().allMatch(entry -> entry.getValue().stream().map(DiscountInfo::getDeductionAmount).distinct().count()==1);

代码优化:上述代码

  1. distinct

  1. count

结合使用时,可以使用

  1. Set

  1. length()

方法实现,但是这里使用

  1. count

  1. distinct

可能从业务上理解更为接近,所以具体需要根据业务场景决定。

  1. boolean allMatch = groupByDataType.entrySet().stream().allMatch(entry -> entry.getValue().stream().map(DiscountInfo::getDeductionAmount).collect(Collectors.toSet()).size()==1);

但是这里可以根据

  1. allMatch

的特性上进行优化,只要找到一个不满足条件的金额,就提前返回

  1. false

提交性能。

  1. boolean allMatch = groupByDataType.entrySet().stream().allMatch(entry ->{Set<BigDecimal> deductionAmounts = entry.getValue().stream().map(DiscountInfo::getDeductionAmount).collect(Collectors.toSet());return deductionAmounts.size()==1;});

  1. max()

返回

  1. stream

处理后的元素最大值。

**

  1. CR

时可优化的代码片段:**

  1. files.stream().mapToInt(UploadRetireMaterialInfoType::getBatchNo).max().getAsInt();

代码优化:这里主要的问题是,再调用

  1. getAsInt()

方法时,一定要判断下是否存在,否则回报异常。

  1. OptionalInt maxBatchNoOptional = files.stream().mapToInt(UploadRetireMaterialInfoType::getBatchNo).max();if(maxBatchNoOptional.isPresent()){int maxBatchNo = maxBatchNoOptional.getAsInt();}else{......}

  1. min()

返回

  1. stream

处理后的元素最小值。

  1. CR

过程中发现可以使用

  1. min()

方法进行优化的代码片段

  1. List<SFltticketStudentByairlineMy> sortRefundDetails = refundDetails.stream().sorted(Comparator.comparing(SFltticketStudentByairlineMy::getSequence)).collect(toList());SFltticketStudentByairlineMy firstSeqTicketNo = sortRefundDetails.get(0);

优化后代码如下:

  1. refundDetails.stream().min(Comparator.comparing(SFltticketStudentByairlineMy::getSequence));

  1. findFirst()

找到第一个符合条件的元素时则终止流处理。

优化片段一:

  1. CR

时发现

  1. .findFirst()

返回

  1. Optional

可以继续进行业务处理,存在一定的优化空间。代码如下:

  1. oc.getOrderInfoList().stream().filter(f ->(StringUtilsExt.compareIgnoreSpaceAndCaps(f.getFlight(), lastTrip.getFlightNo())......).findFirst().orElse(null);if(lastFlight !=null){......}

可以在

  1. findFirst()

方法后继续执行操作,而不需要单独的

  1. if (lastFlight != null)

语句。流式编程提供了

  1. ifPresent

方法,可以让你在找到符合条件的元素时执行某些操作。这样使代码更加简洁和流畅,不需要显式地进行空值检查。

  1. oc.getOrderInfoList().stream().filter(f ->(StringUtilsExt.compareIgnoreSpaceAndCaps(f.getFlight(), lastTrip.getFlightNo())......).findFirst().ifPresent(lastFlight ->{// 在这里执行你需要的操作// 例如:// System.out.println("Found flight: " + lastFlight);});

优化片段二:

  1. .findFirst()

方法使用存在优化空间

  1. List<SFltticketStudentByairlineMy> sortRefundDetails = refundDetails.stream().sorted(Comparator.comparing(SFltticketStudentByairlineMy::getSequence)).collect(toList());SFltticketStudentByairlineMy firstSeqTicketNo = sortRefundDetails.get(0);

使用

  1. .findFirst()

方法获取第一个符合要求的元素即可。当然这个代码还存在优化空间。

  1. SFltticketStudentByairlineMy firstSeqTicketNo = refundDetails.stream().sorted(Comparator.comparing(SFltticketStudentByairlineMy::getSequence)).collect(toList()).findFirst();

  1. findAny()

找到任何一个符合条件的元素时则退出流处理,这个对于串行流时与

  1. findFirst

相同,对于并行流时比较高效,任何分片中找到都会终止后续计算逻辑。

**

  1. CR

时可优化的代码片段:**

  1. orderInfo.getRefundInfoList().stream().filter(a ->MATERIAL_SUPPLEMENT_FLAG.equals(a.getKey())&&TRUE_VALUE.equals(a.getValue())).findAny().isPresent();

优化代码:返回的是一个

  1. boolean

类型,可以直接使用

  1. anyMatch()
  1. boolean isPresent = orderInfo.getRefundOrderFlagInfoList().stream().anyMatch(a ->MATERIAL_SUPPLEMENT_FLAG.equals(a.getKey())&&TRUE_VALUE.equals(a.getValue()));

  1. anyMatch()

返回一个

  1. boolean

值,类似于

  1. isContains()

,用于判断是否有符合条件的元素。

我们也会将写的标准的代码推荐给大家

  1. boolean isAgencyModeOrder =CollectionsUtil.isNotEmpty(orderAlibabaCartList)&& orderAlibabaCartList.stream().filter(s ->Objects.equals(s.getBookType(),BookingTypeConstants.TICKET_PLUS_X_ORDER)).anyMatch(s ->Objects.equals(s.getPaymentVersion(),PaymentVersionConstants.PAYMENT_AGENCY));

  1. allMatch()

返回一个

  1. boolean

值,用于判断是否所有元素都符合条件。

  1. CR

中发现可以优化的代码:在流操作中

  1. fucLi

部分存在优化空间。

  1. privateStream<AllianceAuditDTO>doFilter(List<AllianceAuditDTO> sourceList){return sourceList.stream().filter(
  2. source ->{List<Supplier<Boolean>> fucLi =buildFilterConditions(source);return fucLi.stream().allMatch(Supplier::get);});}

代码是一个过滤方法,它将一个

  1. List<AllianceAuditDTO>

转换为一个

  1. Stream<AllianceAuditDTO>

,并根据某些条件对其进行过滤。具体来说,它使用了

  1. buildFilterConditions

方法来生成一组

  1. Supplier<Boolean>

,然后检查这些条件是否都满足。如果所有条件都满足,则保留该元素。

优化后的代码:将

  1. fucLi

变量内联到

  1. filter

方法中,减少了不必要的局部变量声明,使代码更加简洁。

  1. privateStream<AllianceAuditDTO>doFilter(List<AllianceAuditDTO> sourceList){return sourceList.stream().filter(source ->buildFilterConditions(source).stream().allMatch(Supplier::get));}

  1. noneMatch()

返回一个

  1. boolean

值, 用于判断是否所有元素都不符合条件。

**

  1. CR

时可优化的代码片段:**

  1. boolean userBehaviorsCheck = filterRecordList.stream().noneMatch(record ->IntegerUtils.compare(record.getPageCode(),201));

  1. collect()

将流转换为指定的类型,通过

  1. Collectors

进行指定。

  1. toArray()

将流转换为数组。

  1. iterator()

将流转换为

  1. Iterator

对象。

**

  1. CR

时可优化的代码片段:**

  1. Iterator<M_RelateAliPassenger> iterator = passengers.iterator();while(iterator.hasNext()){M_RelateAliPassenger passenger = iterator.next();boolean matched = passengers2.stream().anyMatch(p -> p.getPassengerName()!=null&& p.getPassengerName().equalsIgnoreCase(passenger.getPassengerName()));if(!matched){
  2. iterator.remove();}}

优化后的代码:主要任务是从

  1. passengers

列表中移除那些在

  1. passengers2

列表中没有匹配的乘客。可以通过集合操作来简化和优化这段代码。

  1. passengers.removeIf(passenger ->
  2. passengers2.stream().noneMatch(p -> p.getPassengerName()!=null&& p.getPassengerName().equalsIgnoreCase(passenger.getPassengerName())));

  1. foreach()

无返回值,对元素进行逐个遍历,然后执行给定的处理逻辑。

  1. foreach()

操作与

  1. parallelStream()

搭配使用时,必须保证是线程安全的。也不要直接使用默认的线程池。

**

  1. CR

时可优化的代码片段:**

  1. parameterList.forEach(param -> orderIds.append(param.getOrderID()).append(","));

优化后的代码:

  1. Collectors.joining(",")

最适合做上述的工作,应该是首先想到的。

  1. String orderIds = parameterList.stream().map(param -> param.getOrderID()).collect(Collectors.joining(","));

常见问题

一旦一个

  1. Stream

被执行了终止操作之后,后续便不可以再读这个流执行其他的操作了,否则会报错,看下面示例:

  1. publicvoidtestHandleStreamAfterClosed(){List<String> ids =Arrays.asList("205","10","308","49","627","193","111","193");Stream<String> stream = ids.stream().filter(s -> s.length()>2);// 统计stream操作后剩余的元素个数System.out.println(stream.count());System.out.println("-----下面会报错-----");// 判断是否有元素值等于205try{System.out.println(stream.anyMatch("205"::equals));}catch(Exception e){
  2. e.printStackTrace();System.out.println(e.toString());}System.out.println("-----上面会报错-----");}

结果:

  1. -----下面会报错-----java.lang.IllegalStateException: stream has already been operated upon or closed
  2. -----上面会报错-----java.lang.IllegalStateException: stream has already been operated upon or closed
  3. at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
  4. at java.util.stream.ReferencePipeline.anyMatch(ReferencePipeline.java:516)
  5. at Solution_0908.main(Solution_0908.java:55)

因为

  1. stream

已经被执行

  1. count()

终止方法了,所以对

  1. stream

再执行

  1. anyMatch

方法的时候,就会报错

  1. stream has already been operated upon or closed

,这一点在使用的时候需要特别注意。

四、collect方法

获取一个集合类的结果对象,比如

  1. List

  1. Set

或者

  1. HashMap

等。

  1. Collectors.toList()
  1. List<NormalOfferModel> collectList = normalOfferModelList
  2. .stream().filter(offer -> offer.getCate1LevelId().equals("11")).collect(Collectors.toList());

  1. Collectors.toSet()
  1. Set<NormalOfferModel> collectSet = normalOfferModelList
  2. .stream().filter(offer -> offer.getCate1LevelId().equals("22")).collect(Collectors.toSet());

  1. Collectors.toMap

CodeReview 时发现的问题:没有考虑

  1. key

重复问题。

  1. Arrays.stream(clazz.getDeclaredFields()).collect(Collectors.toMap(r -> r.getName().toLowerCase(), r -> r));

优化后的代码:

  1. Function.identity()

  1. java.util.function.Function

接口中的一个静态方法。它总是返回一个其输入参数的函数。这在需要传递一个不做任何变换的函数时非常有用。

  1. Function.identity()

等价于上面的

  1. r -> r

  1. (k1, k2) -> k2

就是解决重复

  1. key

的问题,当存在重复

  1. key

时使用最后一个

  1. key

  1. Arrays.stream(clazz.getDeclaredFields()).collect(NormalOfferModel::getName,Function.identity(),(k1, k2)-> k2));

  1. Collectors.joining
  1. List<String> ids =Arrays.asList("205","10","308","49","627","193","111","193");String joinResult = ids.stream().collect(Collectors.joining(","));

  1. Collectors.averagingInt
  1. List<Integer> ids =Arrays.asList(10,20,30,40,50);// 计算平均值Double average = ids.stream().collect(Collectors.averagingInt(value -> value));

  1. Collectors.summarizingInt
  1. List<Integer> ids =Arrays.asList(10,20,30,40,50);// 数据统计信息IntSummaryStatistics summary = ids.stream().collect(Collectors.summarizingInt(value -> value));

Optional 类

  1. ifPresent(Consumer<? super T> action)

如果

  1. Optional

中包含值,执行给定的

  1. Consumer

操作,否则什么也不做。常用于简化代码,避免显式的空值检查。

  1. isPresent()

检查

  1. Optional

中是否包含值。如果包含值,返回

  1. true

,否则返回

  1. false

  1. get()

如果

  1. Optional

中包含值,返回该值;否则抛出

  1. NoSuchElementException

。这个方法不推荐频繁使用,因为它违背了

  1. Optional

的初衷,即避免显式的空值检查和异常处理。

  1. orElse(T other)

如果

  1. Optional

中包含值,返回该值;否则返回

  1. other

。常用于提供默认值。

  1. orElseGet(Supplier<? extends T> other)

如果

  1. Optional

中包含值,返回该值;否则通过调用

  1. Supplier

获取一个默认值。与

  1. orElse

不同的是,

  1. Supplier

只有在需要时才会被调用,因此适用于生成默认值开销较大的情况。

  1. isEmpty()

检查

  1. Optional

中是否为空。如果为空,返回

  1. true

,否则返回

  1. false

  1. orElseThrow()

如果

  1. Optional

中包含值,返回该值;否则抛出

  1. NoSuchElementException

  1. optional.orElseThrow(()->newIllegalArgumentException("Value is absent"));

  1. orElseThrow(Supplier<? extends X> exceptionSupplier)

如果

  1. Optional

中包含值,返回该值;否则通过

  1. Supplier

抛出指定的异常。

  1. filter(Predicate<? super T> predicate)

如果

  1. Optional

中包含值,并且该值满足给定的谓词,返回一个包含该值的

  1. Optional

;否则返回一个空的

  1. Optional

。常用于条件过滤。

  1. Optional<String> filtered = optional.filter(value -> value.length()>3);

  1. map(Function<? super T, ? extends U> mapper)

如果

  1. Optional

中包含值,应用给定的函数并返回一个包含映射结果的

  1. Optional

;否则返回一个空的

  1. Optional

。常用于链式调用。

  1. Optional<Integer> length = optional.map(String::length);

  1. flatMap(Function<? super T, Optional<U>> mapper)

  1. map

类似,但

  1. mapper

函数返回的是一个

  1. Optional

对象,并且不会对返回的

  1. Optional

进行嵌套。

  1. Optional<String> name = optional.flatMap(value ->Optional.of("Processed "+ value));
标签: 数据库 java 面试

本文转载自: https://blog.csdn.net/zhengzhaoyang122/article/details/141465840
版权归原作者 程序猿进阶 所有, 如有侵权,请联系我们删除。

“Stream 流式编程”的评论:

还没有评论