首页 > 开发 > Java > 正文

举例详解Java编程中HashMap的初始化以及遍历的方法

2024-07-13 09:55:58
字体:
来源:转载
供稿:网友

这篇文章主要介绍了Java编程中HashMap的初始化以及遍历的方法,是Java入门学习中的基础知识,需要的朋友可以参考下

一、HashMap的初始化

1、HashMap 初始化的文艺写法

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

 

 
  1. HashMap<String, String> map = 
  2. new HashMap<String, String>(); 
  3. map.put("Name""June");  
  4. map.put("QQ""2572073701"); 

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

 

 
  1. HashMap<String, String> map = 
  2. new HashMap<String, String>() { 
  3. put("Name""June");  
  4. put("QQ""2572073701");  
  5. }; 

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

 

 
  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. static { 
  11. System.out.println("Static block called:静态块被调用"); 
  12. System.out.println("Instance initializer called:实例初始化块被调用"); 
  13. public static void main(String[] args) { 
  14. new Test(); 
  15. System.out.println("======================="); 
  16. new Test(); 

输出:

 

 
  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 文件,反编译该文件内容:

 

 
  1. D:/eclipse_indigo/workspace_home/CDHJobs/bin/pvuv/>jad -p Test$1.class 
 
  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 

 

 
  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. Test$1() 
  7. // 第二个 {} 中的代码放到了构造方法中去了  
  8. put("Name""June"); 
  9. put("QQ""2572073701"); 

 

 
  1. D:/eclipse_indigo/workspace_home/CDHJobs/bin/pvuv/> 

2、推而广之

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

 

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

 

 
  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对象:

 

 
  1. new HashMap(map); 

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

5、执行效率问题

当一种新的工具或者写法出现时,猿们都会来一句:性能怎么样?(这和男生谈论妹纸第一句一般都是:“长得咋样?三围多少?”一个道理:))

关于这个两种写法我这边笔记本上测试文艺写法、普通写法分别创建 10,000,000 个 Map 的结果是 1217、1064,相差 13%。

 

 
  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. System.out.println(System.currentTimeMillis() - st); // 1064 

6、由实例初始化块联想到的一些变量初始化问题

从代码上看,a 为什么可以不先声明类型?你觉得 a、b、c 的值分别是多少?能说明理由么?

TIPS:如果你对这块机制不了解,建议试着反编译一下字节码文件。

6.1 测试源码

 

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

6.2 字节码反编译:

 

 
  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. Test() 
  8. this.e = 6; 
  9. f = 55; 
  10. this.c = 11; 
  11. b = 1; 
  12. a = 3; 
  13. b = 22; 
  14. a = 33; 
  15. g = 7; 
  16. h = 8; 
  17. int c = 1; 
  18. f = 5; 
  19. int e = 66; 
  20. public int test() 
  21. g = 77; 
  22. int h = 88; 
  23. System.out.println((new StringBuilder("h - /u6210/u5458/u53D8/u91CF/uFF1A")).append(this.h).toString()); 
  24. System.out.println((new StringBuilder("h - /u5C40/u90E8/u53D8/u91CF: ")).append(h).toString()); 
  25. return g; 
  26. public static void main(String args[]) 
  27. System.out.println((new StringBuilder("a: ")).append((new Test()).a).toString()); 
  28. System.out.println((new StringBuilder("b: ")).append((new Test()).b).toString()); 
  29. System.out.println((new StringBuilder("c: ")).append((new Test()).c).toString()); 
  30. new Test(); 
  31. System.out.println((new StringBuilder("d: ")).append(d).toString()); 
  32. System.out.println((new StringBuilder("f: ")).append((new Test()).f).toString()); 
  33. System.out.println((new StringBuilder("e: ")).append((new Test()).e).toString()); 
  34. System.out.println((new StringBuilder("g: ")).append((new Test()).test()).toString()); 
  35. int e; 
  36. int f; 
  37. int c; 
  38. int b; 
  39. int a; 
  40. static int d = 4; 
  41. int g; 
  42. int h; 
  43. static 
  44. d = 44; 
  45. 6.3 output: 
  46. a: 33 
  47. b: 22 
  48. c: 11 
  49. d: 44 
  50. f: 5 
  51. e: 6 
  52. h - 成员变量:8 
  53. h - 局部变量: 88 
  54. g: 77 

二、HashMap遍历方法示例

第一种:

 

 
  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.   } 

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

第二种:

 

 
  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来进行遍历,但两者的遍历速度是有差别的,下面请看实例:

 

 
  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的遍历方法和以上的差不多!

发表评论 共有条评论
用户名: 密码:
验证码: 匿名发表