foreach的一个“奇怪”现象——实现原理分析
2015-04-08 14:38
441 查看
先看一个长长的代码,其实很简单,就是使用不同的方法迭代Map,对值进行修改,只要遇到foreach就发现赋值看似成功,实则失败。就想搞清楚为什么,不想直接从搜索引擎搜来别人的总结好的背下来。1、问题的引出1.1、测试的源代码
1: public static void main(String[] args) {
2: Map<String, String[]> map = new HashMap<String, String[]>();
3: map.put("key1", new String[] { "值" });
4: System.out.println();
5: System.out.println("#######第一次#######");
6: System.out.println("#######第一种迭代#######");
7: String[] values = null;
8: for (String str : map.keySet()) {
9: values = map.get(str);
10: System.out.println(values.hashCode());
11: for (int i = 0; i < values.length; i++) {
12: System.out.println(values[i].hashCode());
13: }
14: }
15: System.out.println();
16: System.out.println("#######第二次#######");
17: System.out.println("#######第二种迭代#######");
18: for (Entry<String, String[]> entry : map.entrySet()) {
19: values = entry.getValue();
20: System.out.println(values.hashCode());
21: for (String string : values) {
22: System.out.println(string.hashCode());
23: }
24: }
25: System.out.println();
26: System.out.println("#######第三次#######");
27: System.out.println("#######第一种迭代#######");
28: values = null;
29: for (String str : map.keySet()) {
30: values = map.get(str);
31: System.out.println(values.hashCode());
32: for (int i = 0; i < values.length; i++) {
33: System.out.println("旧:" + values[i].hashCode());
34: values[i] = values[i] + "_1";
35: System.out.println("新:" + values[i].hashCode());
36: }
37: }
38: System.out.println("#######第二种迭代#######");
39: for (Entry<String, String[]> entry : map.entrySet()) {
40: values = entry.getValue();
41: System.out.println(values.hashCode());
42: for (String string : values) {
43: System.out.println(string.hashCode() + "-->" + string);
44: }
45: }
46: System.out.println();
47: System.out.println("#######第四次#######");
48: System.out.println("#######第二种迭代#######");
49: values = null;
50: for (Entry<String, String[]> entry : map.entrySet()) {
51: values = entry.getValue();
52: System.out.println(values.hashCode());
53: for (String string : values) {
54: System.out.println("旧:" + string.hashCode());
55: string = string + "_2";
56: System.out.println("新:" + string.hashCode());
57: }
58: }
59: System.out.println("#######第一种迭代#######");
60: for (String str : map.keySet()) {
61: values = map.get(str);
62: System.out.println(values.hashCode());
63: for (int i = 0; i < values.length; i++) {
64: System.out.println(values[i].hashCode() + "-->" + values[i]);
65: }
66: }
67: System.out.println("#######第二种迭代#######");
68: for (Entry<String, String[]> entry : map.entrySet()) {
69: values = entry.getValue();
70: System.out.println(values.hashCode());
71: for (String string : values) {
72: System.out.println(string.hashCode() + "-->" + string);
73: }
74: }
75: System.out.println("值没有改变,还是指向原来的字符串位置");
76: System.out.println();
77: System.out.println("#######第五次#######");
78: System.out.println("#######第二种迭代#######");
79: values = null;
80: for (Entry<String, String[]> entry : map.entrySet()) {
81: values = entry.getValue();
82: System.out.println(values.hashCode());
83: for (int i = 0; i < values.length; i++) {
84: System.out.println("旧:" + values[i].hashCode());
85: values[i] = values[i] + "_3";
86: System.out.println("新:" + values[i].hashCode());
87: }
88: }
89: System.out.println("#######第一种迭代#######");
90: for (String str : map.keySet()) {
91: values = map.get(str);
92: System.out.println(values.hashCode());
93: for (int i = 0; i < values.length; i++) {
94: System.out.println(values[i].hashCode() + "-->" + values[i]);
95: }
96: }
97: System.out.println("#######第二种迭代#######");
98: for (Entry<String, String[]> entry : map.entrySet()) {
99: values = entry.getValue();
100: System.out.println(values.hashCode());
101: for (String string : values) {
102: System.out.println(string.hashCode() + "-->" + string);
103: }
104: }
105: System.out.println("实验结果就是:使用简单for遍历来赋值成功,使用高级for循环取出赋值失败");
106: }1.2、运行结果
#######第一次#######
#######第一种迭代#######
1951510703
20540
#######第二次#######
#######第二种迭代#######
1951510703
20540
#######第三次#######
#######第一种迭代#######
1951510703
旧:20540
新:19741934
#######第二种迭代#######
1951510703
19741934-->值_1
#######第四次#######
#######第二种迭代#######
1951510703
旧:19741934
新:1792132385
#######第一种迭代#######
1951510703
19741934-->值_1
#######第二种迭代#######
1951510703
19741934-->值_1
值没有改变,还是指向原来的字符串位置
#######第五次#######
#######第二种迭代#######
1951510703
旧:19741934
新:1792132386
#######第一种迭代#######
1951510703
1792132386-->值_1_3
#######第二种迭代#######
1951510703
1792132386-->值_1_3
实验结果就是:使用简单for遍历来赋值成功,使用高级for循环取出赋值失败实验结果就是:使用简单for遍历来赋值成功,使用高级for循环取出赋值失败二、问题为什么它是只读的呢,编译器做了什么?高级for做了什么?没有找到合适的理由,先认为高级for它是只读的吧。三、问题的解决熬了半宿,只是有一个猜测,那么怎么证实呢?写一个最最简单的程序,使用foreach,先看字节码。
public class TestFor {
public static void main(String[] args) {
int a[] = new int[] { 1, 2, 3 };
for (int i : a) {
System.out.println(i);
}
}
}
程序简单的,都不用加注释了。看字节码?javap看了半天没有很乱的,怎么办?换工具,都是32位的,64位的墙外边不好拿到,怎么办?安装虚拟机,装32位系统,非要看看里面是什么。用工具打开一看那是一个乱啊,引用常量表,那个goto乱啊。无意中看到了反编译的结果,哈哈,我要的东西在里面。这正是我要的,证实了我的想法,这就是一个常引用啊,这里是常量赋值,就是只读的。好,那再看看普通for循环编译成字节码,体积较上一个代码还小了些。再反编译那么我们来看Java中的引用类型做了什么。上源码看反编译果然是一个常引用。四、总结高级For在JDK 5.0开始引入,用其迭代代码简洁,但是要注意它取出的值是一个常变量,所以高级For循环可以用来遍历查询,不可修改当前取回的元素本身。我在学习Java的时候,没有看到高级For的特点和缺点,方便的语法屏蔽了底层的实现,却不能让人了解幕后究竟是什么。也许这个问题是很简单,是显而易见的,但是我就是不到黄河心不死,非要看个究竟。本文出自 “终南山下” 博客,转载请与作者联系!
相关文章推荐
- Struts2原理分析,自己实现一个struts2
- HashMap实现原理分析及简单实现一个HashMap
- 一个小语言的词法分析程序原理及其实现(1)
- 一个小语言的词法分析程序原理及其实现(2)
- 利用JAVA的动态属性之反射原理实现一个简单AOP容器 - AOP的实现原理分析
- 一个小语言的词法分析程序原理及其实现(2)
- 也谈WEB打印(二):简单的分析一下IE的打印原理并实现简单的打印和预览
- ASP文件上传原理分析及实现实例
- IronPython for ASP.NET 的原理分析(一):如何在 CLR 类型上实现动态性
- IronPython for ASP.NET 的原理分析(一):如何在 CLR 类型上实现动态性
- [原创] 用Delphi实现控制IE窗体的大小——IESizer程序原理分析 (2003-12)
- 通过AEC解读WF的核心原理(五)实现一个从下向上执行的顺序容器
- 从实现一个制造Flash Memory的控制系统中学习系统分析及.Net(一)
- 用进程注入来实现一个壳(原理)
- 最近由于要用到分隔条,找到了一篇讲原理的, 实现了一个。可以双击缩到左边,可以左右拖动
- 一个算符文法分析的实现
- 一个基于括号匹配原理的汉语句法分析器
- Hibernate实现分页查询的原理分析
- Hibernate实现分页查询的原理分析