您的位置:首页 > 其它

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的特点和缺点,方便的语法屏蔽了底层的实现,却不能让人了解幕后究竟是什么。也许这个问题是很简单,是显而易见的,但是我就是不到黄河心不死,非要看个究竟。本文出自 “终南山下” 博客,转载请与作者联系!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: