[讨论] 求教个问题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);

对对,我复制粘贴错了。抱歉啊~
在上面直接修掉
Global site tag (gtag.js) - Google Analytics