先看一個長長的代碼,其實很簡單,就是使用不同的方法迭代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的特點和缺點,方便的語法屏蔽了底層的實現,卻不能讓人瞭解幕後究竟是什麼。
也許這個問題是很簡單,是顯而易見的,但是我就是不到黃河心不死,非要看個究竟。