hashMap的循環(huán)姿勢你真的使用對了嗎?
Python實(shí)戰(zhàn)社群
Java實(shí)戰(zhàn)社群
長按識(shí)別下方二維碼,按需求添加
掃碼關(guān)注添加客服
進(jìn)Python社群▲
掃碼關(guān)注添加客服
進(jìn)Java社群▲
hashMap 應(yīng)該是java程序員工作中用的比較多的一個(gè)鍵值對處理的數(shù)據(jù)的類型了。這種數(shù)據(jù)類型一般都會(huì)有增刪查的方法,今天我們就來看看它的循環(huán)方法。hashMap 有常見的六七種遍歷的方式。這么多的選擇,大家平時(shí)都是使用哪一種來遍歷數(shù)據(jù)列?歡迎大家在下方留言哦。說實(shí)話這么多種方式,想記也不記不住,也不想浪費(fèi)時(shí)間來記這玩意,所以本人在JDK1.8以前基本上都是用Map.Entry的方式來遍歷,1.8及以后就習(xí)慣性用forEach了,不過這個(gè)不能有continue或者break操作這個(gè)有時(shí)候還是挺不方便的,其他幾種基本上沒怎么用過,也沒太研究這幾種方式,哪種性能是比較好的。反正就是挑自己熟悉的方式。好了話不多說,我們還是直入今天的主題。先來看看每種遍歷的方式:
在for循環(huán)中使用entries實(shí)現(xiàn)Map的遍歷
?public?static?void?forEachEntries()?{
????????for?(Map.Entry?entry?:?map.entrySet())?{
????????????String?mapKey?=?entry.getKey();
????????????String?mapValue?=?entry.getValue();
????????}
????}
在for循環(huán)中遍歷key
?public?static?void?forEachKey()?{
????????for?(String?key?:?map.keySet())?{
????????????String?mapKey?=?key;
????????????String?mapValue?=?map.get(mapKey);
????????}
????}
在for循環(huán)中遍歷value
?public?static?void?forEachValues()?{
????????for?(String?key?:?map.values())?{
????????????String?val?=?key;
????????}
????}
Iterator遍歷
public?static?void?forEachIterator()?{
????????Iterator>?entries?=?map.entrySet().iterator();
????????while?(entries.hasNext())?{
????????????Entry?entry?=?entries.next();
????????????String?key?=?entry.getKey();
????????????String?value?=?entry.getValue();
????????}
????}
forEach jdk1.8遍歷
?public?static?void?forEach()?{
????????map.forEach((key,?val)?->?{
????????????String?key1?=?key;
????????????String?value?=?val;
????????});
????}
Stream jdk1.8遍歷
??map.entrySet().stream().forEach((entry)?->?{
????????????String?key?=?entry.getKey();
????????????String?value?=?entry.getValue();
????????});
Streamparallel jdk1.8遍歷
?public?static?void?forEachStreamparallel()?{
????????map.entrySet().parallelStream().forEach((entry)?->?{
????????????String?key?=?entry.getKey();
????????????String?value?=?entry.getValue();
????????});
????}
以上就是常見的對于map的一些遍歷的方式,下面我們來寫個(gè)測試用例來看下這些遍歷方式,哪些是效率最好的。下面測試用例是基于JMH來測試的 首先引入pom
??
????????????org.openjdk.jmh
????????????jmh-core
????????????1.23
????????
????????
????????????org.openjdk.jmh
????????????jmh-generator-annprocess
????????????1.23
????????????provided
????????
關(guān)于jmh測試如可能會(huì)影響結(jié)果的一些因素這里就不詳細(xì)介紹了,可以參考文末的第一個(gè)鏈接寫的非常詳細(xì)。以及測試用例為什么要這么寫(都是為了消除JIT對測試代碼的影響)這是參照官網(wǎng)的鏈接:編寫測試代碼如下:
package?com.workit.autoconfigure.autoconfigure.controller;
import?org.openjdk.jmh.annotations.*;
import?org.openjdk.jmh.infra.Blackhole;
import?org.openjdk.jmh.results.format.ResultFormatType;
import?org.openjdk.jmh.runner.Runner;
import?org.openjdk.jmh.runner.RunnerException;
import?org.openjdk.jmh.runner.options.Options;
import?org.openjdk.jmh.runner.options.OptionsBuilder;
import?java.util.HashMap;
import?java.util.Iterator;
import?java.util.Map;
import?java.util.Map.Entry;
import?java.util.UUID;
import?java.util.concurrent.TimeUnit;
/**
?*?@author:公眾號(hào):java金融
?*?@Date:?
?*?@Description:微信搜一搜【java金融】回復(fù)666
?*/
@State(Scope.Thread)
@Warmup(iterations?=?5,?time?=?1,?timeUnit?=?TimeUnit.SECONDS)
@Measurement(iterations?=?5,?time?=?1,?timeUnit?=?TimeUnit.SECONDS)
@Fork(1)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
public?class?InstructionsBenchmark?{
????public?static?void?main(String[]?args)?throws?RunnerException?{
????????Options?opt?=?new?OptionsBuilder().include(InstructionsBenchmark.class.getSimpleName()).result("result.json").resultFormat(ResultFormatType.JSON).build();
????????new?Runner(opt).run();
????}
????static?final?int?BASE?=?42;
????static?int?add(int?key,int?val)?{
??????return??BASE?+?key?+val;
????}
????@Param({"1",?"10",?"100",?"1000","10000","100000"})
????int?size;
????private?static?Map??map;
????//?初始化方法,在全部Benchmark運(yùn)行之前進(jìn)行
????@Setup(Level.Trial)
????public?void?init()?{
????????map?=?new?HashMap<>(size);
????????for?(int?i?=?0;?i?????????????map.put(i,?i);
????????}
????}
????/**
?????*?在for循環(huán)中使用entries實(shí)現(xiàn)Map的遍歷:
?????*/
????@Benchmark
????public?static?void?forEachEntries(Blackhole?blackhole)?{
????????for?(Map.Entry?entry?:?map.entrySet())?{
????????????Integer?mapKey?=?entry.getKey();
????????????Integer?mapValue?=?entry.getValue();
????????????blackhole.consume(add(mapKey,mapValue));
????????}
????}
????/**
?????*?在for循環(huán)中遍歷key
?????*/
????@Benchmark
????public?static?StringBuffer?forEachKey(Blackhole?blackhole)?{
????????StringBuffer?stringBuffer?=?new?StringBuffer();
????????for?(Integer?key?:?map.keySet())?{
??????????//??Integer?mapValue?=?map.get(key);
????????????blackhole.consume(add(key,key));
????????}
????????return?stringBuffer;
????}
????/**
?????*?在for循環(huán)中遍歷value
?????*/
????@Benchmark
????public?static?void?forEachValues(Blackhole?blackhole)?{
????????for?(Integer?key?:?map.values())?{
????????????blackhole.consume(add(key,key));
????????}
????}
????/**
?????* Iterator遍歷;
?????*/
????@Benchmark
????public?static?void?forEachIterator(Blackhole?blackhole)?{
????????Iterator>?entries?=?map.entrySet().iterator();
????????while?(entries.hasNext())?{
????????????Entry?entry?=?entries.next();
????????????Integer?key?=?entry.getKey();
????????????Integer?value?=?entry.getValue();
????????????blackhole.consume(add(key,value));
????????}
????}
????/**
?????*?forEach?jdk1.8遍歷
?????*/
????@Benchmark
????public?static?void?forEachLamada(Blackhole?blackhole)?{
????????map.forEach((key,?value)?->?{
????????????blackhole.consume(add(key,value));
????????});
????}
????/**
?????*?forEach?jdk1.8遍歷
?????*/
????@Benchmark
????public?static?void?forEachStream(Blackhole?blackhole)?{
????????map.entrySet().stream().forEach((entry)?->?{
????????????Integer?key?=?entry.getKey();
????????????Integer?value?=?entry.getValue();
????????????blackhole.consume(add(key,value));
????????});
????}
????@Benchmark
????public?static?void?forEachStreamparallel(Blackhole?blackhole)?{
????????map.entrySet().parallelStream().forEach((entry)?->?{
????????????Integer?key?=?entry.getKey();
????????????Integer?value?=?entry.getValue();
????????????blackhole.consume(add(key,value));
????????});
????}
}
運(yùn)行結(jié)果如下:「注:運(yùn)行環(huán)境idea 2019.3,jdk1.8,windows7 64位?!?/strong>
Benchmark????????????????????????????????????(size)??Mode??Cnt????????Score????????Error??Units
InstructionsBenchmark.forEachEntries??????????????1??avgt????5???????10.021?±??????0.224??ns/op
InstructionsBenchmark.forEachEntries?????????????10??avgt????5???????71.709?±??????2.537??ns/op
InstructionsBenchmark.forEachEntries????????????100??avgt????5??????738.873?±?????12.132??ns/op
InstructionsBenchmark.forEachEntries???????????1000??avgt????5?????7804.431?±????136.635??ns/op
InstructionsBenchmark.forEachEntries??????????10000??avgt????5????88540.345?±??14915.682??ns/op
InstructionsBenchmark.forEachEntries?????????100000??avgt????5??1083347.001?±?136865.960??ns/op
InstructionsBenchmark.forEachIterator?????????????1??avgt????5???????10.675?±??????2.532??ns/op
InstructionsBenchmark.forEachIterator????????????10??avgt????5???????73.934?±??????4.517??ns/op
InstructionsBenchmark.forEachIterator???????????100??avgt????5??????775.847?±????198.806??ns/op
InstructionsBenchmark.forEachIterator??????????1000??avgt????5?????8905.041?±???1294.618??ns/op
InstructionsBenchmark.forEachIterator?????????10000??avgt????5????98686.478?±??10944.570??ns/op
InstructionsBenchmark.forEachIterator????????100000??avgt????5??1045309.216?±??36957.608??ns/op
InstructionsBenchmark.forEachKey??????????????????1??avgt????5???????18.478?±??????1.344??ns/op
InstructionsBenchmark.forEachKey?????????????????10??avgt????5???????76.398?±?????12.179??ns/op
InstructionsBenchmark.forEachKey????????????????100??avgt????5??????768.507?±?????23.892??ns/op
InstructionsBenchmark.forEachKey???????????????1000??avgt????5????11117.896?±???1665.021??ns/op
InstructionsBenchmark.forEachKey??????????????10000??avgt????5????84871.880?±??12056.592??ns/op
InstructionsBenchmark.forEachKey?????????????100000??avgt????5??1114948.566?±??65582.709??ns/op
InstructionsBenchmark.forEachLamada???????????????1??avgt????5????????9.444?±??????0.607??ns/op
InstructionsBenchmark.forEachLamada??????????????10??avgt????5???????76.125?±??????5.640??ns/op
InstructionsBenchmark.forEachLamada?????????????100??avgt????5??????861.601?±?????98.045??ns/op
InstructionsBenchmark.forEachLamada????????????1000??avgt????5?????7769.714?±???1663.914??ns/op
InstructionsBenchmark.forEachLamada???????????10000??avgt????5????73250.238?±???6032.161??ns/op
InstructionsBenchmark.forEachLamada??????????100000??avgt????5???836781.987?±??72125.745??ns/op
InstructionsBenchmark.forEachStream???????????????1??avgt????5???????29.113?±??????3.275??ns/op
InstructionsBenchmark.forEachStream??????????????10??avgt????5??????117.951?±?????13.755??ns/op
InstructionsBenchmark.forEachStream?????????????100??avgt????5?????1064.767?±?????66.869??ns/op
InstructionsBenchmark.forEachStream????????????1000??avgt????5?????9969.549?±????342.483??ns/op
InstructionsBenchmark.forEachStream???????????10000??avgt????5????93154.061?±???7638.122??ns/op
InstructionsBenchmark.forEachStream??????????100000??avgt????5??1113961.590?±?218662.668??ns/op
InstructionsBenchmark.forEachStreamparallel???????1??avgt????5???????65.466?±??????5.519??ns/op
InstructionsBenchmark.forEachStreamparallel??????10??avgt????5?????2298.999?±????721.455??ns/op
InstructionsBenchmark.forEachStreamparallel?????100??avgt????5?????8270.759?±???1801.082??ns/op
InstructionsBenchmark.forEachStreamparallel????1000??avgt????5????16049.564?±???1972.856??ns/op
InstructionsBenchmark.forEachStreamparallel???10000??avgt????5????69230.849?±??12169.260??ns/op
InstructionsBenchmark.forEachStreamparallel??100000??avgt????5???638129.559?±??14885.962??ns/op
InstructionsBenchmark.forEachValues???????????????1??avgt????5????????9.743?±??????2.770??ns/op
InstructionsBenchmark.forEachValues??????????????10??avgt????5???????70.761?±?????16.574??ns/op
InstructionsBenchmark.forEachValues?????????????100??avgt????5??????745.069?±????329.548??ns/op
InstructionsBenchmark.forEachValues????????????1000??avgt????5?????7772.584?±???1702.295??ns/op
InstructionsBenchmark.forEachValues???????????10000??avgt????5????74063.468?±??23752.678??ns/op
InstructionsBenchmark.forEachValues??????????100000??avgt????5???994057.370?±?279310.867??ns/op


通過上述的圖我們可以發(fā)現(xiàn),數(shù)據(jù)量較小的時(shí)候forEachEntries和forEachIterator、以及l(fā)amada循環(huán)效率都差不多forEachStreamarallel的效率反而較低,只有當(dāng)數(shù)據(jù)量達(dá)到10000以上parallelStream的優(yōu)勢就體現(xiàn)出來了。所以平時(shí)選擇使用哪種循環(huán)方式的時(shí)候沒必要太糾結(jié)哪一種方式,其實(shí)每種方式之間的效率還是微乎其微的。選擇適合自己的就好。為什么parallelStream在數(shù)據(jù)量較小的時(shí)候效率反而不行?這個(gè)大家可以在下方留言哦。
總結(jié)
上面小實(shí)驗(yàn)只是在我機(jī)器上跑出來的結(jié)果,可能放到不同的機(jī)器運(yùn)行結(jié)果有不一樣哦,大家感興趣的同學(xué)可以把代碼貼到自己的機(jī)器上跑一跑,也許我這這個(gè)結(jié)論就不適用了。
結(jié)束


近期精彩內(nèi)容推薦:??


