Stream 流式编程

打印 上一主题 下一主题

主题 1583|帖子 1583|积分 4749

优质博文:IT-BLOG-CN

各人都知道可以将Collection类转化成流Stream举行操作(Map并不能创建流),代码变得简约流畅。我们先看下游的几个特点:
1、流并不存储元素。这些元素可能存储在底层的聚集中,大概是按需生成。
2、流的操作不会修改其数据元素,而是生成一个新的流。
3、流的操作是尽可能惰性执行的。这意味着直至必要其结果时,操作才会执行。
一、创建流

负责新建一个Stream流,大多数都是基于现有的数组、List、Set、Map等聚集创建新的Stream。
stream()

创建一个stream串行流对象。
CR时可优化的代码片断:
  1. public List<SFltStudent> toListByOldTicketNo(List<SFltStudent> sourceList) {
  2.   List<SFltStudent> targetList = Lists.newArrayListWithExpectedSize(sourceList.size());
  3.   for (SFltStudent source : sourceList) {
  4.     SFltStudent target = new SFltStudent();
  5.     target.setTicketNo(source.getOldTicketNo());
  6.     target.setFlightAgency(source.getFlightAgency());
  7.     targetList.add(target);
  8.   }
  9.   return targetList;
  10. }
复制代码
代码优化:这里sourceList如果数据量很大时,也可以思量parallel stream。这里紧张是想通过stream提高代码简洁性和可读性。
  1. public List<SFltStudent> toListByOldTicketNo(List<SFltStudent> sourceList) {
  2.     return sourceList.stream()
  3.             .map(source -> {
  4.                 SFltStudent target = new SFltStudent();
  5.                 target.setTicketNo(source.getOldTicketNo());
  6.                 target.setFlightAgency(source.getFlightAgency());
  7.                 return target;
  8.             })
  9.             .collect(Collectors.toList());
  10. }
复制代码
parallelStream()

创建一个可并行执行的stream流对象。可以有用利用计算机的多CPU硬件,提升逻辑的执行速率。将一整个stream分别为多个片断,然后对各个分片流并行执行处置惩罚逻辑,末了将各个分片流的执行结果汇总为一个整体流。
::: tip
如果遇到耗时的操作,大概大量IO的操作,大概有线程sleep的操作肯定要避免使用并行流。
并行流场景服从会比迭代器逐个循环更高。
:::
检察parallelStream的源码发现parallel Stream底层是将任务举行了切分,最终将任务传递给了jdk8自带的“全局”ForkJoinPool线程池。在Fork-Join中,比如一个拥有4个线程的ForkJoinPool线程池,有一个任务队列,一个大的任务切分出的子任务会提交到线程池的任务队列中,4个线程从任务队列中获取任务执行,哪个线程执行的任务快,哪个线程执行的任务就多,只有队列中没有任务线程才是空闲的,这就是工作窃取。
  1. /**
  2. * @return a possibly parallel {@code Stream} over the elements in this == parallelStream()并不一定返回一个并行流,有可能parallelStream()全是由主线程顺序执行的。
  3. * collection
  4. * @since 1.8
  5. */
  6. default Stream<E> parallelStream() {
  7.     return StreamSupport.stream(spliterator(), true);
  8. }
