http://www.nulj.cn

首页 所有文章 归档 留言 关于我

你可能没用过这种方式的集合!new HashMap<K,V>(){{put(K,V);}};_JAVA葵花宝典

编辑:李靖2019/3/16993次阅读阅读转载: JAVA葵花宝典

来源:https://blog.csdn.net/luman1991/article/details/53034602

一、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_indigoworkspace_homeCDHJobsbinpvuv>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_indigoworkspace_homeCDHJobsbinpvuv>

  • 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、文艺写法的潜在问题

    文章开头提到的文艺写法的好处很明显就是一目了然。这里来罗列下此种方法的坏处,如果这个对象要串行化,可能会导致串行化失败。

    • 此种方式是匿名内部类的声明方式,所以引用中持有着外部类的引用。所以当串行化这个集合时外部类也会被不知不觉的串行化,当外部类没有实现serialize接口时,就会报错。

    • 上例中,其实是声明了一个继承自HashMap的子类。然而有些串行化方法,例如要通过Gson串行化为json,或者要串行化为xml时,类库中提供的方式,是无法串行化Hashset或者HashMap的子类的,从而导致串行化失败。

    解决办法,重新初始化为一个HashMap对象:

  • new HashMap(map);

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

    4、执行效率问题

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

    关于这个两种写法我这边笔记本上测试文艺写法、普通写法分别创建 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

  •    }

  • }

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

    从代码上看,a 为什么可以不先声明类型?你觉得 a、b、c 的值分别是多少?能说明理由么?TIP:如果你对这块机制不了解,建议试着反编译一下字节码文件。

    5.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());

  •    }

  • }

  • 5.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 - u6210u5458u53D8u91CFuFF1A")).append(this.h).toString());

  •  System.out.println((new StringBuilder("h - u5C40u90E8u53D8u91CF: ")).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;

  • }

  • }

  • 5.3 输出结果:

  • 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() {

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

    更多精彩推荐

    ? 微信第 1 行代码曝光!? 14 个实用的数据库设计技巧,好用到爆? 排名前 16 的 Java 工具类,哪个你没用过?? Spring事务的那些坑,这里都给你总结好了!? 这应该是全网最全的Git分支开发规范手册~

    最后,推荐给大家一个有趣有料的公众号:写代码的渣渣鹏,7年老程序员教你写bug,回复 面试或资源 送一你整套开发笔记 有惊喜哦

    感谢阅读,如果觉得本文对您有帮助,请给我点个,或者把本文的网址复制分享其它好友。
    本文网址:http://www.nulj.cn/k/48.html复制
    写博客不容易,服务器成本也很高,感谢谢支持。微信打赏红包

    发表评论

    李靖的博客

    大家好,我是李靖,不是封神榜的托塔天王李靖。我只是一个普通的上班族,1991年出生于湖南邵阳市。我喜欢看书/看电影/摄影/打手游。感谢你访问我的博客,我在博客中会经常分享我喜欢的文章。创建这个博客记录生活,让我学会很多知识也认识了很多朋友。
    关于我的更多资料>>

    支持博主

    写博客不容易,服务器成本也很高。您可以用微信给我发个小红包。疾风知劲草,板荡识诚臣。你的支持就是我的动力。谢谢!

    推荐阅读

    垃圾信息处理邮箱tousu117@163.com icp备案号 粤ICP备10034762号 互联网安全管理备案 不良信息举报平台 Copyright 2021 www.nulj.cn All Rights Reserved