0


Stream 流式编程

优质博文:IT-BLOG-CN

在这里插入图片描述

大家都知道可以将

Collection

类转化成流

Stream

进行操作(

Map

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

一、创建流

负责新建一个

Stream

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

List

Set

Map

等集合创建新的

Stream

stream()

创建一个

stream

串行流对象。

**

CR

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

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

代码优化:这里

sourceList

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

parallel stream

。这里主要是想通过

stream

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

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

parallelStream()

创建一个可并行执行的

stream

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

CPU

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

stream

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

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

IO

的操作,或者有线程

sleep

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

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

查看

parallelStream

的源码发现

parallel Stream

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

jdk8

自带的“全局”

ForkJoinPool

线程池。在

Fork-Join

中,比如一个拥有

4

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

4

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

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

注意:

parallelStream

和整个

java

进程共用

ForkJoinPool

:如果直接使用

parallelStream().foreach

会默认使用全局的

ForkJoinPool

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

gc

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

ForkJoinPool

的地方都会出现问题。

**

CR

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

List<String> errorMessageList =Collections.synchronizedList(newArrayList<>());List<String> errorProductOrderIds =Collections.synchronizedList(newArrayList<>());

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())){
            errorMessageList.add(response.getMessage());
            errorProductOrderIds.add(productOrderId);}return isSuccess;}));

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

processOrder

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

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())){
        errorMessageList.add(response.getMessage());
        errorProductOrderIds.add(productOrderId);}return isSuccess;}

Stream.of()

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

stream

串行流对象。

二、Stream 中间处理

输入

Stream

对象,输出一个新的

Stream

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

规范

CR

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

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

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

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

filter()

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

stream

流。

**

CR

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

.filter

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

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

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

.filter

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

.filter

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

.filter

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

.filter

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

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

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

.filter

方法

.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()))

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

.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());}

map()

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

stream

流。

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

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

B1B2B3

下面的代码因对

map

filter

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

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

原因:如果使用

map

这段代码会返回一个

List<boolean>

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

respose

返回了结果,那么

map

就会返回一个

List<boolean>

列表,这个列表可能为:

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

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

respose

返回了结果

code

无论是不是

404

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

flatMap()

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

1

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

stream

流。

案例:

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);

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

B1B2B3B4
flatMap

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

Stream

,然后将多个

Stream

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

Stream

,如下:

**

CR

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

List

中的对象中包含

List

列表

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());

代码优化:提前检查

p.getMaterialInfoList()

是否为空的处理,

CollectionUtils

Collectors

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

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

limit()

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

stream

流。

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

skip()

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

stream

流。

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

concat()

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

1

个新的流,返回新的

stream

流。

distinct()

Stream

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

stream

流。

**

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

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

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

distinct

的集合转换为

Set

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

sorted()

stream

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

stream

流。

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

CR

片段一

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

片段二

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

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

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

片段四

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

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

original

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

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

peek()

stream

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

stream

流。意味着

peek

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

foreach

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

CR

过程中使用

peek

的代码,

peek

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

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);
        auditInfo.setCustomKey(customKey);}).collect(Collectors.toList());

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

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

三、终止Stream

通过终止管道操作之后,

Stream

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

count()

返回

stream

处理后最终的元素个数。

**

CR

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

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

代码优化:上述代码

distinct

count

结合使用时,可以使用

Set

length()

方法实现,但是这里使用

count

distinct

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

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

但是这里可以根据

allMatch

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

false

提交性能。

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

max()

返回

stream

处理后的元素最大值。

**

CR

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

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

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

getAsInt()

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

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

min()

返回

stream

处理后的元素最小值。

CR

过程中发现可以使用

min()

方法进行优化的代码片段

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

优化后代码如下:

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

findFirst()

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

优化片段一:

CR

时发现

.findFirst()

返回

Optional

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

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

可以在

findFirst()

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

if (lastFlight != null)

语句。流式编程提供了

ifPresent

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

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

优化片段二:

.findFirst()

方法使用存在优化空间

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

使用

.findFirst()

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

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

findAny()

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

findFirst

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

**

CR

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

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

优化代码:返回的是一个

boolean

类型,可以直接使用

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

anyMatch()

返回一个

boolean

值,类似于

isContains()

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

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

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));

allMatch()

返回一个

boolean

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

CR

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

fucLi

部分存在优化空间。

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

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

List<AllianceAuditDTO>

转换为一个

Stream<AllianceAuditDTO>

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

buildFilterConditions

方法来生成一组

Supplier<Boolean>

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

优化后的代码:将

fucLi

变量内联到

filter

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

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

noneMatch()

返回一个

boolean

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

**

CR

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

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

collect()

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

Collectors

进行指定。

toArray()

将流转换为数组。

iterator()

将流转换为

Iterator

对象。

**

CR

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

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){
        iterator.remove();}}

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

passengers

列表中移除那些在

passengers2

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

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

foreach()

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

foreach()

操作与

parallelStream()

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

**

CR

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

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

优化后的代码:

Collectors.joining(",")

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

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

常见问题

一旦一个

Stream

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

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){
        e.printStackTrace();System.out.println(e.toString());}System.out.println("-----上面会报错-----");}

结果:

-----下面会报错-----java.lang.IllegalStateException: stream has already been operated upon or closed
-----上面会报错-----java.lang.IllegalStateException: stream has already been operated upon or closed
  at java.util.stream.AbstractPipeline.evaluate(AbstractPipeline.java:229)
  at java.util.stream.ReferencePipeline.anyMatch(ReferencePipeline.java:516)
  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()
List<NormalOfferModel> collectList = normalOfferModelList
        .stream().filter(offer -> offer.getCate1LevelId().equals("11")).collect(Collectors.toList());

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

Collectors.toMap

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

key

重复问题。

Arrays.stream(clazz.getDeclaredFields()).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

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

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

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

Collectors.summarizingInt
List<Integer> ids =Arrays.asList(10,20,30,40,50);// 数据统计信息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

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

orElseThrow(Supplier<? extends X> exceptionSupplier)

如果

Optional

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

Supplier

抛出指定的异常。

filter(Predicate<? super T> predicate)

如果

Optional

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

Optional

;否则返回一个空的

Optional

。常用于条件过滤。

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

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

如果

Optional

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

Optional

;否则返回一个空的

Optional

。常用于链式调用。

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

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

map

类似,但

mapper

函数返回的是一个

Optional

对象,并且不会对返回的

Optional

进行嵌套。

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

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

“Stream 流式编程”的评论:

还没有评论