通过Map.put(K,V)添加值时,是否必须通过Map.get(K)返回相同的实例?

时间:2011-02-17 12:33:35

标签: java collections map semantics

假设您有以下代码:

Map<Foo, Bar> map = new HashMap<Foo, Bar>();

Foo foo = new Foo();
Bar bar = new Bar();
map.put(foo, bar);

Bar barReturned = map.get(foo);

Java是否需要barReturned == bar?也就是说,Java 是否需要 barReturned 与<{1}}相同的实例?如果没有,预期会有什么语义?

Javadoc表示bar必须为真,但我不是百分百确定:

  

barReturned == bar

     

返回指定键映射到的值,如果此映射不包含键的映射,则返回V get(Object key)

     

更正式地说,如果此地图包含从关键null到值k的映射,使得v,则此方法返回(key==null ? k==null : key.equals(k));否则返回v。 (最多可以有一个这样的映射。)

     

如果此映射允许null值,则返回值null不会必须表示映射不包含该键的映射;地图也可能明确地将密钥映射到null。 containsKey操作可用于区分这两种情况。

     

参数:

     

null - 要返回其关联值的键

     

返回:

     

指定密钥映射的值 ,如果此地图不包含密钥映射,则为key

(强调我的)

编辑:我了解与标准库捆绑在一起的null实现遵循Map语义。我想知道的是,根据文档,此行为是否必需。例如,如果我编写自己的实现barReturned == bar的类,我是否还必须遵守这些语义?

7 个答案:

答案 0 :(得分:4)

如果你问你是否可以打破这种关系,我认为答案是肯定的。例如,如果您正在实现一个像持久性缓存一样的Map,那么如果不在一段时间内使用,则可以将特定值写入磁盘,然后再重新加载。在那种情况下你不会有参考平等,但那没关系。显然,你想要记录任何与标准行为的偏差,但我认为这不符合合理使用地图的范围。

答案 1 :(得分:1)

当然,它将是相同的实例。否则Map将毫无用处。

但是,唯一的例外是多线程应用程序,其他线程可以在您对putget的调用之间为同一个键添加任意值。


编辑正如Adam在评论中指出的,他有一些特殊情况,他的Map实现可以重新创建传入的对象并使用他们的副本。意义上的副本original.equals(copy)为真,original == copy为假。在这种情况下,我认为Map可以存储副本而不是原始副本。

答案 2 :(得分:1)

如果您将javadoc中使用的术语解释为地图中对象的引用,则意味着您始终需要返回与您相同的引用放入地图,你就会有==平等。然后,它变得更加成为文档中使用的术语值定义的问题。

答案 3 :(得分:1)

javadoc并没有明确地说明(或限制)你放在地图中的值在物理上等于放入它的那些值。 所以我敢说,没有100%的保证,它必须如此。 你可能会认为:

Map<Foo,Bar> map = new HashMap<Foo,Bar>();
Foo foo = new Foo();
Bar bar = new Bar();
map.put(foo, bar);

System.out.println( bar.equals( map.get(foo) ) ); // ==> Should be guaranteed
System.out.println( bar == map.get(foo) );        // ==> no guarantee

理论上,您可以创建在不同JVM上共享和同步的集群Map实现,在这种情况下,获取相同的物理实例是不可能的(或者至少非常困难)。

我的意见是,在您的实施中,您应该保证bar.equals( map.get(foo) ),但不是必须bar == map.get(foo)。 但是,只要您清楚地记录并说明上述两种情况都不能得到保证,您就可以使用Map实现做您想做的事情。 ;)

答案 4 :(得分:1)

如果您正在实现自己的Map版本,那么您不需要返回相同的对象实例。文档和语言中没有任何内容要求您的实现返回相同的对象实例。

当且仅当代码中存在某些库或其他部分时,您的代码可能会中断(无理由,但通常是合理的)假设返回值将是同一个对象实例。在这种情况下,好吧,只是不要将Map的实现传递给该代码。

Patient: Doctor, it hurts when I do this
Doctor: Well, don't do that

答案 5 :(得分:1)

注意它表示键映射到的“值”,而不是“引用”或“实例”。当你听到“值”时,它会建议Object.equals()语义。你无需担心。

答案 6 :(得分:0)

class Foo{

}
class Bar{

}
public class MapDemo {
    public static void main(String[] args) {
        Map<Foo,Bar> map = new HashMap<Foo,Bar>();
        Foo foo = new Foo();
        Bar bar = new Bar();
        map.put(foo, bar);
        System.out.println(bar == map.get(foo));
    }
}

返回 true

HashMap实现了Map,但没有任何内容可以写出来。

  /**
  137        * Returns the value of the mapping with the specified key.
  138        * 
  139        * @param key
  140        *            the key.
  141        * @return the value of the mapping with the specified key, or {@code null}
  142        *         if no mapping for the specified key is found.
  143        */

但几乎每个实现都这样做,我的意思是它保持相同的Object