[讨论] 求教个问题Java对象存储
lucane
2011-07-11
java.lang.Float.equals方法的说明的最后一句:This definition allows hash tables to operate properly.
这里的hash tables怎么理解来着? 是不是指的Java的对象在内存中存放在某个hash table结构中的 比如一个对象Float f1 = new Float(Float.NaN); 另一个对象Float f2 = new Float(Float.NaN);他们在内存中是如何分布的? 谢谢各位大大 |
|
ngn9999
2011-07-11
跟Java对象的存储完全没关系,是跟hash table(HashMap/Hashtable)的实现方式有关。
hash table要先用hashCode()来找到一个位置,然后再从这个位置开始向后逐个用equals()来比较,找到完全匹配的元素。 如果一个Float对象包装的值是Float.NaN,并且它被作为key插入到了一个hash table里,那么如果new Float(Float.NaN).equals(new Float(Float.NaN)) == false,这个key就拿不出来了。0.0f同理。所以Float的equals()才需要特殊规定。 |
|
IcyFenix
2011-07-11
引用 另一个对象Float f2 = new Float(Float.NaN);他们在内存中是如何分布的? JVMS没有定义memory layout,而且说明这部分应该由具体vm实现来自由发挥。所以讨论内存如何布局的应当先确定vm实现,假定在32bit版hotspot中。 “Float f2”这部分,反映为栈帧的一个局部变量。 “new Float(Float.NaN)”这部分,反应为堆中一个结构化内存,其中具体为4字节的MarkWord(存储对象hashcode、gc age以及一些锁相关信息)、4字节的KlassOop(指向类元数据的指针)、一个float类型的field(Float类里面定义的),也是4字节,另外为了保证对象地址是8的倍数,再填了4字节的空数据进去。 |
|
lucane
2011-07-11
额,不理解
Float.NaN作为hash table的key,new Float(Float.NaN)作为value 你的意思是说new Float(Float.NaN).equals(new Float(Float.NaN))这个为false才有可能得出有两个不同的key,如果new Float(Float.NaN).equals(new Float(Float.NaN))为true只能推导出一个key出来? 不知道你能不能明白我的意思 |
|
RednaxelaFX
2011-07-11
简单啊这个。确实跟对象布局完全没关系。
楼主做下这个实验就应该能理解了: 先从JDK里的src.zip把java.lang.Float的源码拿出来。把里面的equals()方法的实现从原来的: public boolean equals(Object obj) { return (obj instanceof Float) && (floatToIntBits(((Float)obj).value) == floatToIntBits(value)); } 改为: public boolean equals(Object obj) { return (obj instanceof Float) && ((((Float)obj).value) == value); } 把修改过的Java源码文件存起来。 修改后的版本会使得new Float(Float.NaN).equals(new Float(Float.NaN)) == false,而修改前的则是new Float(Float.NaN).equals(new Float(Float.NaN)) == true 然后写这样一个测试程序: import java.util.HashMap; import java.util.Map; public class Test { public static void main(String[] args) { Map<Float, String> map = new HashMap<Float, String>(); map.put(Float.NaN, "Some string"); String value = map.get(Float.NaN); System.out.println(value); // 得到"Some string"还是null呢? } } 依照下面的目录结构来保存前面说的Float.java和Test.java两文件,编译它们然后做实验: D:\fx\experiment\test_float_equals>tree /f 卷 DATAPART1 的文件夹 PATH 列表 卷序列号为 3684-D7AA D:. │ Test.java │ └─java └─lang Float.java D:\fx\experiment\test_float_equals>java -version java version "1.6.0_23" Java(TM) SE Runtime Environment (build 1.6.0_23-b05) Java HotSpot(TM) 64-Bit Server VM (build 19.0-b09, mixed mode) D:\fx\experiment\test_float_equals>javac -g Test.java .\java\lang\Float.java:10: 警告:sun.misc.FloatingDecimal 是 Sun 的专用 API,可能会在未来版本中删除 import sun.misc.FloatingDecimal; ^ .\java\lang\Float.java:11: 警告:sun.misc.FpUtils 是 Sun 的专用 API,可能会在未来版本中删除 import sun.misc.FpUtils; ^ .\java\lang\Float.java:12: 警告:sun.misc.FloatConsts 是 Sun 的专用 API,可能会在未来版本中删除 import sun.misc.FloatConsts; ^ .\java\lang\Float.java:13: 警告:sun.misc.DoubleConsts 是 Sun 的专用 API,可能会在未来版本中删除 import sun.misc.DoubleConsts; ^ .\java\lang\Float.java:182: 警告:sun.misc.FloatingDecimal 是 Sun 的专用 API,可能会在未来版本中删除 return new FloatingDecimal(f).toJavaFormatString(); ^ .\java\lang\Float.java:260: 警告:sun.misc.FloatConsts 是 Sun 的专用 API,可能会在未来版本中删除 if (Math.abs(f) < FloatConsts.MIN_NORMAL ^ .\java\lang\Float.java:267: 警告:sun.misc.DoubleConsts 是 Sun 的专用 API,可能会在未来版本中删除 DoubleConsts.MIN_EXPONENT- ^ .\java\lang\Float.java:268: 警告:sun.misc.FloatConsts 是 Sun 的专用 API,可能会在未来版本中删除 FloatConsts.MIN_EXPONENT)); ^ .\java\lang\Float.java:265: 警告:sun.misc.FpUtils 是 Sun 的专用 API,可能会在未来版本中删除 String s = Double.toHexString(FpUtils.scalb((double)f, ^ .\java\lang\Float.java:388: 警告:sun.misc.FloatingDecimal 是 Sun 的专用 API,可能会在未来版本中删除 return new Float(FloatingDecimal.readJavaFormatString(s).floatValue()); ^ .\java\lang\Float.java:422: 警告:sun.misc.FloatingDecimal 是 Sun 的专用 API,可能会在未来版本中删除 return FloatingDecimal.readJavaFormatString(s).floatValue(); ^ .\java\lang\Float.java:683: 警告:sun.misc.FloatConsts 是 Sun 的专用 API,可能会在未来版本中删除 if ( ((result & FloatConsts.EXP_BIT_MASK) == ^ .\java\lang\Float.java:684: 警告:sun.misc.FloatConsts 是 Sun 的专用 API,可能会在未来版本中删除 FloatConsts.EXP_BIT_MASK) && ^ .\java\lang\Float.java:685: 警告:sun.misc.FloatConsts 是 Sun 的专用 API,可能会在未来版本中删除 (result & FloatConsts.SIGNIF_BIT_MASK) != 0) ^ 注意:.\java\lang\Float.java 使用了未经检查或不安全的操作。 注意:要了解详细信息,请使用 -Xlint:unchecked 重新编译。 14 警告 D:\fx\experiment\test_float_equals>tree /f 卷 DATAPART1 的文件夹 PATH 列表 卷序列号为 3684-D7AA D:. │ Test.class │ Test.java │ └─java └─lang Float.class Float.java D:\fx\experiment\test_float_equals>java Test Some string D:\fx\experiment\test_float_equals>java -Xbootclasspath/p:. Test null 看到了么,直接运行java Test的时候,使用的java.lang.Float的实现是原本JDK里的实现,这个时候可以从一个HashMap取出key为Float.NaN的包装对象对应的value。 而第二次实验加上了-Xbootclasspath/p:.参数,则使用在当前目录里已经修改过的那个Float实现,运行的结果就变成了无法从HashMap取出key为Float.NaN的包装对象对应的value了。 |
|
lucane
2011-07-12
@RednaxelaFX
@ngn9999 是我自己搞糊涂了,我一直认为Float的实现不需要参照或者依赖hash tables 看了你们的回答我明白了 public V get(Object key) { if (key == null) return getForNullKey(); int hash = hash(key.hashCode()); for (Entry<K,V> e = table[indexFor(hash, table.length)]; e != null; e = e.next) { Object k; if (e.hash == hash && ((k = e.key) == key || key.equals(k))) return e.value; } return null; } equals的这么实现确实是为了hash tables(HashMap/Hashtable)的正确实现 @IcyFenix 你讲解的通俗易懂,不愧为写书之人,虽然我真不知道它在内存中是如何分布的,哈哈 谢谢你们 这个帖子要不要移走啊,貌似标题严重误导人。。。 |
|
conel
2011-07-19
修改的Float类源码中:这一句:
return (obj instanceof Float) && ((((Float)obj).value) == floatToIntBits(value)); 是不是应改为: return (obj instanceof Float) && (((Float)obj).value) == value); |
|
RednaxelaFX
2011-07-19
conel 写道 修改的Float类源码中:这一句:
return (obj instanceof Float) && ((((Float)obj).value) == floatToIntBits(value)); 是不是应改为: return (obj instanceof Float) && (((Float)obj).value) == value); 对对,我复制粘贴错了。抱歉啊~ 在上面直接修掉 |