詳解Kotlin:forEach也能break和continue
詳解Kotlin:forEach也能break和continue
這樣的問題。也就是說,他們想用forEach而不是for循環(huán),因?yàn)檫@很fp,很洋氣(我也喜歡),
但是他們又想使用break和continue,也就是普通的流程控制語句中的控制語句。
這很不fp,因?yàn)樵居衒ilter是用于完成這個(gè)工作的,還有flapMap。BennyHuo在他發(fā)的文章里面也說的是這種方法。
filter很fp,但是會(huì)導(dǎo)致兩次遍歷,這樣的話給人一股效率很低的趕腳。而Java8的Stream API就只會(huì)遍歷一次,
而且很fp。但是它會(huì)有l(wèi)ambda對(duì)象的產(chǎn)生而且實(shí)現(xiàn)超復(fù)雜(我沒看過,不清楚),而Kotlin的集合框架可是能inline掉lambda的,
少產(chǎn)生了多少對(duì)象啊,怎么能和辣雞Java同流合污呢?
有人提到使用label return,比如:
fun main(ags: Array<String>) { (0..100).forEach { if (50 <= it) return@forEach println(it) } }
但是他做了實(shí)驗(yàn)之后發(fā)現(xiàn)這玩意只能相當(dāng)于continue,也就是說你只能跳出當(dāng)前循環(huán),然后還是會(huì)繼續(xù)下一輪。
講道理這個(gè)你仔細(xì)想想就可以發(fā)現(xiàn)。為了搞清楚其中的道理,我們自己實(shí)現(xiàn)一個(gè)forEach。
fun Pair<Int, Int>.forEach(block: (Int) -> Unit) { for (i in first..second) block.invoke(i) }
然后調(diào)用一下:
Pair(1, 100).forEach(::println)
沒毛病老鐵。
然后你會(huì)發(fā)現(xiàn),你在函數(shù)體內(nèi)對(duì)block產(chǎn)生了(second - first)次調(diào)用,不論你怎么return,都只會(huì)跳出這個(gè)block,
它并不影響你之后繼續(xù)調(diào)用這個(gè)block,也就是說這個(gè)for循環(huán)不受block行為的影響。
看起來無解了,那怎么辦呢?
那么就讓我來拯救你們吧。
fun main(ags: Array<String>) { run outside@ { (0..20).forEach inside@ { if (10 <= it) return@outside println(it) } } }
編譯之后運(yùn)行結(jié)果:
0 1 2 3 4 5 6 7 8 9 Process finished with exit code 0
吶,跳出去了。
就是這樣:
run breaking@ { (0..20).forEach continuing@ { if (10 <= it) return@breaking println(it) } }
上面這是break,運(yùn)行結(jié)果就上面那樣。
下面這是continue,運(yùn)行結(jié)果就是continue的效果。為了讓效果表現(xiàn)的明顯,我把println復(fù)制了一下,
分別在if前后,這樣可以很清楚地看到效果。
run breaking@ { (0..20).forEach continuing@ { print(it) if (10 <= it) return@continuing println(it) } }
運(yùn)行一下:
00 11 22 33 44 55 66 77 88 99 1011121314151617181920 Process finished with exit code 0
而且只進(jìn)行了一次迭代,效率看起來也比較高。
如何證明只有一次迭代?我使用jd-gui逆向了剛才的代碼,結(jié)果:
public final class _5Kt { public static final void main(@NotNull String[] args) { Intrinsics.checkParameterIsNotNull(args, "args"); int $i$a$1$run; Iterable $receiver$iv = (Iterable)new IntRange(0, 20); int $i$f$forEach; for (Iterator localIterator = $receiver$iv.iterator(); localIterator.hasNext();) { int element$iv = ((IntIterator)localIterator).nextInt();int it = element$iv; int $i$a$1$forEach; System.out.print(it); if (10 <= it) { break; } System.out.println(it); } } }
確實(shí)只有一次,而且jd-gui直接把我的行為反編譯為break了。服不服?
感謝閱讀,希望能幫助到大家,謝謝大家對(duì)本站的支持!
相關(guān)文章
Spring boot項(xiàng)目使用thymeleaf模板過程詳解
這篇文章主要介紹了Spring boot項(xiàng)目使用thymeleaf模板過程詳解,文中通過示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友可以參考下2020-07-07org.hibernate.QueryTimeoutException查詢超時(shí)的解決方法
本文主要介紹了org.hibernate.QueryTimeoutException查詢超時(shí)的解決方法,這通常發(fā)生在數(shù)據(jù)庫響應(yīng)緩慢、查詢?cè)O(shè)計(jì)不合理或系統(tǒng)資源緊張等情況下,感興趣的可以了解一下2024-05-05Spring?Boot實(shí)現(xiàn)文件上傳下載
這篇文章主要為大家詳細(xì)介紹了Spring?Boot實(shí)現(xiàn)文件上傳下載,文中示例代碼介紹的非常詳細(xì),具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2022-08-08基于SpringMVC攔截器實(shí)現(xiàn)接口耗時(shí)監(jiān)控功能
本文呢主要介紹了基于SpringMVC攔截器實(shí)現(xiàn)的接口耗時(shí)監(jiān)控功能,統(tǒng)計(jì)接口的耗時(shí)情況屬于一個(gè)可以復(fù)用的功能點(diǎn),因此這里直接使用 SpringMVC的HandlerInterceptor攔截器來實(shí)現(xiàn),需要的朋友可以參考下2024-02-02Spring中@Configuration注解的使用場(chǎng)景
這篇文章主要介紹了Spring中@Configuration注解的使用場(chǎng)景,@Configuration注解是從Spring?3.0版本開始加入的一個(gè)使Spring能夠支持注解驅(qū)動(dòng)開發(fā)的標(biāo)注型注解,主要用于標(biāo)注在類上,需要的朋友可以參考下2023-11-11MyBatis接口綁定的實(shí)現(xiàn)方式和工作原理
在日常開發(fā)中,數(shù)據(jù)持久層是幾乎每個(gè)項(xiàng)目都會(huì)涉及的一個(gè)關(guān)鍵組成部分,MyBatis作為一個(gè)流行的持久層框架,其提供的接口綁定機(jī)制極大地簡(jiǎn)化了數(shù)據(jù)庫操作,本文將通過詳細(xì)的代碼示例和講解,帶你深入理解MyBatis接口綁定的工作原理和實(shí)踐方式,需要的朋友可以參考下2024-03-03Java Tree結(jié)構(gòu)數(shù)據(jù)中查找匹配節(jié)點(diǎn)方式
這篇文章主要介紹了Java Tree結(jié)構(gòu)數(shù)據(jù)中查找匹配節(jié)點(diǎn)方式,具有很好的參考價(jià)值,希望對(duì)大家有所幫助。一起跟隨小編過來看看吧2020-09-09