举例详解Java编程中HashMap的初始化以及遍历的方法
一、HashMap的初始化
1、HashMap初始化的文艺写法
HashMap是一种常用的数据结构,一般用来做数据字典或者Hash查找的容器。普通青年一般会这么初始化:
HashMap<String,String>map=
newHashMap<String,String>();
map.put("Name","June");
map.put("QQ","2572073701");
看完这段代码,很多人都会觉得这么写太啰嗦了,对此,文艺青年一般这么来了:
HashMap<String,String>map=
newHashMap<String,String>(){
{
put("Name","June");
put("QQ","2572073701");
}
};
嗯,看起来优雅了不少,一步到位,一气呵成的赶脚。然后问题来了,有童鞋会问:纳尼?这里的双括号到底什么意思,什么用法呢?哈哈,其实很简单,看看下面的代码你就知道啥意思了。
publicclassTest{
/*privatestaticHashMap<String,String>map=newHashMap<String,String>(){
{
put("Name","June");
put("QQ","2572073701");
}
};*/
publicTest(){
System.out.println("Constructorcalled:构造器被调用");
}
static{
System.out.println("Staticblockcalled:静态块被调用");
}
{
System.out.println("Instanceinitializercalled:实例初始化块被调用");
}
publicstaticvoidmain(String[]args){
newTest();
System.out.println("=======================");
newTest();
}
}
输出:
Staticblockcalled:静态块被调用 Instanceinitializercalled:实例初始化被调用 Constructorcalled:构造器被调用 ======================= Instanceinitializercalled:实例初始化被调用 Constructorcalled:构造器被调用
也就是说第一层括弧实际是定义了一个匿名内部类(AnonymousInnerClass),第二层括弧实际上是一个实例初始化块(instanceinitializerblock),这个块在内部匿名类构造时被执行。这个块之所以被叫做“实例初始化块”是因为它们被定义在了一个类的实例范围内。
上面代码如果是写在Test类中,编译后你会看到会生成Test$1.class文件,反编译该文件内容:
D:\eclipse_indigo\workspace_home\CDHJobs\bin\pvuv\>jad-pTest$1.class
//DecompiledbyJadv1.5.8g.Copyright2001PavelKouznetsov. //Jadhomepage:http://www.kpdus.com/jad.html //Decompileroptions:packimports(3) //SourceFileName:Test.java
packagepvuv.zhaopin;
importjava.util.HashMap;
//Referencedclassesofpackagepvuv.zhaopin:
//Test
classTest$1extendsHashMap//创建了一个HashMap的子类
{
Test$1()
{//第二个{}中的代码放到了构造方法中去了
put("Name","June");
put("QQ","2572073701");
}
}
D:\eclipse_indigo\workspace_home\CDHJobs\bin\pvuv\>
2、推而广之
这种写法,推而广之,在初始化ArrayList、Set的时候都可以这么玩,比如你还可以这么玩:
List<String>names=newArrayList<String>(){
{
for(inti=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的支持
在Java7中你可以像Ruby,Perl、Python一样创建collections了。
Note:这些集合是不可变的。
PS:由于原文[5]作者并没有标出java7哪个小版本号引入的这些新特性,对于留言报错的同学,请尝试大于1.7.0_09或者java8试试?
Listlist=newArrayList();
list.add("item");
Stringitem=list.get(0);
Set<String>set=newHashSet<String>();
set.add("item");
Map<String,Integer>map=newHashMap<String,Integer>();
map.put("key",1);
intvalue=map.get("key");
//现在你还可以:
List<String>list=["item"];
Stringitem=list[0];
Set<String>set={"item"};
Map<String,Integer>map={"key":1};
intvalue=map["key"];
4、文艺写法的潜在问题
文章开头提到的文艺写法的好处很明显就是一目了然。这里来罗列下此种方法的坏处,如果这个对象要串行化,可能会导致串行化失败。
1.此种方式是匿名内部类的声明方式,所以引用中持有着外部类的引用。所以当串行化这个集合时外部类也会被不知不觉的串行化,当外部类没有实现serialize接口时,就会报错。
2.上例中,其实是声明了一个继承自HashMap的子类。然而有些串行化方法,例如要通过Gson串行化为json,或者要串行化为xml时,类库中提供的方式,是无法串行化Hashset或者HashMap的子类的,从而导致串行化失败。解决办法:重新初始化为一个HashMap对象:
newHashMap(map);
这样就可以正常初始化了。
5、执行效率问题
当一种新的工具或者写法出现时,猿们都会来一句:性能怎么样?(这和男生谈论妹纸第一句一般都是:“长得咋样?三围多少?”一个道理:))
关于这个两种写法我这边笔记本上测试文艺写法、普通写法分别创建10,000,000个Map的结果是1217、1064,相差13%。
publicclassTest{
publicstaticvoidmain(String[]args){
longst=System.currentTimeMillis();
/*
for(inti=0;i<10000000;i++){
HashMap<String,String>map=newHashMap<String,String>(){
{
put("Name","June");
put("QQ","2572073701");
}
};
}
System.out.println(System.currentTimeMillis()-st);//1217
*/
for(inti=0;i<10000000;i++){
HashMap<String,String>map=newHashMap<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测试源码
publicclassTest{
inte=6;
Test(){
intc=1;
this.f=5;
inte=66;
}
intf=55;
intc=11;
intb=1;
{
a=3;
b=22;
}
inta=33;
static{
d=4;
}
staticintd=44;
intg=7;
inth=8;
publicinttest(){
g=77;
inth=88;
System.out.println("h-成员变量:"+this.h);
System.out.println("h-局部变量:"+h);
returng;
}
publicstaticvoidmain(String[]args){
System.out.println("a:"+newTest().a);
System.out.println("b:"+newTest().b);
System.out.println("c:"+newTest().c);
System.out.println("d:"+newTest().d);
System.out.println("f:"+newTest().f);
System.out.println("e:"+newTest().e);
System.out.println("g:"+newTest().test());
}
}
6.2字节码反编译:
//DecompiledbyJadv1.5.8g.Copyright2001PavelKouznetsov.
//Jadhomepage:http://www.kpdus.com/jad.html
//Decompileroptions:packimports(3)
//SourceFileName:Test.java
importjava.io.PrintStream;
publicclassTest
{
Test()
{
this.e=6;
f=55;
this.c=11;
b=1;
a=3;
b=22;
a=33;
g=7;
h=8;
intc=1;
f=5;
inte=66;
}
publicinttest()
{
g=77;
inth=88;
System.out.println((newStringBuilder("h-\u6210\u5458\u53D8\u91CF\uFF1A")).append(this.h).toString());
System.out.println((newStringBuilder("h-\u5C40\u90E8\u53D8\u91CF:")).append(h).toString());
returng;
}
publicstaticvoidmain(Stringargs[])
{
System.out.println((newStringBuilder("a:")).append((newTest()).a).toString());
System.out.println((newStringBuilder("b:")).append((newTest()).b).toString());
System.out.println((newStringBuilder("c:")).append((newTest()).c).toString());
newTest();
System.out.println((newStringBuilder("d:")).append(d).toString());
System.out.println((newStringBuilder("f:")).append((newTest()).f).toString());
System.out.println((newStringBuilder("e:")).append((newTest()).e).toString());
System.out.println((newStringBuilder("g:")).append((newTest()).test()).toString());
}
inte;
intf;
intc;
intb;
inta;
staticintd=4;
intg;
inth;
static
{
d=44;
}
}
6.3output:
a:33
b:22
c:11
d:44
f:5
e:6
h-成员变量:8
h-局部变量:88
g:77
二、HashMap遍历方法示例
第一种:
Mapmap=newHashMap();
Iteratoriter=map.entrySet().iterator();
while(iter.hasNext()){
Map.Entryentry=(Map.Entry)iter.next();Objectkey=entry.getKey();
Objectval=entry.getValue();
}
效率高,以后一定要使用此种方式!
第二种:
Mapmap=newHashMap();
Iteratoriter=map.keySet().iterator();
while(iter.hasNext()){
Objectkey=iter.next();
Objectval=map.get(key);
}
效率低,以后尽量少使用!
HashMap的遍历有两种常用的方法,那就是使用keyset及entryset来进行遍历,但两者的遍历速度是有差别的,下面请看实例:
publicclassHashMapTest{
publicstaticvoidmain(String[]args)...{
HashMaphashmap=newHashMap();
for(inti=0;i<1000;i)...{
hashmap.put(""i,"thanks");
}
longbs=Calendar.getInstance().getTimeInMillis();
Iteratoriterator=hashmap.keySet().iterator();
while(iterator.hasNext())...{
System.out.print(hashmap.get(iterator.next()));
}
System.out.println();
System.out.println(Calendar.getInstance().getTimeInMillis()-bs);
listHashMap();
}
publicstaticvoidlistHashMap()...{
java.util.HashMaphashmap=newjava.util.HashMap();
for(inti=0;i<1000;i)...{
hashmap.put(""i,"thanks");
}
longbs=Calendar.getInstance().getTimeInMillis();
java.util.Iteratorit=hashmap.entrySet().iterator();
while(it.hasNext())...{
java.util.Map.Entryentry=(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的遍历方法和以上的差不多!