复制代码
注意:parallelStream和整个java进程共用ForkJoinPool:如果直接使用parallelStream().foreach会默认使用全局的ForkJoinPool,而这样就会导致当前程序很多地方共用同一个线程池,包括gc干系操作在内,以是一旦任务队列中满了之后,就会出现阻塞的情况,导致整个程序的只要当前使用ForkJoinPool的地方都会出现问题。
CR时可优化的代码片断: :并发获取接口数据,举行业务处置惩罚,对共享数据的修改必要思量多线程安全问题。
  1. List<String> errorMessageList = Collections.synchronizedList(new ArrayList<>());
  2. List<String> errorProductOrderIds = Collections.synchronizedList(new ArrayList<>());
  3. infos.parallelStream()
  4.     .filter(XStudentOrderInfo::getChecked)
  5.     .map(XStudentOrderInfo::getProductOrderID)
  6.     .filter(StringUtils::isNotBlank)
  7.     .distinct()
  8.     .allMatch(productOrderId -> {
  9.         XRefundResponse response = xStudentCancelSoa.xStudentClassOrder(getXStudentRequest(eid, refundInfo, productOrderId));
  10.         boolean isSuccess = response.getResponseStatus() != null
  11.             && response.getResponseStatus().ack == AckCodeType.Success
  12.             && response.isIsSuccess() != null
  13.             && response.isIsSuccess();
  14.         if (!isSuccess && StringUtils.isNotBlank(response.getMessage())) {
  15.             errorMessageList.add(response.getMessage());
  16.             errorProductOrderIds.add(productOrderId);
  17.         }
  18.         return isSuccess;
  19.     })
  20. );
复制代码
代码优化:将复杂的条件判断提取到processOrder方法中,使主流处置惩罚逻辑更加简洁和易读。
  1. List<String> errorMessageList = Collections.synchronizedList(new ArrayList<>());
  2. List<String> errorProductOrderIds = Collections.synchronizedList(new ArrayList<>());
  3. boolean allSuccess = infos.parallelStream()
  4.     .filter(XStudentOrderInfo::getChecked)
  5.     .map(XStudentOrderInfo::getProductOrderID)
  6.     .filter(StringUtils::isNotBlank)
  7.     .distinct()
  8.     .allMatch(productOrderId -> processOrder(productOrderId, errorMessageList, errorProductOrderIds));
  9. private boolean processOrder(String productOrderId, List<String> errorMessageList, List<String> errorProductOrderIds) {
  10.     XRefundResponse response = xStudentCancelSoa.xStudentClassOrder(getXStudentRequest(eid, refundInfo, productOrderId));
  11.     boolean isSuccess = response.getResponseStatus() != null
  12.             && response.getResponseStatus().ack == AckCodeType.Success
  13.             && Boolean.TRUE.equals(response.isIsSuccess());
  14.     if (!isSuccess && StringUtils.isNotBlank(response.getMessage())) {
  15.         errorMessageList.add(response.getMessage());
  16.         errorProductOrderIds.add(productOrderId);
  17.     }
  18.     return isSuccess;
  19. }
复制代码
Stream.of()

通过给定的一系列元素创建一个新的stream串行流对象。
二、Stream 中心处置惩罚

输入Stream对象,输出一个新的Stream对象,中心管道操作可以举行叠加。
规范

CR时发现不规范的流式编程如下:
  1. issueBillList.stream().map(IssueBillDO::getIssueBillId).collect(Collectors.toList());
复制代码
根据代码规范,在代码中使用链式调用时,为了提高代码的可读性和维护性,建议在方法链的每个方法调用之间举行换行。这样可以使代码更容易阅读和明白。
  1. List<Long> issueBillIds = issueBillList.stream()
  2.                                        .map(IssueBillDO::getIssueBillId)
  3.                                        .collect(Collectors.toList());
复制代码
filter()

按照条件过滤符合要求的元素,返回新的stream流。
CR时可优化的代码片断: .filter多个过滤条件并存,存在肯定的优化空间。编程如下:
  1. .filter(r -> StringUtilsExt.compareIgnoreSpaceAndCaps(r.getPassengerName(), trace.getPassengerName())
  2.             && StringUtilsExt.compareIgnoreSpaceAndCaps(r.getFlight(), trace.getFlightNo())
  3.             && StringUtilsExt.compareIgnoreSpaceAndCaps(r.getDPort(), trace.getDport()))
  4.             ......
