码迷,mamicode.com
首页 > 其他好文 > 详细

foreach的一个“奇怪”现象——实现原理分析

时间:2015-04-08 16:44:53      阅读:220      评论:0      收藏:0      [点我收藏+]

标签:java foreach 原理

先看一个长长的代码,其实很简单,就是使用不同的方法迭代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的特点和缺点,方便的语法屏蔽了底层的实现,却不能让人了解幕后究竟是什么。

也许这个问题是很简单,是显而易见的,但是我就是不到黄河心不死,非要看个究竟。

本文出自 “终南山下” 博客,转载请与作者联系!

foreach的一个“奇怪”现象——实现原理分析

标签:java foreach 原理

原文地址:http://me2xp.blog.51cto.com/6716920/1630007

(0)
(0)
   
举报
评论 一句话评论(0
登录后才能评论!
© 2014 mamicode.com 版权所有  联系我们:gaon5@hotmail.com
迷上了代码!