浅平等和深平等有什么区别?这在缓存中如何应用?

11

我在笔记中发现了以下内容,但我无法理解它:

原始类型包装类为有限数量的值实现缓存。
这保证了有限数量的深度相等的包装对象也是浅度相等的: 如果o1.equals(o2),则o1 == o2
例如:new Integer(0) == new Integer(0)
总体而言,这种方法并不总是奏效。
例如:new Integer(666) == new Integer(666)
可能不成立。
缓存的原因是可以节省内存。
通常情况下,缓存适用于“小”原始值。

我不明白这意味着什么,或者深度(.equals())和浅度(==)相等之间的区别。我知道在实践中,必须使用.equals()处理对象,而对于整数值,则使用==,但是这一切的实际原因我不明白。

我认为,根据名称,浅层可能只检查两个值是否具有相同的类型和名称,而深层则检查两个变量是否指向同一个对象?然而,我不明白缓存如何发挥作用,或者为什么会有用。


1
你的笔记完全是错的。new Integer(0) == new Integer(0)永远不会成立,new Integer(666) == new Integer(666)也不会成立。我建议你向你的教授询问他的意思,因为如果他期望在考试中出现这种情况,那么他对Java的了解很少。 - Mark Peters
5个回答

9
当您使用==进行比较时,您正在比较引用的相等性。这意味着您在说“内存中的地址对于两个对象是否相同?”
当您使用.equals()进行比较时,您正在比较对象本身的相等性。这意味着您在说“这两个对象是否认为它们自己相等?”
所给出的例子很差。JLS规定的仅有缓存的是.valueOf()方法。构造函数不会被缓存。
此外,JLS仅指定了必须缓存[-128:127]之间的最小值。JVM实现可以选择缓存更多。这意味着在某些机器上,Integer.valueOf(500) == Integer.valueOf(500)可能是false,但在其他机器上可能是true
class biziclop {

    public static void main(String[] args) {
        System.out.println(new Integer(5) == new Integer(5));
        System.out.println(new Integer(500) == new Integer(500));

        System.out.println(Integer.valueOf(5) == Integer.valueOf(5));
        System.out.println(Integer.valueOf(500) == Integer.valueOf(500));
    }
}

结果为:

C:\Documents and Settings\glow\My Documents>java biziclop
false
false
true
false

C:\Documents and Settings\glow\My Documents>

这里有一个更详细的答案(评论也很有价值!):为什么人们还在Java中使用原始类型?


你好, 下面这两行代码的输出结果有什么不同呢? System.out.println(Integer.valueOf(5) == Integer.valueOf(5)); System.out.println(Integer.valueOf(500) == Integer.valueOf(500)); - Bhavuk Mathur
1
这个答案的整个意思就是:5 太小了,JVM 会缓存它,而 500 不会。虽然在某些罕见的机器上可能会相同(都为 true),但在大多数机器上,5 将被缓存,因此返回相同的 Integer 对象,而 500 不会,所以 valueOf 将创建新的 Integer。 - corsiKa

9

实际上,浅层/深层解剖与==/等于解剖是不同的:

  1. == compares for object identity, that is you checking whether operands are the same in fact (two references to the same area of memory), whereas equals compares for object equivalence, that is "logical" value of two, possibly not identical objects, is the same. If for two objects

    a == b
    

    then it's true that

    a.equals(b) // if a != null
    

    , but opposite isn't true in all cases.

  2. shallow/deep distinction makes sense only for equals comparison. Shallow means that you compare only immediate contents of two objects to find whether they "equal" in your sense, whereas deep means that you compare contents of your objects recursively until all you need to compare is primitive fields. If you define equals method of your objects as sequence of calls to equals on instance fields of these objects, you use deep comparison. If you define equals using == operator to compare compound types, such as Strings, then you use shallow comparison -- and that's incorrect in Java.
所有这些的 morale 是,除非你认为它们只有在相同的情况下才相等,否则你不能使用 == 来比较两个复合对象。

你说得没错,不过讲师好像是用“浅层”指代引用标识,“深层”则表示等价关系。这个逻辑的确很有道理。 - Ernest Friedman-Hill

2
你所称的“浅等”其实是指身份:如果两个引用(即对象)是完全相同的实例,则它们的身份相同。如果你知道其他语言中指针的概念,那么可以将身份比作指针相等。
你所称的“深等”其实是指相等性:如果a.equals(b)返回true(希望反之亦然),则两个对象a和b是相等的。相等性的正确性强烈依赖于equals方法的实现方式。请参阅Object类的Javadoc以获取更多详细信息。

2

首先:new Integer(0) == new Integer(0) 永远不会评估为 true,因为new总是创建一个新的对象,绕过可能存在的自动装箱缓存机制。

你可能听说过的是自动装箱(即在必要时将基本数据类型转换为对应的包装类)。 自动装箱使用一个也可以通过包装类的valueOf()方法访问的机制。 换句话说:将int自动装箱为Integer与调用Integer.valueOf(int)的操作基本相同。

Integer.valueOf(0) == Integer.valueOf(0) 将评估为 true,因为常见的值(即绝对值较低的值)会被缓存。当您连续调用valueOf(0)时,您将获得相同的Integer对象。但是对于较高的值(例如您示例中的666),这并不一定成立。


1

equals() 测试两个对象是否本质相同,但它可能对于两个不同的对象返回 true;例如,两个不同的回形针是"equals"的。对于引用类型,"=="测试两个引用是否引用同一个对象——即回形针只等于它自己。"==" 测试身份,而 equals测试等价性

你可以拥有两个具有 0 的不同 Integer 对象(它们是equals());缓存意味着在可能的情况下保存对象并重复使用它们。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接