舉例詳解Java編程中HashMap的初始化以及遍歷的方法
一、HashMap的初始化
1、HashMap 初始化的文藝寫(xiě)法
HashMap 是一種常用的數(shù)據(jù)結(jié)構(gòu),一般用來(lái)做數(shù)據(jù)字典或者 Hash 查找的容器。普通青年一般會(huì)這么初始化:
HashMap<String, String> map = new HashMap<String, String>(); map.put("Name", "June"); map.put("QQ", "2572073701");
看完這段代碼,很多人都會(huì)覺(jué)得這么寫(xiě)太啰嗦了,對(duì)此,文藝青年一般這么來(lái)了:
HashMap<String, String> map = new HashMap<String, String>() { { put("Name", "June"); put("QQ", "2572073701"); } };
嗯,看起來(lái)優(yōu)雅了不少,一步到位,一氣呵成的趕腳。然后問(wèn)題來(lái)了,有童鞋會(huì)問(wèn):納尼?這里的雙括號(hào)到底什么意思,什么用法呢?哈哈,其實(shí)很簡(jiǎn)單,看看下面的代碼你就知道啥意思了。
public class Test { /*private static HashMap< String, String> map = new HashMap< String, String>() { { put("Name", "June"); put("QQ", "2572073701"); } };*/ public Test() { System.out.println("Constructor called:構(gòu)造器被調(diào)用"); } static { System.out.println("Static block called:靜態(tài)塊被調(diào)用"); } { System.out.println("Instance initializer called:實(shí)例初始化塊被調(diào)用"); } public static void main(String[] args) { new Test(); System.out.println("======================="); new Test(); } }
輸出:
Static block called:靜態(tài)塊被調(diào)用 Instance initializer called:實(shí)例初始化被調(diào)用 Constructor called:構(gòu)造器被調(diào)用 ======================= Instance initializer called:實(shí)例初始化被調(diào)用 Constructor called:構(gòu)造器被調(diào)用
也就是說(shuō)第一層括弧實(shí)際是定義了一個(gè)匿名內(nèi)部類 (Anonymous Inner Class),第二層括弧實(shí)際上是一個(gè)實(shí)例初始化塊 (instance initializer block),這個(gè)塊在內(nèi)部匿名類構(gòu)造時(shí)被執(zhí)行。這個(gè)塊之所以被叫做“實(shí)例初始化塊”是因?yàn)樗鼈儽欢x在了一個(gè)類的實(shí)例范圍內(nèi)。
上面代碼如果是寫(xiě)在 Test 類中,編譯后你會(huì)看到會(huì)生成 Test$1.class 文件,反編譯該文件內(nèi)容:
D:\eclipse_indigo\workspace_home\CDHJobs\bin\pvuv\>jad -p Test$1.class
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov. // Jad home page: http://www.kpdus.com/jad.html // Decompiler options: packimports(3) // Source File Name: Test.java
package pvuv.zhaopin; import java.util.HashMap; // Referenced classes of package pvuv.zhaopin: // Test class Test$1 extends HashMap // 創(chuàng)建了一個(gè) HashMap 的子類 { Test$1() { // 第二個(gè) {} 中的代碼放到了構(gòu)造方法中去了 put("Name", "June"); put("QQ", "2572073701"); } }
D:\eclipse_indigo\workspace_home\CDHJobs\bin\pvuv\>
2、推而廣之
這種寫(xiě)法,推而廣之,在初始化 ArrayList、Set 的時(shí)候都可以這么玩,比如你還可以這么玩:
List<String> names = new ArrayList<String>() { { for (int i = 0; i < 10; i++) { add("A" + i); } } }; System.out.println(names.toString()); // [A0, A1, A2, A3, A4, A5, A6, A7, A8, A9]
3、Java7:增加對(duì) collections 的支持
在 Java 7 中你可以像 Ruby, Perl、Python 一樣創(chuàng)建 collections 了。
Note:這些集合是不可變的。
PS:由于原文[5]作者并沒(méi)有標(biāo)出 java 7 哪個(gè)小版本號(hào)引入的這些新特性,對(duì)于留言報(bào)錯(cuò)的同學(xué),請(qǐng)嘗試大于 1.7.0_09 或者 java8 試試?
List list = new ArrayList(); list.add("item"); String item = list.get(0); Set< String> set = new HashSet< String>(); set.add("item"); Map< String, Integer> map = new HashMap< String, Integer>(); map.put("key", 1); int value = map.get("key"); // 現(xiàn)在你還可以: List< String> list = ["item"]; String item = list[0]; Set< String> set = {"item"}; Map< String, Integer> map = {"key" : 1}; int value = map["key"];
4、文藝寫(xiě)法的潛在問(wèn)題
文章開(kāi)頭提到的文藝寫(xiě)法的好處很明顯就是一目了然。這里來(lái)羅列下此種方法的壞處,如果這個(gè)對(duì)象要串行化,可能會(huì)導(dǎo)致串行化失敗。
1.此種方式是匿名內(nèi)部類的聲明方式,所以引用中持有著外部類的引用。所以當(dāng)串行化這個(gè)集合時(shí)外部類也會(huì)被不知不覺(jué)的串行化,當(dāng)外部類沒(méi)有實(shí)現(xiàn)serialize接口時(shí),就會(huì)報(bào)錯(cuò)。
2.上例中,其實(shí)是聲明了一個(gè)繼承自HashMap的子類。然而有些串行化方法,例如要通過(guò)Gson串行化為json,或者要串行化為xml時(shí),類庫(kù)中提供的方式,是無(wú)法串行化Hashset或者HashMap的子類的,從而導(dǎo)致串行化失敗。解決辦法:重新初始化為一個(gè)HashMap對(duì)象:
new HashMap(map);
這樣就可以正常初始化了。
5、執(zhí)行效率問(wèn)題
當(dāng)一種新的工具或者寫(xiě)法出現(xiàn)時(shí),猿們都會(huì)來(lái)一句:性能怎么樣?(這和男生談?wù)撁眉埖谝痪湟话愣际牵骸伴L(zhǎng)得咋樣?三圍多少?”一個(gè)道理:))
關(guān)于這個(gè)兩種寫(xiě)法我這邊筆記本上測(cè)試文藝寫(xiě)法、普通寫(xiě)法分別創(chuàng)建 10,000,000 個(gè) Map 的結(jié)果是 1217、1064,相差 13%。
public class Test { public static void main(String[] args) { long st = System.currentTimeMillis(); /* for (int i = 0; i < 10000000; i++) { HashMap< String, String> map = new HashMap< String, String>() { { put("Name", "June"); put("QQ", "2572073701"); } }; } System.out.println(System.currentTimeMillis() - st); // 1217 */ for (int i = 0; i < 10000000; i++) { HashMap< String, String> map = new HashMap< String, String>(); map.put("Name", "June"); map.put("QQ", "2572073701"); } System.out.println(System.currentTimeMillis() - st); // 1064 } }
6、由實(shí)例初始化塊聯(lián)想到的一些變量初始化問(wèn)題
從代碼上看,a 為什么可以不先聲明類型?你覺(jué)得 a、b、c 的值分別是多少?能說(shuō)明理由么?
TIPS:如果你對(duì)這塊機(jī)制不了解,建議試著反編譯一下字節(jié)碼文件。
6.1 測(cè)試源碼
public class Test { int e = 6; Test() { int c = 1; this.f = 5; int e = 66; } int f = 55; int c = 11; int b = 1; { a = 3; b = 22; } int a = 33; static { d = 4; } static int d = 44; int g = 7; int h = 8; public int test(){ g = 77; int h = 88; System.out.println("h - 成員變量:" + this.h); System.out.println("h - 局部變量: " + h); return g; } public static void main(String[] args) { System.out.println("a: " + new Test().a); System.out.println("b: " + new Test().b); System.out.println("c: " + new Test().c); System.out.println("d: " + new Test().d); System.out.println("f: " + new Test().f); System.out.println("e: " + new Test().e); System.out.println("g: " + new Test().test()); } }
6.2 字節(jié)碼反編譯:
// Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov. // Jad home page: http://www.kpdus.com/jad.html // Decompiler options: packimports(3) // Source File Name: Test.java import java.io.PrintStream; public class Test { Test() { this.e = 6; f = 55; this.c = 11; b = 1; a = 3; b = 22; a = 33; g = 7; h = 8; int c = 1; f = 5; int e = 66; } public int test() { g = 77; int h = 88; System.out.println((new StringBuilder("h - \u6210\u5458\u53D8\u91CF\uFF1A")).append(this.h).toString()); System.out.println((new StringBuilder("h - \u5C40\u90E8\u53D8\u91CF: ")).append(h).toString()); return g; } public static void main(String args[]) { System.out.println((new StringBuilder("a: ")).append((new Test()).a).toString()); System.out.println((new StringBuilder("b: ")).append((new Test()).b).toString()); System.out.println((new StringBuilder("c: ")).append((new Test()).c).toString()); new Test(); System.out.println((new StringBuilder("d: ")).append(d).toString()); System.out.println((new StringBuilder("f: ")).append((new Test()).f).toString()); System.out.println((new StringBuilder("e: ")).append((new Test()).e).toString()); System.out.println((new StringBuilder("g: ")).append((new Test()).test()).toString()); } int e; int f; int c; int b; int a; static int d = 4; int g; int h; static { d = 44; } } 6.3 output: a: 33 b: 22 c: 11 d: 44 f: 5 e: 6 h - 成員變量:8 h - 局部變量: 88 g: 77
二、HashMap遍歷方法示例
第一種:
Map map = new HashMap(); Iterator iter = map.entrySet().iterator(); while (iter.hasNext()) { Map.Entry entry = (Map.Entry) iter.next(); Object key = entry.getKey(); Object val = entry.getValue(); }
效率高,以后一定要使用此種方式!
第二種:
Map map = new HashMap(); Iterator iter = map.keySet().iterator(); while (iter.hasNext()) { Object key = iter.next(); Object val = map.get(key); }
效率低,以后盡量少使用!
HashMap的遍歷有兩種常用的方法,那就是使用keyset及entryset來(lái)進(jìn)行遍歷,但兩者的遍歷速度是有差別的,下面請(qǐng)看實(shí)例:
public class HashMapTest { public static void main(String[] args) ...{ HashMap hashmap = new HashMap(); for (int i = 0; i < 1000; i ) ...{ hashmap.put("" i, "thanks"); } long bs = Calendar.getInstance().getTimeInMillis(); Iterator iterator = hashmap.keySet().iterator(); while (iterator.hasNext()) ...{ System.out.print(hashmap.get(iterator.next())); } System.out.println(); System.out.println(Calendar.getInstance().getTimeInMillis() - bs); listHashMap(); } public static void listHashMap() ...{ java.util.HashMap hashmap = new java.util.HashMap(); for (int i = 0; i < 1000; i ) ...{ hashmap.put("" i, "thanks"); } long bs = Calendar.getInstance().getTimeInMillis(); java.util.Iterator it = hashmap.entrySet().iterator(); while (it.hasNext()) ...{ java.util.Map.Entry entry = (java.util.Map.Entry) it.next(); // entry.getKey() 返回與此項(xiàng)對(duì)應(yīng)的鍵 // entry.getValue() 返回與此項(xiàng)對(duì)應(yīng)的值 System.out.print(entry.getValue()); } System.out.println(); System.out.println(Calendar.getInstance().getTimeInMillis() - bs); } }
對(duì)于keySet其實(shí)是遍歷了2次,一次是轉(zhuǎn)為iterator,一次就從hashmap中取出key所對(duì)于的value。而entryset只是遍歷了第一次,他把key和value都放到了entry中,所以就快了。
注:Hashtable的遍歷方法和以上的差不多!
相關(guān)文章
SpringBoot實(shí)現(xiàn)API接口多版本支持的示例代碼
這篇文章主要介紹了SpringBoot實(shí)現(xiàn)API接口多版本支持的示例代碼,文中通過(guò)示例代碼介紹的非常詳細(xì),對(duì)大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價(jià)值,需要的朋友們下面隨著小編來(lái)一起學(xué)習(xí)學(xué)習(xí)吧2020-10-10java實(shí)現(xiàn)將數(shù)字轉(zhuǎn)換成人民幣大寫(xiě)
前面給大家介紹過(guò)使用javascript,php,c#,python等語(yǔ)言實(shí)現(xiàn)人民幣大寫(xiě)格式化,這篇文章主要介紹了java實(shí)現(xiàn)將數(shù)字轉(zhuǎn)換成人民幣大寫(xiě)的代碼,非常的簡(jiǎn)單實(shí)用,分享給大家,需要的朋友可以參考下2015-04-04詳解spring-boot下如何滿足多生產(chǎn)環(huán)境中個(gè)性化定制功能
這篇文章主要介紹了詳解spring-boot下如何滿足多生產(chǎn)環(huán)境中個(gè)性化定制功能,小編覺(jué)得挺不錯(cuò)的,現(xiàn)在分享給大家,也給大家做個(gè)參考。一起跟隨小編過(guò)來(lái)看看吧2019-03-03java使用Filter實(shí)現(xiàn)自動(dòng)登錄的方法
這篇文章主要為大家詳細(xì)介紹了java使用Filter實(shí)現(xiàn)自動(dòng)登錄的方法,具有一定的參考價(jià)值,感興趣的小伙伴們可以參考一下2017-04-04對(duì)arraylist中元素進(jìn)行排序?qū)嵗a
這篇文章主要介紹了對(duì)arraylist中元素進(jìn)行排序?qū)嵗a,還是比較不錯(cuò)的,這里分享給大家,供需要的朋友參考。2017-11-11