2019独角兽企业重金招聘Python工程师标准>>>

如果你接触过不同的语言,从语法和代码层面来说,Java 是一种不折不扣的“臃肿、啰嗦”的语言,从另一方面来说这种臃肿和啰嗦也体现了它严谨的一面,作为适合构建大型、复杂项目的理由之一。

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();}
}

output:

Static block called:静态块被调用
Instance initializer called:实例初始化被调用
Constructor called:构造器被调用
=======================
Instance initializer called:实例初始化被调用
Constructor called:构造器被调用

Note:关于 static 的作用与用法如果不了解,请参考:

http://my.oschina.net/leejun2005/blog/193439#OSC_h3_1  为什么 main 方法是 public static void?

http://my.oschina.net/leejun2005/blog/144349#OSC_h3_2  设计模式之:聊聊 java 中的单例模式(Singleton)

也就是说第一层括弧实际是定义了一个匿名内部类 (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.javapackage pvuv.zhaopin;import java.util.HashMap;// Referenced classes of package pvuv.zhaopin:
//            Testclass 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<String> list = new ArrayList<String>();
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.javaimport 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

7、Refer:

[1]Double Brace Initialization In Java!  http://viralpatel.net/blogs/double-brace-initialization-in-java/

[2]Double Brace Initialization Idiom and its Drawbacks  http://java.dzone.com/articles/double-brace-initialization

[3]Hidden Features of Java  http://stackoverflow.com/questions/15496/hidden-features-of-java

[4]Java 大括号语法糖  http://my.oschina.net/trydofor/blog/79222

[5]Java 7 的新特性:http://code.joejag.com/2009/new-language-features-in-java-7/

http://www.iteye.com/news/11490-java-7?page=5

[6]java map双括号初始化方式的问题  http://blog.csdn.net/liubo2012/article/details/8591956

[7]Efficiency of Java “Double Brace Initialization”?  http://stackoverflow.com/questions/924285/efficiency-of-java-double-brace-initialization

[8]初始化代码块中的变量,为什么可以不需要声明数据类型?  http://segmentfault.com/q/1010000000580267

[9]Java 8速查表  http://www.importnew.com/12156.html

[10] Java 8新特性之旅:使用Stream API处理集合    http://www.importnew.com/16545.html

转载于:https://my.oschina.net/leejun2005/blog/282783

聊聊 Java 中 HashMap 初始化的另一种方式相关推荐

  1. java string hash变量_聊聊 Java 中 HashMap 初始化的另一种方式

    如果你接触过不同的语言,从语法和代码层面来说,Java 是一种不折不扣的"臃肿.啰嗦"的语言,从另一方面来说这种臃肿和啰嗦也体现了它严谨的一面,作为适合构建大型.复杂项目的理由之一 ...

  2. Java中遍历Map集合的5种方式总结

    这篇文章主要给大家介绍了关于Java中遍历Map集合的5种方式,文中通过示例代码介绍的非常详细,对大家的学习或者工作具有一定的参考学习价值. 方式一 通过Map.keySet使用iterator遍历 ...

  3. java 判断数组已经存满_详解Java中数组判断元素存在几种方式比较

    1. 通过将数组转换成List,然后使用List中的contains进行判断其是否存在 public static boolean useList(String[] arr,String contai ...

  4. Java中线程的创建有两种方式

    Java中继承thread类与实现Runnable接口的区别 Java中线程的创建有两种方式: 1.  通过继承Thread类,重写Thread的run()方法,将线程运行的逻辑放在其中 2.  通过 ...

  5. java中读取properties文件内容五种方式

    一.背景 最近,在项目开发的过程中,遇到需要在properties文件中定义一些自定义的变量,以供java程序动态的读取,修改变量,不再需要修改代码的问题.就借此机会把Spring+SpringMVC ...

  6. java中遍历Map对象的四种方式

    import java.util.HashMap; import java.util.Map; /**  * 遍历Map对象 * @author xiaosongsong:  * @CreateTim ...

  7. cmd 将文件夹下文件剪切到另外一个文件_总结java中文件拷贝剪切的5种方式-JAVA IO基础总结第五篇...

    本文是Java IO总结系列篇的第5篇,前篇的访问地址如下: 总结java中创建并写文件的5种方式-JAVA IO基础总结第一篇 总结java从文件中读取数据的6种方法-JAVA IO基础总结第二篇 ...

  8. java 文件 剪切_总结java中文件拷贝剪切的5种方式-JAVA IO基础总结第五篇

    本文是Java IO总结系列篇的第5篇,前篇的访问地址如下: 很多朋友在看我的<java IO总结系列>之前觉得创建文件.文件夹删除文件这些基础操作真的是太简单了.但看了我的文章之后,有小 ...

  9. java中打印输出数组内容的三种方式

    今天输出数组遇到问题,学习一下打印输出数组内容的几种方式 错误示范:System.out.println(array); //这样输出的是数组的首地址,而不能打印出数组数据.(唉,我开始就是这么写的. ...

  10. Java实现剪切MP3格式的文件_总结java中文件拷贝剪切的5种方式-JAVA IO基础总结第五篇...

    本文是Java IO总结系列篇的第5篇,前篇的访问地址如下: 很多朋友在看我的<java IO总结系列>之前觉得创建文件.文件夹删除文件这些基础操作真的是太简单了.但看了我的文章之后,有小 ...

最新文章

  1. 50 jQuery拷贝对象与多库共存
  2. 如何使用小的显存训练大的batch_size
  3. python2 x与python3 x_python2.x 与 python3.x的不同
  4. 涂鸦板练习(20200214)
  5. 代码编辑器Sublime Text 3 免费使用方法与简体中文汉化包下载
  6. 关于模拟量转光纤的不同实现方法与区别
  7. DCB(串口的DCB结构)
  8. 【MySQL性能优化的21个最佳实践】
  9. 最棒的SpringCloud微服务后台管理系统
  10. MapGuide Open Source 2.2发布
  11. 网页打开显示无法访问
  12. 10分钟教你生成超高逼格微信朋友圈
  13. 《Android Studio从入门到精通》读后感,电子书PDF百度网盘下载
  14. NFT艺术品交易平台
  15. 米的换算单位和公式_小学数学单位换算公式(附练习题)
  16. 常用元器件使用方法26:LoRa芯片SX1268的使用方法(硬件连接)
  17. 清华大学王观堂先生纪念碑铭----陈寅恪
  18. 开题报告:基于java新冠疫苗在线预约系统 毕业设计论文开题报告模板
  19. 安卓学习 Day18:利用单选按钮实现底部导航条
  20. 实现数智内控,数据分析创造价值——辽宁烟草智能风险体检系统

热门文章

  1. Scikit-learn:分类classification
  2. python模块导入及属性:import
  3. it技术交流平台_IT协会向你招手了,不了解了解?
  4. python3.5安装pip_python详细安装pip教程
  5. python同时输出名字和时间,python中的日期和时间格式化输出
  6. java用信号量写理发师_课内资源 - 基于Java实现的生产者与消费者问题、读者写者问题、哲学家进餐问题、理发师睡觉问题、医生看病问题...
  7. JSP — 小项目【实现登录界面】
  8. C语言 — 数据类型,基本整型所占字节数
  9. C++ 构造函数中抛出异常
  10. linux 下配置jdk