先看一个长长的代码,其实很简单,就是使用不同的方法迭代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的特点和缺点,方便的语法屏蔽了底层的实现,却不能让人了解幕后究竟是什么。
也许这个问题是很简单,是显而易见的,但是我就是不到黄河心不死,非要看个究竟。
本文出自 “终南山下” 博客,转载请与作者联系!
原文地址:http://me2xp.blog.51cto.com/6716920/1630007