举例详解Java编程中HashMap的初始化以及遍历的方法
一、hashmap的初始化
1、hashmap 初始化的文艺写法
hashmap 是一种常用的数据结构,一般用来做数据字典或者 hash 查找的容器。普通青年一般会这么初始化:
hashmap<string, string> map = new hashmap<string, string>(); map.put("name", "june"); map.put("qq", "2572073701");
看完这段代码,很多人都会觉得这么写太啰嗦了,对此,文艺青年一般这么来了:
hashmap<string, string> map = new hashmap<string, string>() { { put("name", "june"); put("qq", "2572073701"); } };
嗯,看起来优雅了不少,一步到位,一气呵成的赶脚。然后问题来了,有童鞋会问:纳尼?这里的双括号到底什么意思,什么用法呢?哈哈,其实很简单,看看下面的代码你就知道啥意思了。
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:构造器被调用"); } static { system.out.println("static block called:静态块被调用"); } { system.out.println("instance initializer called:实例初始化块被调用"); } public static void main(string[] args) { new test(); system.out.println("======================="); new test(); } }
输出:
static block called:静态块被调用 instance initializer called:实例初始化被调用 constructor called:构造器被调用 ======================= instance initializer called:实例初始化被调用 constructor called:构造器被调用
也就是说第一层括弧实际是定义了一个匿名内部类 (anonymous inner class),第二层括弧实际上是一个实例初始化块 (instance initializer block),这个块在内部匿名类构造时被执行。这个块之所以被叫做“实例初始化块”是因为它们被定义在了一个类的实例范围内。
上面代码如果是写在 test 类中,编译后你会看到会生成 test$1.class 文件,反编译该文件内容:
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 // 创建了一个 hashmap 的子类 { test$1() { // 第二个 {} 中的代码放到了构造方法中去了 put("name", "june"); put("qq", "2572073701"); } }
d:\eclipse_indigo\workspace_home\cdhjobs\bin\pvuv\>
2、推而广之
这种写法,推而广之,在初始化 arraylist、set 的时候都可以这么玩,比如你还可以这么玩:
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:增加对 collections 的支持
在 java 7 中你可以像 ruby, perl、python 一样创建 collections 了。
note:这些集合是不可变的。
ps:由于原文[5]作者并没有标出 java 7 哪个小版本号引入的这些新特性,对于留言报错的同学,请尝试大于 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"); // 现在你还可以: list< string> list = ["item"]; string item = list[0]; set< string> set = {"item"}; map< string, integer> map = {"key" : 1}; int value = map["key"];
4、文艺写法的潜在问题
文章开头提到的文艺写法的好处很明显就是一目了然。这里来罗列下此种方法的坏处,如果这个对象要串行化,可能会导致串行化失败。
1.此种方式是匿名内部类的声明方式,所以引用中持有着外部类的引用。所以当串行化这个集合时外部类也会被不知不觉的串行化,当外部类没有实现serialize接口时,就会报错。
2.上例中,其实是声明了一个继承自hashmap的子类。然而有些串行化方法,例如要通过gson串行化为json,或者要串行化为xml时,类库中提供的方式,是无法串行化hashset或者hashmap的子类的,从而导致串行化失败。解决办法:重新初始化为一个hashmap对象:
new hashmap(map);
这样就可以正常初始化了。
5、执行效率问题
当一种新的工具或者写法出现时,猿们都会来一句:性能怎么样?(这和男生谈论妹纸第一句一般都是:“长得咋样?三围多少?”一个道理:))
关于这个两种写法我这边笔记本上测试文艺写法、普通写法分别创建 10,000,000 个 map 的结果是 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、由实例初始化块联想到的一些变量初始化问题
从代码上看,a 为什么可以不先声明类型?你觉得 a、b、c 的值分别是多少?能说明理由么?
tips:如果你对这块机制不了解,建议试着反编译一下字节码文件。
6.1 测试源码
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 字节码反编译:
// 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来进行遍历,但两者的遍历速度是有差别的,下面请看实例:
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() 返回与此项对应的键 // entry.getvalue() 返回与此项对应的值 system.out.print(entry.getvalue()); } system.out.println(); system.out.println(calendar.getinstance().gettimeinmillis() - bs); } }
对于keyset其实是遍历了2次,一次是转为iterator,一次就从hashmap中取出key所对于的value。而entryset只是遍历了第一次,他把key和value都放到了entry中,所以就快了。
注:hashtable的遍历方法和以上的差不多!
推荐阅读
-
举例详解Java编程中HashMap的初始化以及遍历的方法
-
详解Java编程中throw和throws子句的使用方法
-
Jquery遍历筛选数组的几种方法和遍历解析json对象,Map()方法详解以及数组中查询某值是否存在
-
java中关于Map的三种遍历方法详解
-
java中关于Map的三种遍历方法详解
-
Jquery遍历筛选数组的几种方法和遍历解析json对象,Map()方法详解以及数组中查询某值是否存在
-
荐 浅谈Java中类和对象的初始化、实例化以及方法重载的底层机制
-
荐 浅谈Java中类和对象的初始化、实例化以及方法重载的底层机制
-
java中关于Map的三种遍历方法详解
-
Java中HashMap遍历与使用的示例详解