柚子快報(bào)邀請(qǐng)碼778899分享:數(shù)據(jù)庫(kù) Stream 流式編程
柚子快報(bào)邀請(qǐng)碼778899分享:數(shù)據(jù)庫(kù) Stream 流式編程
優(yōu)質(zhì)博文:IT-BLOG-CN
大家都知道可以將Collection類轉(zhuǎn)化成流Stream進(jìn)行操作(Map并不能創(chuàng)建流),代碼變得簡(jiǎn)約流暢。我們先看下流的幾個(gè)特點(diǎn): 1、流并不存儲(chǔ)元素。這些元素可能存儲(chǔ)在底層的集合中,或者是按需生成。 2、流的操作不會(huì)修改其數(shù)據(jù)元素,而是生成一個(gè)新的流。 3、流的操作是盡可能惰性執(zhí)行的。這意味著直至需要其結(jié)果時(shí),操作才會(huì)執(zhí)行。
一、創(chuàng)建流
負(fù)責(zé)新建一個(gè)Stream流,大多數(shù)都是基于現(xiàn)有的數(shù)組、List、Set、Map等集合創(chuàng)建新的Stream。
stream()
創(chuàng)建一個(gè)stream串行流對(duì)象。
CR時(shí)可優(yōu)化的代碼片段:
public List
List
for (SFltStudent source : sourceList) {
SFltStudent target = new SFltStudent();
target.setTicketNo(source.getOldTicketNo());
target.setFlightAgency(source.getFlightAgency());
targetList.add(target);
}
return targetList;
}
代碼優(yōu)化:這里sourceList如果數(shù)據(jù)量很大時(shí),也可以考慮parallel stream。這里主要是想通過(guò)stream提高代碼簡(jiǎn)潔性和可讀性。
public List
return sourceList.stream()
.map(source -> {
SFltStudent target = new SFltStudent();
target.setTicketNo(source.getOldTicketNo());
target.setFlightAgency(source.getFlightAgency());
return target;
})
.collect(Collectors.toList());
}
parallelStream()
創(chuàng)建一個(gè)可并行執(zhí)行的stream流對(duì)象。可以有效利用計(jì)算機(jī)的多CPU硬件,提升邏輯的執(zhí)行速度。將一整個(gè)stream劃分為多個(gè)片段,然后對(duì)各個(gè)分片流并行執(zhí)行處理邏輯,最后將各個(gè)分片流的執(zhí)行結(jié)果匯總為一個(gè)整體流。
::: tip 如果遇到耗時(shí)的操作,或者大量IO的操作,或者有線程sleep的操作一定要避免使用并行流。
并行流場(chǎng)景效率會(huì)比迭代器逐個(gè)循環(huán)更高。 :::
查看parallelStream的源碼發(fā)現(xiàn)parallel Stream底層是將任務(wù)進(jìn)行了切分,最終將任務(wù)傳遞給了jdk8自帶的“全局”ForkJoinPool線程池。在Fork-Join中,比如一個(gè)擁有4個(gè)線程的ForkJoinPool線程池,有一個(gè)任務(wù)隊(duì)列,一個(gè)大的任務(wù)切分出的子任務(wù)會(huì)提交到線程池的任務(wù)隊(duì)列中,4個(gè)線程從任務(wù)隊(duì)列中獲取任務(wù)執(zhí)行,哪個(gè)線程執(zhí)行的任務(wù)快,哪個(gè)線程執(zhí)行的任務(wù)就多,只有隊(duì)列中沒(méi)有任務(wù)線程才是空閑的,這就是工作竊取。
/**
* @return a possibly parallel {@code Stream} over the elements in this == parallelStream()并不一定返回一個(gè)并行流,有可能parallelStream()全是由主線程順序執(zhí)行的。
* collection
* @since 1.8
*/
default Stream
return StreamSupport.stream(spliterator(), true);
}
注意:parallelStream和整個(gè)java進(jìn)程共用ForkJoinPool:如果直接使用parallelStream().foreach會(huì)默認(rèn)使用全局的ForkJoinPool,而這樣就會(huì)導(dǎo)致當(dāng)前程序很多地方共用同一個(gè)線程池,包括gc相關(guān)操作在內(nèi),所以一旦任務(wù)隊(duì)列中滿了之后,就會(huì)出現(xiàn)阻塞的情況,導(dǎo)致整個(gè)程序的只要當(dāng)前使用ForkJoinPool的地方都會(huì)出現(xiàn)問(wèn)題。
CR時(shí)可優(yōu)化的代碼片段: :并發(fā)獲取接口數(shù)據(jù),進(jìn)行業(yè)務(wù)處理,對(duì)共享數(shù)據(jù)的修改需要考慮多線程安全問(wèn)題。
List
List
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;
})
);
代碼優(yōu)化:將復(fù)雜的條件判斷提取到processOrder方法中,使主流處理邏輯更加簡(jiǎn)潔和易讀。
List
List
boolean allSuccess = infos.parallelStream()
.filter(XStudentOrderInfo::getChecked)
.map(XStudentOrderInfo::getProductOrderID)
.filter(StringUtils::isNotBlank)
.distinct()
.allMatch(productOrderId -> processOrder(productOrderId, errorMessageList, errorProductOrderIds));
private boolean processOrder(String productOrderId, List
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()
通過(guò)給定的一系列元素創(chuàng)建一個(gè)新的stream串行流對(duì)象。
二、Stream 中間處理
輸入Stream對(duì)象,輸出一個(gè)新的Stream對(duì)象,中間管道操作可以進(jìn)行疊加。
規(guī)范
CR時(shí)發(fā)現(xiàn)不規(guī)范的流式編程如下:
issueBillList.stream().map(IssueBillDO::getIssueBillId).collect(Collectors.toList());
根據(jù)代碼規(guī)范,在代碼中使用鏈?zhǔn)秸{(diào)用時(shí),為了提高代碼的可讀性和維護(hù)性,建議在方法鏈的每個(gè)方法調(diào)用之間進(jìn)行換行。這樣可以使代碼更容易閱讀和理解。
List
.map(IssueBillDO::getIssueBillId)
.collect(Collectors.toList());
filter()
按照條件過(guò)濾符合要求的元素,返回新的stream流。
CR時(shí)可優(yōu)化的代碼片段: .filter多個(gè)過(guò)濾條件并存,存在一定的優(yōu)化空間。編程如下:
.filter(r -> StringUtilsExt.compareIgnoreSpaceAndCaps(r.getPassengerName(), trace.getPassengerName())
&& StringUtilsExt.compareIgnoreSpaceAndCaps(r.getFlight(), trace.getFlightNo())
&& StringUtilsExt.compareIgnoreSpaceAndCaps(r.getDPort(), trace.getDport()))
......
建議根據(jù)業(yè)務(wù)將它們拆分為多個(gè).filter方法調(diào)用可以提高代碼的可讀性和可維護(hù)性。但是需要注意每個(gè).filter調(diào)用都會(huì)遍歷一次流中的元素。如果流非常大,多個(gè).filter調(diào)用可能會(huì)帶來(lái)性能開銷。同時(shí)如果條件之間存在邏輯依賴關(guān)系,拆分成多個(gè).filter調(diào)用可能會(huì)導(dǎo)致邏輯錯(cuò)誤。例如,如果某個(gè)條件的結(jié)果會(huì)影響另一個(gè)條件的判斷,拆分可能會(huì)破壞這種依賴關(guān)系。雖然拆分可以提高某些情況下的可讀性,但如果條件本身很簡(jiǎn)單,拆分反而會(huì)使代碼顯得冗長(zhǎng)和復(fù)雜。
具體大家根據(jù)自己的業(yè)務(wù)特點(diǎn)進(jìn)行選擇
方案一:如果條件非常復(fù)雜,或者你希望每個(gè)條件都能單獨(dú)清晰地表達(dá),可以拆分成多個(gè).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()))
方案二:如果條件邏輯非常復(fù)雜,考慮將條件封裝到一個(gè)輔助方法中,這樣代碼會(huì)更加清晰
.filter(r -> matchesTraceInfo(r, trace.getTripInfo()))
private boolean matchesTraceInfo(Record r, TripInfo tripInfo) {
return StringUtilsExt.compareIgnoreSpaceAndCaps(r.getPassengerName(), tripInfo.getPassengerName()) &&
StringUtilsExt.compareIgnoreSpaceAndCaps(r.getFlight(), tripInfo.getFlightNo()) &&
StringUtilsExt.compareIgnoreSpaceAndCaps(r.getDPort(), tripInfo.getDport());
}
map()
將已有元素轉(zhuǎn)換為另一個(gè)對(duì)象類型,一對(duì)一邏輯,返回新的stream流。
List
// 使用流操作
List
.map(id -> {
id.replace("A","B");
return id;
})
.collect(Collectors.toList());
System.out.println(results);
執(zhí)行之后,會(huì)發(fā)現(xiàn)每一個(gè)元素都被轉(zhuǎn)換為對(duì)應(yīng)新的元素,但是前后總元素個(gè)數(shù)是一致的:
B1
B2
B3
下面的代碼因?qū)ap和filter功能的混淆,導(dǎo)致代碼執(zhí)行解決與預(yù)期不符,最終出現(xiàn)生產(chǎn)故障。
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)) {
throw new ResultException("fail);
}
});
原因:如果使用map這段代碼會(huì)返回一個(gè)List
flatMap()
將已有元素轉(zhuǎn)換為另一個(gè)對(duì)象類型,一對(duì)多邏輯,即原來(lái)一個(gè)元素對(duì)象可能會(huì)轉(zhuǎn)換為1個(gè)或者多個(gè)新類型的元素,返回新的stream流。
案例:
List
// 使用流操作
List
.flatMap(sentence -> Arrays.stream(sentence.split(" ")))
.collect(Collectors.toList());
System.out.println(results2);
執(zhí)行之后,會(huì)發(fā)現(xiàn)每一個(gè)元素都被轉(zhuǎn)換為多個(gè)新的元素:
B1
B2
B3
B4
flatMap操作是先將每個(gè)元素進(jìn)行處理并返回一個(gè)新的Stream,然后將多個(gè)Stream展開合并為了一個(gè)完整的新的Stream,如下:
CR時(shí)可優(yōu)化的代碼片段: 應(yīng)用場(chǎng)景為L(zhǎng)ist中的對(duì)象中包含List列表
List
.filter(Objects::nonNull)
.filter(p -> CollectionUtils.isNotEmpty(p.getMaterialInfoList()))
.flatMap(p -> p.getMaterialInfoList().stream().filter(Objects::nonNull))
.collect(Collectors.toList());
代碼優(yōu)化:提前檢查p.getMaterialInfoList()是否為空的處理,CollectionUtils和Collectors被頻繁使用,可以進(jìn)行靜態(tài)導(dǎo)入以簡(jiǎn)化代碼。
List
.filter(p -> p != null && isNotEmpty(p.getMaterialInfoList()))
.flatMap(p -> p.getMaterialInfoList().stream())
.filter(Objects::nonNull)
.collect(toList());
limit()
僅保留集合前面指定個(gè)數(shù)的元素,返回新的stream流。
Stream
.limit(2);
System.out.println(Arrays.toString(integerStream.toArray())); // [1, 2]
skip()
跳過(guò)集合前面指定個(gè)數(shù)的元素,返回新的stream流。
Stream
.skip(2);
System.out.println(Arrays.toString(integerStream.toArray())); // [3]
concat()
將兩個(gè)流的數(shù)據(jù)合并起來(lái)為1個(gè)新的流,返回新的stream流。
distinct()
對(duì)Stream中所有元素進(jìn)行去重,返回新的stream流。
**CR`時(shí)可優(yōu)化的代碼片段:**
submitReiEntityList = model.getReibursementInfo().getSubmitReiEntityList().stream()
.map(ReibursementApplyOrderInfo::getOrderId)
.distinct()
.collect(Collectors.toList());
這里主要說(shuō)一個(gè)思想,是否可以將需要distinct的集合轉(zhuǎn)換為Set進(jìn)行存儲(chǔ),提高查找效率。
sorted()
對(duì)stream中所有的元素按照指定規(guī)則進(jìn)行排序,返回新的stream流。
這里主要看一下目前存在的寫法
CR片段一
wordSet1 = wordSet.stream().sorted(new Comparator
@Override
public int compare(String o1, String o2) {
return o2.length() - o1.length();
}
}).collect(Collectors.toList());
CR片段二
List
.sorted((RescheduleLog i1, RescheduleLog i2) -> i2.getRecordTime().compareTo(i1.getRecordTime()))
.collect(Collectors.toList());
CR片段三:上面的片段可以按照該規(guī)范,簡(jiǎn)化代碼。
List
.sorted(Comparator.comparing(RescheduleIssueBill::getIssueBillID).reversed())
.collect(Collectors.toList());
CR片段四
List
.stream()
.sorted(Comparator.comparing(RescheduleIssueBill::getIssueBillID).reversed())
.collect(Collectors.toList());
代碼優(yōu)化:如果不需要保留原始列表的順序,可以直接對(duì)original進(jìn)行排序,避免創(chuàng)建額外的心列表。
original.sort(Comparator.comparing(SegmentInfo::getSortedSequence));
peek()
對(duì)stream流中的每個(gè)元素進(jìn)行逐個(gè)遍歷處理,返回處理后的stream流。意味著peek只能作為管道中途的一個(gè)處理步驟,而沒(méi)法直接執(zhí)行得到結(jié)果,其后面必須還要有其它終止操作的時(shí)候才會(huì)被執(zhí)行;而foreach作為無(wú)返回值的終止方法,則可以直接執(zhí)行相關(guān)操作。
CR過(guò)程中使用peek的代碼,peek么有問(wèn)題,但是代碼還是有一定的優(yōu)化空間。
List
.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());
我們給一個(gè)更優(yōu)雅的代碼:
List
.filter(auditInfo -> AllianceAuditStatusEnum.AUDIT_SUCCESS.getValue().equals(auditInfo.getAuditStatus()))
.peek(auditInfo -> auditInfo.setCustomKey(idxAtomic.getAndIncrement()))
.collect(Collectors.toList());
三、終止Stream
通過(guò)終止管道操作之后,Stream流將會(huì)結(jié)束,最后可能會(huì)執(zhí)行某些邏輯處理,或者是按照要求返回某些執(zhí)行后的結(jié)果數(shù)據(jù)。
count()
返回stream處理后最終的元素個(gè)數(shù)。
CR時(shí)可優(yōu)化的代碼片段:
groupByDataType.entrySet().stream()
.allMatch(entry -> entry.getValue().stream()
.map(DiscountInfo::getDeductionAmount)
.distinct()
.count() == 1);
代碼優(yōu)化:上述代碼distinct與count結(jié)合使用時(shí),可以使用Set與length()方法實(shí)現(xiàn),但是這里使用count和distinct可能從業(yè)務(wù)上理解更為接近,所以具體需要根據(jù)業(yè)務(wù)場(chǎng)景決定。
boolean allMatch = groupByDataType.entrySet().stream()
.allMatch(entry -> entry.getValue().stream()
.map(DiscountInfo::getDeductionAmount)
.collect(Collectors.toSet())
.size() == 1);
但是這里可以根據(jù)allMatch的特性上進(jìn)行優(yōu)化,只要找到一個(gè)不滿足條件的金額,就提前返回false提交性能。
boolean allMatch = groupByDataType.entrySet().stream()
.allMatch(entry -> {
Set
.map(DiscountInfo::getDeductionAmount)
.collect(Collectors.toSet());
return deductionAmounts.size() == 1;
});
max()
返回stream處理后的元素最大值。
CR時(shí)可優(yōu)化的代碼片段:
files.stream()
.mapToInt(UploadRetireMaterialInfoType::getBatchNo)
.max()
.getAsInt();
代碼優(yōu)化:這里主要的問(wèn)題是,再調(diào)用getAsInt()方法時(shí),一定要判斷下是否存在,否則回報(bào)異常。
OptionalInt maxBatchNoOptional = files.stream()
.mapToInt(UploadRetireMaterialInfoType::getBatchNo)
.max();
if (maxBatchNoOptional.isPresent()) {
int maxBatchNo = maxBatchNoOptional.getAsInt();
} else {
......
}
min()
返回stream處理后的元素最小值。
CR過(guò)程中發(fā)現(xiàn)可以使用min()方法進(jìn)行優(yōu)化的代碼片段
List
.sorted(Comparator.comparing(SFltticketStudentByairlineMy::getSequence))
.collect(toList());
SFltticketStudentByairlineMy firstSeqTicketNo = sortRefundDetails.get(0);
優(yōu)化后代碼如下:
refundDetails.stream()
.min(Comparator.comparing(SFltticketStudentByairlineMy::getSequence));
findFirst()
找到第一個(gè)符合條件的元素時(shí)則終止流處理。
優(yōu)化片段一:
CR時(shí)發(fā)現(xiàn).findFirst()返回Optional可以繼續(xù)進(jìn)行業(yè)務(wù)處理,存在一定的優(yōu)化空間。代碼如下:
oc.getOrderInfoList().stream()
.filter(f -> (StringUtilsExt.compareIgnoreSpaceAndCaps(f.getFlight(), lastTrip.getFlightNo())
......)
.findFirst().orElse(null);
if (lastFlight != null) {
......
}
可以在findFirst()方法后繼續(xù)執(zhí)行操作,而不需要單獨(dú)的if (lastFlight != null)語(yǔ)句。流式編程提供了ifPresent方法,可以讓你在找到符合條件的元素時(shí)執(zhí)行某些操作。這樣使代碼更加簡(jiǎn)潔和流暢,不需要顯式地進(jìn)行空值檢查。
oc.getOrderInfoList().stream()
.filter(f -> (StringUtilsExt.compareIgnoreSpaceAndCaps(f.getFlight(), lastTrip.getFlightNo())
......)
.findFirst()
.ifPresent(lastFlight -> {
// 在這里執(zhí)行你需要的操作
// 例如:
// System.out.println("Found flight: " + lastFlight);
});
優(yōu)化片段二:
對(duì).findFirst()方法使用存在優(yōu)化空間
List
.sorted(Comparator.comparing(SFltticketStudentByairlineMy::getSequence))
.collect(toList());
SFltticketStudentByairlineMy firstSeqTicketNo = sortRefundDetails.get(0);
使用.findFirst()方法獲取第一個(gè)符合要求的元素即可。當(dāng)然這個(gè)代碼還存在優(yōu)化空間。
SFltticketStudentByairlineMy firstSeqTicketNo = refundDetails.stream()
.sorted(Comparator.comparing(SFltticketStudentByairlineMy::getSequence))
.collect(toList())
.findFirst();
findAny()
找到任何一個(gè)符合條件的元素時(shí)則退出流處理,這個(gè)對(duì)于串行流時(shí)與findFirst相同,對(duì)于并行流時(shí)比較高效,任何分片中找到都會(huì)終止后續(xù)計(jì)算邏輯。
CR時(shí)可優(yōu)化的代碼片段:
orderInfo.getRefundInfoList().stream()
.filter(a -> MATERIAL_SUPPLEMENT_FLAG.equals(a.getKey()) && TRUE_VALUE.equals(a.getValue()))
.findAny()
.isPresent();
優(yōu)化代碼:返回的是一個(gè)boolean類型,可以直接使用anyMatch()
boolean isPresent = orderInfo.getRefundOrderFlagInfoList().stream()
.anyMatch(a -> MATERIAL_SUPPLEMENT_FLAG.equals(a.getKey()) && TRUE_VALUE.equals(a.getValue()));
anyMatch()
返回一個(gè)boolean值,類似于isContains(),用于判斷是否有符合條件的元素。
我們也會(huì)將寫的標(biāo)準(zhǔn)的代碼推薦給大家
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()
返回一個(gè)boolean值,用于判斷是否所有元素都符合條件。
在CR中發(fā)現(xiàn)可以優(yōu)化的代碼:在流操作中fucLi部分存在優(yōu)化空間。
private Stream
return sourceList.stream()
.filter(
source -> {
List
buildFilterConditions(source);
return fucLi.stream().allMatch(Supplier::get);
});
}
代碼是一個(gè)過(guò)濾方法,它將一個(gè)List
優(yōu)化后的代碼:將fucLi變量?jī)?nèi)聯(lián)到filter方法中,減少了不必要的局部變量聲明,使代碼更加簡(jiǎn)潔。
private Stream
return sourceList.stream()
.filter(source -> buildFilterConditions(source).stream().allMatch(Supplier::get));
}
noneMatch()
返回一個(gè)boolean值, 用于判斷是否所有元素都不符合條件。
CR時(shí)可優(yōu)化的代碼片段:
boolean userBehaviorsCheck = filterRecordList.stream().noneMatch(record -> IntegerUtils.compare(record.getPageCode(), 201));
collect()
將流轉(zhuǎn)換為指定的類型,通過(guò)Collectors進(jìn)行指定。
toArray()
將流轉(zhuǎn)換為數(shù)組。
iterator()
將流轉(zhuǎn)換為Iterator對(duì)象。
CR時(shí)可優(yōu)化的代碼片段:
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();
}
}
優(yōu)化后的代碼:主要任務(wù)是從passengers列表中移除那些在passengers2列表中沒(méi)有匹配的乘客??梢酝ㄟ^(guò)集合操作來(lái)簡(jiǎn)化和優(yōu)化這段代碼。
passengers.removeIf(passenger ->
passengers2.stream()
.noneMatch(p -> p.getPassengerName() != null
&& p.getPassengerName().equalsIgnoreCase(passenger.getPassengerName()))
);
foreach()
無(wú)返回值,對(duì)元素進(jìn)行逐個(gè)遍歷,然后執(zhí)行給定的處理邏輯。foreach()操作與parallelStream()搭配使用時(shí),必須保證是線程安全的。也不要直接使用默認(rèn)的線程池。
CR時(shí)可優(yōu)化的代碼片段:
parameterList.forEach(param -> orderIds.append(param.getOrderID()).append(","));
優(yōu)化后的代碼:Collectors.joining(",")最適合做上述的工作,應(yīng)該是首先想到的。
String orderIds = parameterList.stream()
.map(param -> param.getOrderID())
.collect(Collectors.joining(","));
常見(jiàn)問(wèn)題
一旦一個(gè)Stream被執(zhí)行了終止操作之后,后續(xù)便不可以再讀這個(gè)流執(zhí)行其他的操作了,否則會(huì)報(bào)錯(cuò),看下面示例:
public void testHandleStreamAfterClosed() {
List
Stream
// 統(tǒng)計(jì)stream操作后剩余的元素個(gè)數(shù)
System.out.println(stream.count());
System.out.println("-----下面會(huì)報(bào)錯(cuò)-----");
// 判斷是否有元素值等于205
try {
System.out.println(stream.anyMatch("205"::equals));
} catch (Exception e) {
e.printStackTrace();
System.out.println(e.toString());
}
System.out.println("-----上面會(huì)報(bào)錯(cuò)-----");
}
結(jié)果:
-----下面會(huì)報(bào)錯(cuò)-----
java.lang.IllegalStateException: stream has already been operated upon or closed
-----上面會(huì)報(bào)錯(cuò)-----
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)
因?yàn)閟tream已經(jīng)被執(zhí)行count()終止方法了,所以對(duì)stream再執(zhí)行anyMatch方法的時(shí)候,就會(huì)報(bào)錯(cuò)stream has already been operated upon or closed,這一點(diǎn)在使用的時(shí)候需要特別注意。
四、collect方法
獲取一個(gè)集合類的結(jié)果對(duì)象,比如List、Set或者HashMap等。
Collectors.toList()
List
.stream()
.filter(offer -> offer.getCate1LevelId().equals("11"))
.collect(Collectors.toList());
Collectors.toSet()
Set
.stream()
.filter(offer -> offer.getCate1LevelId().equals("22"))
.collect(Collectors.toSet());
Collectors.toMap
CodeReview 時(shí)發(fā)現(xiàn)的問(wèn)題:沒(méi)有考慮key重復(fù)問(wèn)題。
Arrays.stream(clazz.getDeclaredFields())
.collect(Collectors.toMap(r -> r.getName().toLowerCase(), r -> r));
優(yōu)化后的代碼:Function.identity()是java.util.function.Function接口中的一個(gè)靜態(tài)方法。它總是返回一個(gè)其輸入?yún)?shù)的函數(shù)。這在需要傳遞一個(gè)不做任何變換的函數(shù)時(shí)非常有用。Function.identity()等價(jià)于上面的r -> r。(k1, k2) -> k2就是解決重復(fù)key的問(wèn)題,當(dāng)存在重復(fù)key時(shí)使用最后一個(gè)key。
Arrays.stream(clazz.getDeclaredFields())
.collect(NormalOfferModel::getName, Function.identity(), (k1, k2) -> k2));
Collectors.joining
List
String joinResult = ids.stream().collect(Collectors.joining(","));
Collectors.averagingInt
List
// 計(jì)算平均值
Double average = ids.stream().collect(Collectors.averagingInt(value -> value));
Collectors.summarizingInt
List
// 數(shù)據(jù)統(tǒng)計(jì)信息
IntSummaryStatistics summary = ids.stream().collect(Collectors.summarizingInt(value -> value));
Optional 類
ifPresent(Consumer super T> action)
如果Optional中包含值,執(zhí)行給定的Consumer操作,否則什么也不做。常用于簡(jiǎn)化代碼,避免顯式的空值檢查。
isPresent()
檢查Optional中是否包含值。如果包含值,返回true,否則返回false。
get()
如果Optional中包含值,返回該值;否則拋出NoSuchElementException。這個(gè)方法不推薦頻繁使用,因?yàn)樗`背了Optional的初衷,即避免顯式的空值檢查和異常處理。
orElse(T other)
如果Optional中包含值,返回該值;否則返回other。常用于提供默認(rèn)值。
orElseGet(Supplier extends T> other)
如果Optional中包含值,返回該值;否則通過(guò)調(diào)用Supplier獲取一個(gè)默認(rèn)值。與orElse不同的是,Supplier只有在需要時(shí)才會(huì)被調(diào)用,因此適用于生成默認(rèn)值開銷較大的情況。
isEmpty()
檢查Optional中是否為空。如果為空,返回true,否則返回false。
orElseThrow()
如果Optional中包含值,返回該值;否則拋出NoSuchElementException。
optional.orElseThrow(() -> new IllegalArgumentException("Value is absent"));
orElseThrow(Supplier extends X> exceptionSupplier)
如果Optional中包含值,返回該值;否則通過(guò)Supplier拋出指定的異常。
filter(Predicate super T> predicate)
如果Optional中包含值,并且該值滿足給定的謂詞,返回一個(gè)包含該值的Optional;否則返回一個(gè)空的Optional。常用于條件過(guò)濾。
Optional
map(Function super T, ? extends U> mapper)
如果Optional中包含值,應(yīng)用給定的函數(shù)并返回一個(gè)包含映射結(jié)果的Optional;否則返回一個(gè)空的Optional。常用于鏈?zhǔn)秸{(diào)用。
Optional
flatMap(Function super T, Optional> mapper)
與map類似,但mapper函數(shù)返回的是一個(gè)Optional對(duì)象,并且不會(huì)對(duì)返回的Optional進(jìn)行嵌套。
Optional
柚子快報(bào)邀請(qǐng)碼778899分享:數(shù)據(jù)庫(kù) Stream 流式編程
推薦鏈接
本文內(nèi)容根據(jù)網(wǎng)絡(luò)資料整理,出于傳遞更多信息之目的,不代表金鑰匙跨境贊同其觀點(diǎn)和立場(chǎng)。
轉(zhuǎn)載請(qǐng)注明,如有侵權(quán),聯(lián)系刪除。