复制代码
建议根据业务将它们拆分为多个.filter方法调用可以提高代码的可读性和可维护性。但是必要注意每个.filter调用都会遍历一次流中的元素。如果流非常大,多个.filter调用可能会带来性能开销。同时如果条件之间存在逻辑依赖关系,拆分成多个.filter调用可能会导致逻辑错误。例如,如果某个条件的结果会影响另一个条件的判断,拆分可能会粉碎这种依赖关系。虽然拆分可以提高某些情况下的可读性,但如果条件自己很简朴,拆分反而会使代码显得冗长和复杂。
具体各人根据自己的业务特点举行选择
方案一:如果条件非常复杂,大概你希望每个条件都能单独清晰地表达,可以拆分成多个.filter方法
  1. .filter(r -> StringUtilsExt.compareIgnoreSpaceAndCaps(r.getPassengerName(), trace.getTripInfo().getPassengerName()))
  2. .filter(r -> StringUtilsExt.compareIgnoreSpaceAndCaps(r.getFlight(), trace.getTripInfo().getFlightNo()))
  3. .filter(r -> StringUtilsExt.compareIgnoreSpaceAndCaps(r.getDPort(), trace.getTripInfo().getDport()))
复制代码
方案二:如果条件逻辑非常复杂,思量将条件封装到一个辅助方法中,这样代码会更加清晰
  1. .filter(r -> matchesTraceInfo(r, trace.getTripInfo()))
  2. private boolean matchesTraceInfo(Record r, TripInfo tripInfo) {
  3.     return StringUtilsExt.compareIgnoreSpaceAndCaps(r.getPassengerName(), tripInfo.getPassengerName()) &&
  4.            StringUtilsExt.compareIgnoreSpaceAndCaps(r.getFlight(), tripInfo.getFlightNo()) &&
  5.            StringUtilsExt.compareIgnoreSpaceAndCaps(r.getDPort(), tripInfo.getDport());
  6. }
复制代码
map()

将已有元素转换为另一个对象范例,一对一逻辑,返回新的stream流。

  1. List<String> ids = Arrays.asList("A1", "A2", "A3");
  2.         // 使用流操作
  3. List<String> results = ids.stream()
  4.         .map(id -> {
  5.             id.replace("A","B");
  6.             return id;
  7.         })
  8.         .collect(Collectors.toList());
  9. System.out.println(results);
复制代码
执行之后,会发现每一个元素都被转换为对应新的元素,但是前后总元素个数是一致的:
  1. B1
  2. B2
  3. B3
