new HashMap(){{put(key, value);}}的解释

来源:互联网 发布:淘宝怎么获得天猫积分 编辑:程序博客网 时间:2024/06/05 20:29

一、HashMap的初始化
1、HashMap 初始化的文艺写法
   HashMap 是一种常用的数据结构,一般用来做数据字典或者 Hash 查找的容器。普通青年一般会这么初始化:

 

[java] view plain copy
  1. HashMap<String, String> map =  
  2.   new HashMap<String, String>();  
  3.  map.put("Name""June");   
  4.  map.put("QQ""2572073701");  

看完这段代码,很多人都会觉得这么写太啰嗦了,对此,文艺青年一般这么来了:
 

[java] view plain copy
  1. HashMap<String, String> map =  
  2.   new HashMap<String, String>() {  
  3.  {  
  4.  put("Name""June");   
  5.  put("QQ""2572073701");   
  6.  }  
  7.  };  

   嗯,看起来优雅了不少,一步到位,一气呵成的赶脚。然后问题来了,有童鞋会问:纳尼?这里的双括号到底什么意思,什么用法呢?哈哈,其实很简单,看看下面的代码你就知道啥意思了。

[java] view plain copy
  1. public class Test {  
  2.  /*private static HashMap< String, String> map = new HashMap< String, String>() { 
  3.   { 
  4.    put("Name", "June"); 
  5.    put("QQ", "2572073701"); 
  6.   } 
  7.  };*/  
  8.  public Test() {  
  9.   System.out.println("Constructor called:构造器被调用");  
  10.  }  
  11.  static {  
  12.   System.out.println("Static block called:静态块被调用");  
  13.  }  
  14.  {  
  15.   System.out.println("Instance initializer called:实例初始化块被调用");  
  16.  }  
  17.  public static void main(String[] args) {  
  18.   new Test();  
  19.   System.out.println("=======================");  
  20.   new Test();  
  21.  }  
  22. }  

输出:

[plain] view plain copy
  1. Static block called:静态块被调用  
  2. Instance initializer called:实例初始化被调用  
  3. Constructor called:构造器被调用  
  4. =======================  
  5. Instance initializer called:实例初始化被调用  
  6. Constructor called:构造器被调用  

也就是说第一层括弧实际是定义了一个匿名内部类 (Anonymous Inner Class),第二层括弧实际上是一个实例初始化块 (instance initializer block),这个块在内部匿名类构造时被执行。这个块之所以被叫做“实例初始化块”是因为它们被定义在了一个类的实例范围内。
上面代码如果是写在 Test 类中,编译后你会看到会生成 Test$1.class 文件,反编译该文件内容:

D:eclipse_indigoworkspace_homeCDHJobsbinpvuv>jad -p Test$1.class
[plain] view plain copy
  1. // Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.  
  2. // Jad home page: http://www.kpdus.com/jad.html  
  3. // Decompiler options: packimports(3)  
  4. // Source File Name: Test.java  
[java] view plain copy
  1. package pvuv.zhaopin;  
  2. import java.util.HashMap;  
  3. // Referenced classes of package pvuv.zhaopin:  
  4. // Test  
  5.  class Test$1 extends HashMap // 创建了一个 HashMap 的子类  
  6.  {  
  7.  Test$1()  
  8.  { // 第二个 {} 中的代码放到了构造方法中去了   
  9.  put("Name""June");  
  10.  put("QQ""2572073701");  
  11.  }  
  12.  }  
D:eclipse_indigoworkspace_homeCDHJobsbinpvuv>

2、推而广之
  这种写法,推而广之,在初始化 ArrayList、Set 的时候都可以这么玩,比如你还可以这么玩:

[java] view plain copy
  1. List<String> names = new ArrayList<String>() {  
  2. {  
  3. for (int i = 0; i < 10; i++) {  
  4.  add("A" + i);  
  5. }  
  6. }  
  7. };  
  8. 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 试试?

[java] view plain copy
  1. List list = new ArrayList();  
  2.  list.add("item");  
  3.  String item = list.get(0);  
  4.  Set< String> set = new HashSet< String>();  
  5.  set.add("item");  
  6.  Map< String, Integer> map = new HashMap< String, Integer>();  
  7.  map.put("key"1);  
  8.  int value = map.get("key");  
  9.  // 现在你还可以:   
  10.  List< String> list = ["item"];  
  11.  String item = list[0];  
  12.    
  13.  Set< String> set = {"item"};  
  14.    
  15.  Map< String, Integer> map = {"key" : 1};  
  16.  int value = map["key"];  