复制代码
下面的代码因对map和filter功能的混淆,导致代码执行解决与预期不符,最终出现生产故障。
  1. if (response  != null && response.isPresent() && response.isPresent().get().getResult() != null) {
  2.         ResultType resultType = response.isPresent().get().getResult();
  3.         resultType.getResultList().stream()
  4.                                 .map(p -> matchChildResult(p) && p.getCode == CODE_404)
  5.                                 .findFirst().ifPresent(result -> {
  6.                                     logger.build("childdata", "fail:).info();
  7.                                     if (ConfigFunc.getBoolean("childIntercept", false)) {
  8.                                         throw new ResultException("fail);
  9.                                     }
  10.                                 });
复制代码
原因:如果使用map这段代码会返回一个List<boolean>的列表,应该不是开发者想要的。而且,只要respose返回了结果,那么map就会返回一个List<boolean>列表,这个列表可能为:[true,false,......]等等,开发者应该要的是满意条件才抛出错误的,但是生产应该是只要respose返回了结果code无论是不是404都会抛错。导致线上系统异常,订单下跌。
flatMap()

将已有元素转换为另一个对象范例,一对多逻辑,即原来一个元素对象可能会转换为1个大概多个新范例的元素,返回新的stream流。

案例:
  1. List<String> sentences = Arrays.asList("B1 B2","B3 B4");
  2. // 使用流操作
  3. List<String> results2 = sentences.stream()
  4.         .flatMap(sentence -> Arrays.stream(sentence.split(" ")))
  5.         .collect(Collectors.toList());
  6. System.out.println(results2);
复制代码
执行之后,会发现每一个元素都被转换为多个新的元素:
  1. B1
  2. B2
  3. B3
  4. B4
复制代码
flatMap操作是先将每个元素举行处置惩罚并返回一个新的Stream,然后将多个Stream展开合并为了一个完整的新的Stream,如下:

CR时可优化的代码片断: 应用场景为List中的对象中包罗List列表
  1. List<SpecialEventMaterialInfo> allMaterialList = specialEventInfoForPageList.stream()
  2.     .filter(Objects::nonNull)
  3.     .filter(p -> CollectionUtils.isNotEmpty(p.getMaterialInfoList()))
  4.     .flatMap(p -> p.getMaterialInfoList().stream().filter(Objects::nonNull))
  5.     .collect(Collectors.toList());
复制代码
代码优化:提前查抄p.getMaterialInfoList()是否为空的处置惩罚,CollectionUtils和Collectors被频繁使用,可以举行静态导入以简化代码。
  1. List<SpecialEventMaterialInfo> allMaterialList = specialEventInfoForPageList.stream()
  2.     .filter(p -> p != null && isNotEmpty(p.getMaterialInfoList()))
  3.     .flatMap(p -> p.getMaterialInfoList().stream())
  4.     .filter(Objects::nonNull)
  5.     .collect(toList());
复制代码
limit()

仅保存聚集前面指定个数的元素,返回新的stream流。
  1. Stream<Integer> integerStream = Arrays.stream({1, 2, 3})
  2.                                       .limit(2);
  3. System.out.println(Arrays.toString(integerStream.toArray())); // [1, 2]
复制代码
skip()

跳过聚集前面指定个数的元素,返回新的stream流。
  1. Stream<Integer> integerStream = Arrays.stream({1, 2, 3});
  2.                                       .skip(2);
  3. System.out.println(Arrays.toString(integerStream.toArray())); // [3]
复制代码
concat()

将两个流的数据合并起来为1个新的流,返回新的stream流。
distinct()

对Stream中全部元素举行去重,返回新的stream流。
**CR`时可优化的代码片断:**
  1. submitReiEntityList = model.getReibursementInfo().getSubmitReiEntityList().stream()
  2.     .map(ReibursementApplyOrderInfo::getOrderId)
  3.     .distinct()
  4.     .collect(Collectors.toList());
复制代码
这里紧张说一个思想,是否可以将必要distinct的聚集转换为Set举行存储,提高查找服从。
sorted()

对stream中全部的元素按照指定规则举行排序,返回新的stream流。
这里紧张看一下目前存在的写法
CR片断一
  1. wordSet1 = wordSet.stream().sorted(new Comparator<String>() {
  2.     @Override
  3.     public int compare(String o1, String o2) {
  4.         return o2.length() - o1.length();
  5.     }
  6. }).collect(Collectors.toList());
复制代码
CR片断二
  1. List<RescheduleLog> sortedLogs = logs.stream()
  2.     .sorted((RescheduleLog i1, RescheduleLog i2) -> i2.getRecordTime().compareTo(i1.getRecordTime()))
  3.     .collect(Collectors.toList());
复制代码
CR片断三:上面的片断可以按照该规范,简化代码。
  1. List<RescheduleIssueBill> orderedDescList = rescheduleIssueBills.stream()
  2.     .sorted(Comparator.comparing(RescheduleIssueBill::getIssueBillID).reversed())
  3.     .collect(Collectors.toList());
复制代码
CR片断四
  1. List<RescheduleIssueBill> orderedDescList = rescheduleIssueBills
  2.     .stream()
  3.     .sorted(Comparator.comparing(RescheduleIssueBill::getIssueBillID).reversed())
  4.     .collect(Collectors.toList());
复制代码
代码优化:如果不必要保存原始列表的序次,可以直接对original举行排序,避免创建额外的心列表。
  1. original.sort(Comparator.comparing(SegmentInfo::getSortedSequence));
复制代码
peek()

对stream流中的每个元素举行逐个遍历处置惩罚,返回处置惩罚后的stream流。意味着peek只能作为管道中途的一个处置惩罚步调,而没法直接执行得到结果,其背面必须还要有别的终止操作的时候才会被执行;而foreach作为无返回值的终止方法,则可以直接执行干系操作。
CR过程中使用peek的代码,peek么有问题,但是代码还是有肯定的优化空间。
  1. List<AllianceAuditInfo> auditSuccessList = auditInfoList.stream()
  2.     .filter(auditInfo -> AllianceAuditStatusEnum.AUDIT_SUCCESS.getValue().equals(auditInfo.getAuditStatus()))
  3.     .peek(auditInfo -> {
  4.         Integer customKey = idxAtomic.getAndUpdate(idx -> idx + NumberUtils.INTEGER_ONE);
  5.         auditInfo.setCustomKey(customKey);
  6.     })
  7.     .collect(Collectors.toList());
复制代码
我们给一个更优雅的代码:
  1. List<AllianceAuditInfo> auditSuccessList = auditInfoList.stream()
  2.     .filter(auditInfo -> AllianceAuditStatusEnum.AUDIT_SUCCESS.getValue().equals(auditInfo.getAuditStatus()))
  3.     .peek(auditInfo -> auditInfo.setCustomKey(idxAtomic.getAndIncrement()))
  4.     .collect(Collectors.toList());
复制代码
三、终止Stream

通过终止管道操作之后,Stream流将会竣事,末了可能会执行某些逻辑处置惩罚,大概是按照要求返回某些执行后的结果数据。
count()

返回stream处置惩罚后最终的元素个数。
CR时可优化的代码片断:
  1. groupByDataType.entrySet().stream()
  2.     .allMatch(entry -> entry.getValue().stream()
  3.         .map(DiscountInfo::getDeductionAmount)
  4.         .distinct()
  5.         .count() == 1);
复制代码
代码优化:上述代码distinct与count联合使用时,可以使用Set与length()方法实现,但是这里使用count和distinct可能从业务上明白更为接近,以是具体必要根据业务场景决定。
  1. boolean allMatch = groupByDataType.entrySet().stream()
  2.     .allMatch(entry -> entry.getValue().stream()
  3.         .map(DiscountInfo::getDeductionAmount)
  4.         .collect(Collectors.toSet())
  5.         .size() == 1);
复制代码
但是这里可以根据allMatch的特性上举行优化,只要找到一个不满意条件的金额,就提前返回false提交性能。
  1. boolean allMatch = groupByDataType.entrySet().stream()
  2.     .allMatch(entry -> {
  3.         Set<BigDecimal> deductionAmounts = entry.getValue().stream()
  4.             .map(DiscountInfo::getDeductionAmount)
  5.             .collect(Collectors.toSet());
  6.         return deductionAmounts.size() == 1;
  7.     });
复制代码
max()

返回stream处置惩罚后的元素最大值。
CR时可优化的代码片断:
  1. files.stream()
  2.      .mapToInt(UploadRetireMaterialInfoType::getBatchNo)
  3.      .max()
  4.      .getAsInt();
复制代码
代码优化:这里紧张的问题是,再调用getAsInt()方法时,肯定要判断下是否存在,否则回报异常。
  1. OptionalInt maxBatchNoOptional = files.stream()
  2.             .mapToInt(UploadRetireMaterialInfoType::getBatchNo)
  3.             .max();
  4.         if (maxBatchNoOptional.isPresent()) {
  5.             int maxBatchNo = maxBatchNoOptional.getAsInt();
  6.         } else {
  7.             ......
  8.         }
复制代码
min()

返回stream处置惩罚后的元素最小值。
CR过程中发现可以使用min()方法举行优化的代码片断
  1. List<SFltticketStudentByairlineMy> sortRefundDetails = refundDetails.stream()
  2.     .sorted(Comparator.comparing(SFltticketStudentByairlineMy::getSequence))
  3.     .collect(toList());
  4. SFltticketStudentByairlineMy firstSeqTicketNo = sortRefundDetails.get(0);
复制代码
优化子女码如下:
  1. refundDetails.stream()
  2.     .min(Comparator.comparing(SFltticketStudentByairlineMy::getSequence));
复制代码
findFirst()

找到第一个符合条件的元素时则终止流处置惩罚。
优化片断一:
CR时发现.findFirst()返回Optional可以继续举行业务处置惩罚,存在肯定的优化空间。代码如下:
  1. oc.getOrderInfoList().stream()
  2.     .filter(f -> (StringUtilsExt.compareIgnoreSpaceAndCaps(f.getFlight(), lastTrip.getFlightNo())
  3.             ......)
  4.     .findFirst().orElse(null);
  5.     if (lastFlight != null) {
  6.         ......
  7.     }
复制代码
可以在findFirst()方法后继续执行操作,而不必要单独的if (lastFlight != null)语句。流式编程提供了ifPresent方法,可以让你在找到符合条件的元素时执行某些操作。这样使代码更加简洁和流畅,不必要显式地举行空值查抄。
  1. oc.getOrderInfoList().stream()
  2.     .filter(f -> (StringUtilsExt.compareIgnoreSpaceAndCaps(f.getFlight(), lastTrip.getFlightNo())
  3.             ......)
  4.     .findFirst()
  5.     .ifPresent(lastFlight -> {
  6.         // 在这里执行你需要的操作
  7.         // 例如:
  8.         // System.out.println("Found flight: " + lastFlight);
  9.     });
复制代码
优化片断二:
对.findFirst()方法使用存在优化空间
  1. List<SFltticketStudentByairlineMy> sortRefundDetails = refundDetails.stream()
  2.     .sorted(Comparator.comparing(SFltticketStudentByairlineMy::getSequence))
  3.     .collect(toList());
  4. SFltticketStudentByairlineMy firstSeqTicketNo = sortRefundDetails.get(0);
复制代码
使用.findFirst()方法获取第一个符合要求的元素即可。固然这个代码还存在优化空间。
  1. SFltticketStudentByairlineMy firstSeqTicketNo = refundDetails.stream()
  2.     .sorted(Comparator.comparing(SFltticketStudentByairlineMy::getSequence))
  3.     .collect(toList())
  4.     .findFirst();
复制代码
findAny()

找到任何一个符合条件的元素时则退出流处置惩罚,这个对于串行流时与findFirst相同,对于并行流时比较高效,任何分片中找到都会终止后续计算逻辑。
CR时可优化的代码片断:
  1. orderInfo.getRefundInfoList().stream()
  2.     .filter(a -> MATERIAL_SUPPLEMENT_FLAG.equals(a.getKey()) && TRUE_VALUE.equals(a.getValue()))
  3.     .findAny()
  4.     .isPresent();
复制代码
优化代码:返回的是一个boolean范例,可以直接使用anyMatch()
  1. boolean isPresent = orderInfo.getRefundOrderFlagInfoList().stream()
  2.     .anyMatch(a -> MATERIAL_SUPPLEMENT_FLAG.equals(a.getKey()) && TRUE_VALUE.equals(a.getValue()));
复制代码
anyMatch()

返回一个boolean值,雷同于isContains(),用于判断是否有符合条件的元素。
我们也会将写的尺度的代码保举给各人
  1. boolean isAgencyModeOrder = CollectionsUtil.isNotEmpty(orderAlibabaCartList)
  2.         && orderAlibabaCartList.stream()
  3.                                 .filter(s -> Objects.equals(s.getBookType(), BookingTypeConstants.TICKET_PLUS_X_ORDER))
  4.                                 .anyMatch(s -> Objects.equals(s.getPaymentVersion(), PaymentVersionConstants.PAYMENT_AGENCY));
复制代码
allMatch()

返回一个boolean值,用于判断是否全部元素都符合条件。
在CR中发现可以优化的代码:在流操作中fucLi部分存在优化空间。
  1. private Stream<AllianceAuditDTO> doFilter(List<AllianceAuditDTO> sourceList) {
  2.     return sourceList.stream()
  3.             .filter(
  4.                     source -> {
  5.                         List<Supplier<Boolean>> fucLi =
  6.                                 buildFilterConditions(source);
  7.                         return fucLi.stream().allMatch(Supplier::get);
  8.                     });
  9. }
复制代码
代码是一个过滤方法,它将一个List<AllianceAuditDTO>转换为一个Stream<AllianceAuditDTO>,并根据某些条件对其举行过滤。具体来说,它使用了buildFilterConditions方法来生成一组Supplier<Boolean>,然后查抄这些条件是否都满意。如果全部条件都满意,则保存该元素。
优化后的代码:将fucLi变量内联到filter方法中,淘汰了不须要的局部变量声明,使代码更加简洁。
  1. private Stream<AllianceAuditDTO> doFilter(List<AllianceAuditDTO> sourceList) {
  2.     return sourceList.stream()
  3.             .filter(source -> buildFilterConditions(source).stream().allMatch(Supplier::get));
  4. }
复制代码
noneMatch()

返回一个boolean值, 用于判断是否全部元素都不符合条件。
CR时可优化的代码片断:
  1. boolean userBehaviorsCheck = filterRecordList.stream().noneMatch(record -> IntegerUtils.compare(record.getPageCode(), 201));
复制代码
collect()

将流转换为指定的范例,通过Collectors举行指定。
toArray()

将流转换为数组。
iterator()

将流转换为Iterator对象。
CR时可优化的代码片断:
  1. Iterator<M_RelateAliPassenger> iterator = passengers.iterator();
  2. while (iterator.hasNext()) {
  3.     M_RelateAliPassenger passenger = iterator.next();
  4.     boolean matched = passengers2.stream()
  5.         .anyMatch(p -> p.getPassengerName() != null && p.getPassengerName().equalsIgnoreCase(passenger.getPassengerName()));
  6.     if (!matched) {
  7.         iterator.remove();
  8.     }
  9. }
复制代码
优化后的代码:紧张任务是从passengers列表中移除那些在passengers2列表中没有匹配的搭客。可以通过聚集操作来简化和优化这段代码。
  1. passengers.removeIf(passenger ->
  2.     passengers2.stream()
  3.         .noneMatch(p -> p.getPassengerName() != null
  4.             && p.getPassengerName().equalsIgnoreCase(passenger.getPassengerName()))
  5. );
复制代码
foreach()

无返回值,对元素举行逐个遍历,然后执行给定的处置惩罚逻辑。foreach()操作与parallelStream()搭配使用时,必须包管是线程安全的。也不要直接使用默认的线程池。
CR时可优化的代码片断:
  1. parameterList.forEach(param -> orderIds.append(param.getOrderID()).append(","));
复制代码
优化后的代码:Collectors.joining(",")最适合做上述的工作,应该是起首想到的。
  1. String orderIds = parameterList.stream()
  2.     .map(param -> param.getOrderID())
  3.     .collect(Collectors.joining(","));
复制代码
常见问题

一旦一个Stream被执行了终止操作之后,后续便不可以再读这个流执行其他的操作了,否则会报错,看下面示例:
  1. public void testHandleStreamAfterClosed() {
  2.     List<String> ids = Arrays.asList("205", "10", "308", "49", "627", "193", "111", "193");
  3.     Stream<String> stream = ids.stream().filter(s -> s.length() > 2);
  4.     // 统计stream操作后剩余的元素个数
  5.     System.out.println(stream.count());
  6.     System.out.println("-----下面会报错-----");
  7.     // 判断是否有元素值等于205
  8.     try {
  9.         System.out.println(stream.anyMatch("205"::equals));
  10.     } catch (Exception e) {
  11.         e.printStackTrace();
  12.         System.out.println(e.toString());
  13.     }
  14.     System.out.println("-----上面会报错-----");
  15. }
复制代码
结果:
  1. -----下面会报错-----
  2. java.lang.IllegalStateException: stream has already been operated upon or closed
  3. -----上面会报错-----
  4. java.lang.IllegalStateException: stream has already been operated upon or closed
  5.   at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
  6.   at java.util.stream.ReferencePipeline.anyMatch(ReferencePipeline.java:516)
  7.   at Solution_0908.main(Solution_0908.java:55)
复制代码
因为stream已经被执行count()终止方法了,以是对stream再执行anyMatch方法的时候,就会报错stream has already been operated upon or closed,这一点在使用的时候必要特别注意。
四、collect方法

获取一个聚集类的结果对象,比如List、Set大概HashMap等。
Collectors.toList()

  1. List<NormalOfferModel> collectList = normalOfferModelList
  2.         .stream()
  3.         .filter(offer -> offer.getCate1LevelId().equals("11"))
  4.         .collect(Collectors.toList());
复制代码
Collectors.toSet()

  1. Set<NormalOfferModel> collectSet = normalOfferModelList
  2.         .stream()
  3.         .filter(offer -> offer.getCate1LevelId().equals("22"))
  4.         .collect(Collectors.toSet());
复制代码
Collectors.toMap

CodeReview 时发现的问题:没有思量key重复问题。
  1. Arrays.stream(clazz.getDeclaredFields())
  2.                 .collect(Collectors.toMap(r -> r.getName().toLowerCase(), r -> r));
复制代码
优化后的代码:Function.identity()是java.util.function.Function接口中的一个静态方法。它总是返回一个其输入参数的函数。这在必要传递一个不做任何变换的函数时非常有用。Function.identity()等价于上面的r -> r。(k1, k2) -> k2就是解决重复key的问题,当存在重复key时使用末了一个key。
  1. Arrays.stream(clazz.getDeclaredFields())
  2.                 .collect(NormalOfferModel::getName, Function.identity(), (k1, k2) -> k2));
复制代码
Collectors.joining

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

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

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

ifPresent(Consumer<? super T> action)

如果Optional中包罗值,执行给定的Consumer操作,否则什么也不做。常用于简化代码,避免显式的空值查抄。
isPresent()

查抄Optional中是否包罗值。如果包罗值,返回true,否则返回false。
get()

如果Optional中包罗值,返回该值;否则抛出NoSuchElementException。这个方法不保举频繁使用,因为它违背了Optional的初志,即避免显式的空值查抄和异常处置惩罚。
orElse(T other)

如果Optional中包罗值,返回该值;否则返回other。常用于提供默认值。
orElseGet(Supplier<? extends T> other)

如果Optional中包罗值,返回该值;否则通过调用Supplier获取一个默认值。与orElse不同的是,Supplier只有在必要时才会被调用,因此适用于生成默认值开销较大的情况。
isEmpty()

查抄Optional中是否为空。如果为空,返回true,否则返回false。
orElseThrow()

如果Optional中包罗值,返回该值;否则抛出NoSuchElementException。
  1. optional.orElseThrow(() -> new IllegalArgumentException("Value is absent"));
复制代码
orElseThrow(Supplier<? extends X> exceptionSupplier)

如果Optional中包罗值,返回该值;否则通过Supplier抛出指定的异常。
filter(Predicate<? super T> predicate)

如果Optional中包罗值,并且该值满意给定的谓词,返回一个包罗该值的Optional;否则返回一个空的Optional。常用于条件过滤。
  1. Optional<String> filtered = optional.filter(value -> value.length() > 3);
复制代码
map(Function<? super T, ? extends U> mapper)

如果Optional中包罗值,应用给定的函数并返回一个包罗映射结果的Optional;否则返回一个空的Optional。常用于链式调用。
  1. Optional<Integer> length = optional.map(String::length);
复制代码
flatMap(Function<? super T, Optional<U>> mapper)

与map雷同,但mapper函数返回的是一个Optional对象,并且不会对返回的Optional举行嵌套。
  1. Optional<String> name = optional.flatMap(value -> Optional.of("Processed " + value));
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。

本帖子中包含更多资源

您需要 登录 才可以下载或查看,没有账号?立即注册

x
回复

举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

用户云卷云舒

论坛元老
这个人很懒什么都没写!
快速回复 返回顶部 返回列表