4、文艺写法的潜在问题
    文章开头提到的文艺写法的好处很明显就是一目了然。这里来罗列下此种方法的坏处,如果这个对象要串行化,可能会导致串行化失败。
  1.此种方式是匿名内部类的声明方式,所以引用中持有着外部类的引用。所以当串行化这个集合时外部类也会被不知不觉的串行化,当外部类没有实现serialize接口时,就会报错。

2.上例中,其实是声明了一个继承自HashMap的子类。然而有些串行化方法,例如要通过Gson串行化为json,或者要串行化为xml时,类库中提供的方式,是无法串行化Hashset或者HashMap的子类的,从而导致串行化失败。解决办法:重新初始化为一个HashMap对象:

[java] view plain copy
  1. new HashMap(map);  

这样就可以正常初始化了。

5、执行效率问题
   当一种新的工具或者写法出现时,猿们都会来一句:性能怎么样?(这和男生谈论妹纸第一句一般都是:“长得咋样?三围多少?”一个道理:))
关于这个两种写法我这边笔记本上测试文艺写法、普通写法分别创建 10,000,000 个 Map 的结果是 1217、1064,相差 13%。

[java] view plain copy
  1. public class Test {  
  2.  public static void main(String[] args) {  
  3.   long st = System.currentTimeMillis();  
  4.   /* 
  5.   for (int i = 0; i < 10000000; i++) { 
  6.    HashMap< String, String> map = new HashMap< String, String>() { 
  7.     { 
  8.      put("Name", "June"); 
  9.      put("QQ", "2572073701"); 
  10.     } 
  11.    }; 
  12.   } 
  13.   System.out.println(System.currentTimeMillis() - st); // 1217 
  14.   */  
  15.   for (int i = 0; i < 10000000; i++) {  
  16.    HashMap< String, String> map = new HashMap< String, String>();  
  17.    map.put("Name""June");  
  18.    map.put("QQ""2572073701");  
  19.   }  
  20.   System.out.println(System.currentTimeMillis() - st); // 1064  
  21.  }  
  22. }  

6、由实例初始化块联想到的一些变量初始化问题
  从代码上看,a 为什么可以不先声明类型?你觉得 a、b、c 的值分别是多少?能说明理由么?
TIPS:如果你对这块机制不了解,建议试着反编译一下字节码文件。

6.1 测试源码

[java] view plain copy
  1. public class Test {  
  2.    
  3.  int e = 6;  
  4.  Test() {  
  5.   int c = 1;  
  6.   this.f = 5;  
  7.   int e = 66;  
  8.  }  
  9.  int f = 55;  
  10.  int c = 11;  
  11.  int b = 1;  
  12.  {  
  13.   a = 3;  
  14.   b = 22;  
  15.  }  
  16.  int a = 33;  
  17.  static {  
  18.   d = 4;  
  19.  }  
  20.  static int d = 44;  
  21.    
  22.  int g = 7;  
  23.  int h = 8;  
  24.  public int test(){  
  25.   g = 77;  
  26.   int h = 88;  
  27.   System.out.println("h - 成员变量:" + this.h);  
  28.   System.out.println("h - 局部变量: " + h);  
  29.   return g;  
  30.  }  
  31.  public static void main(String[] args) {  
  32.   System.out.println("a: " + new Test().a);  
  33.   System.out.println("b: " + new Test().b);  
  34.   System.out.println("c: " + new Test().c);  
  35.   System.out.println("d: " + new Test().d);  
  36.   System.out.println("f: " + new Test().f);  
  37.   System.out.println("e: " + new Test().e);  
  38.   System.out.println("g: " + new Test().test());  
  39.  }  
  40. }  

6.2 字节码反编译:

[java] view plain copy
  1. // Decompiled by Jad v1.5.8g. Copyright 2001 Pavel Kouznetsov.  
  2. // Jad home page: http://www.kpdus.com/jad.html  
  3. // Decompiler options: packimports(3)  
  4. // Source File Name: Test.java  
  5. import java.io.PrintStream;  
  6. public class Test  
  7. {  
  8.  Test()  
  9.  {  
  10.   this.e = 6;  
  11.   f = 55;  
  12.   this.c = 11;  
  13.   b = 1;  
  14.   a = 3;  
  15.   b = 22;  
  16.   a = 33;  
  17.   g = 7;  
  18.   h = 8;  
  19.   int c = 1;  
  20.   f = 5;  
  21.   int e = 66;  
  22.  }  
  23.  public int test()  
  24.  {  
  25.   g = 77;  
  26.   int h = 88;  
  27.   System.out.println((new StringBuilder("h - u6210u5458u53D8u91CFuFF1A")).append(this.h).toString());  
  28.   System.out.println((new StringBuilder("h - u5C40u90E8u53D8u91CF: ")).append(h).toString());  
  29.   return g;  
  30.  }  
  31.  public static void main(String args[])  
  32.  {  
  33.   System.out.println((new StringBuilder("a: ")).append((new Test()).a).toString());  
  34.   System.out.println((new StringBuilder("b: ")).append((new Test()).b).toString());  
  35.   System.out.println((new StringBuilder("c: ")).append((new Test()).c).toString());  
  36.   new Test();  
  37.   System.out.println((new StringBuilder("d: ")).append(d).toString());  
  38.   System.out.println((new StringBuilder("f: ")).append((new Test()).f).toString());  
  39.   System.out.println((new StringBuilder("e: ")).append((new Test()).e).toString());  
  40.   System.out.println((new StringBuilder("g: ")).append((new Test()).test()).toString());  
  41.  }  
  42.  int e;  
  43.  int f;  
  44.  int c;  
  45.  int b;  
  46.  int a;  
  47.  static int d = 4;  
  48.  int g;  
  49.  int h;  
  50.  static  
  51.  {  
  52.   d = 44;  
  53.  }  
  54. }  
  55. 6.3 output:  
  56.  a: 33  
  57.  b: 22  
  58.  c: 11  
  59.  d: 44  
  60.  f: 5  
  61.  e: 6  
  62.  h - 成员变量:8  
  63.  h - 局部变量: 88  
  64.  g: 77  


二、HashMap遍历方法示例
第一种:
  

[java] view plain copy
  1. Map map = new HashMap();  
  2.   Iterator iter = map.entrySet().iterator();  
  3.   while (iter.hasNext()) {  
  4.   Map.Entry entry = (Map.Entry) iter.next(); Object key = entry.getKey();  
  5.   Object val = entry.getValue();  
  6.   }  

  效率高,以后一定要使用此种方式!
  第二种:
 

[java] view plain copy
  1. Map map = new HashMap();  
  2.  Iterator iter = map.keySet().iterator();  
  3.  while (iter.hasNext()) {  
  4.  Object key = iter.next();  
  5.  Object val = map.get(key);  
  6.  }  

  效率低,以后尽量少使用!
  HashMap的遍历有两种常用的方法,那就是使用keyset及entryset来进行遍历,但两者的遍历速度是有差别的,下面请看实例:
 

[java] view plain copy
  1. public class HashMapTest {  
  2.  public static void main(String[] args) ...{  
  3.  HashMap hashmap = new HashMap();  
  4.  for (int i = 0; i < 1000; i ) ...{  
  5.  hashmap.put("" i, "thanks");  
  6.  }  
  7.  long bs = Calendar.getInstance().getTimeInMillis();  
  8.  Iterator iterator = hashmap.keySet().iterator();  
  9.  while (iterator.hasNext()) ...{  
  10.  System.out.print(hashmap.get(iterator.next()));  
  11.  }  
  12.  System.out.println();  
  13.  System.out.println(Calendar.getInstance().getTimeInMillis() - bs);  
  14.  listHashMap();  
  15.  }  
  16.  public static void listHashMap() ...{  
  17.  java.util.HashMap hashmap = new java.util.HashMap();  
  18.  for (int i = 0; i < 1000; i ) ...{  
  19.  hashmap.put("" i, "thanks");  
  20.  }  
  21.  long bs = Calendar.getInstance().getTimeInMillis();  
  22.  java.util.Iterator it = hashmap.entrySet().iterator();  
  23.  while (it.hasNext()) ...{  
  24.  java.util.Map.Entry entry = (java.util.Map.Entry) it.next();  
  25.  // entry.getKey() 返回与此项对应的键  
  26.  // entry.getValue() 返回与此项对应的值  
  27.  System.out.print(entry.getValue());  
  28.  }  
  29.  System.out.println();  
  30.  System.out.println(Calendar.getInstance().getTimeInMillis() - bs);  
  31.  }  
  32.  }  

  对于keySet其实是遍历了2次,一次是转为iterator,一次就从hashmap中取出key所对于的value。而entryset只是遍历了第一次,他把key和value都放到了entry中,所以就快了。
  注:Hashtable的遍历方法和以上的差不多!

原创粉丝点击