这个问题是由 奇怪的 HashMap.put() 行为

我想我明白为什么 Map<K,V>.put 需要一个 KMap<K,V>.get 需要一个 Object, ,似乎不这样做会破坏太多现有代码。

现在我们陷入了一个非常容易出错的场景:

java.util.HashMap<Long, String> m = new java.util.HashMap<Long, String>();
m.put(5L,"Five"); // compiler barfs on m.put(5, "Five")
m.contains(5); // no complains from compiler, but returns false

难道不能通过返回 true 来解决这个问题吗? Long 价值在 int 范围和值相等吗?

有帮助吗?

解决方案

下面是从Long.java源

public boolean equals(Object obj) {
    if (obj instanceof Long) {
        return value == ((Long)obj).longValue();
    }
    return false;
}

即。它必须是一个长型相等。我认为之间的主要区别在于:

long l = 42L
int i = 42;
l == i

和上方的例子是与图元的int值的隐式加宽可以发生,但是与对象类型存在用于从整数隐式转换为无长规则。

还检查了爪哇谜题时,它具有类似于这个有很多的实施例。

其他提示

一般来说,虽然合同中没有严格明示 等于(), ,对象不应该认为自己等于另一个不属于同一类的对象(即使它是子类)。考虑对称属性 - 如果 a.equals(b) 为 true,则 b.equals(a) 也必须为 true。

让我们有两个对象, foo 班级的 Super, , 和 bar 班级的 Sub, ,这延伸了 Super. 。现在考虑实施 equals() 在 Super 中,特别是当它被称为 foo.equals(bar). 。Foo 只知道 bar 是强类型的 Object, ,因此为了获得准确的比较,需要检查它是否是 Super 的实例,如果不是则返回 false。是的,所以这部分很好。它现在比较所有实例字段等。(或者无论实际的比较实现是什么),并发现它们相等。到目前为止,一切都很好。

然而,根据合约,只有当它知道 bar.equals(foo) 也将返回 true 时,它​​才能返回 true。由于 bar 可以是 Super 的任何子类,因此不清楚 equals() 方法是否将被重写(如果可能的话)。因此,为了确保你的实现是正确的,你需要对称地编写它并确保两个对象是同一个类。

更根本的是,不同类的对象实际上不能被认为是相等的 - 因为在这种情况下,只有其中一个可以插入到一个对象中。 HashSet<Sub>, , 例如。

是的,但这一切都取决于比较算法以及转换的程度。例如,当你尝试时你希望发生什么 m.Contains("5")?或者如果你向它传递一个以 5 作为第一个元素的数组?简单来说,就是“类型不同,按键不同”。

然后用一个集合 object 作为钥匙。如果你想发生什么 put A 5L, ,然后尝试得到 5, "5", ...?如果你怎么办 put A 5L 和一个 5 和一个 "5" 并且您想检查 5F?

由于它是一个通用集合(或模板化集合,或任何您想称呼它的名称),因此它必须检查并对某些值类型进行一些特殊比较。如果 K 是 int 然后检查传递的对象是否是 long, short, float, double, ,...,然后转换并比较。如果 K 是 float 然后检查传递的对象是否是...

你明白了。

然而,另一种实现可能是在类型不匹配时抛出异常,而我经常希望如此。

您问题似乎在其表面上合理,但它会的一般约定为equals()方法违规行为,如果没有合同,为两种不同类型的返回true。

Java语言设计的一部分是为对象从来没有隐式转换到其他类型的,不像C ++。这使得Java小,简单的语言的一部分。的C ++的复杂性合理一部分来自隐式转换以及它们与其它特征的交互。

此外,Java有原语和对象之间的尖锐和可见二分法。这是从该差分的封面作为优化下隐藏着其他语言不同。这意味着,你不能指望龙和整数表现得像long和int。

图书馆可以编写代码来隐藏这些差异,但可以通过使编程环境不太一致的实际危害。

所以,你的代码应该是......

java.util.HashMap<Long, String> m = new java.util.HashMap<Long, String>();
m.put(5L, "Five"); // compiler barfs on m.put(5, "Five")
System.out.println(m.containsKey(5L)); // true

您忘记了,Java是自动装箱代码,所以上面的代码将被equivelenet到

java.util.HashMap<Long, String> m = new java.util.HashMap<Long, String>();
m.put(new Long(5L), "Five"); // compiler barfs on m.put(5, "Five")
System.out.println(m.containsKey(new Long(5))); // true
System.out.println(m.containsKey(new Long(5L))); // true

所以你的问题的一部分是自动装箱。另一部分是,你有不同类型作为其他海报说明。

其他答案充分解释为什么失败了,但他们没有解决如何编写代码,误差不解决此问题容易发生。不必记住添加类型强制转换(没有编译器帮助),后缀原语与L-等等仅仅是不能接受的IMHO。

我强烈建议使用收藏的GNU宝藏库,当你有原语(和其他许多情况下)。例如,有一个interally存储东西的原始渴望一个TLongLongHashMap。其结果是,你永远不与拳击/拆箱结束了,从不与意外的行为结束了:

TLongLongHashMap map = new TLongLongHashMap();
map.put(1L, 45L);
map.containsKey(1); // returns true, 1 gets promoted to long from int by compiler
int x = map.get(1); // Helpful compiler error. x is not a long
int x = (int)map.get(1); // OK. cast reassures compiler that you know
long x = map.get(1); // Better.

等。没有必要要获得类型的权利,并且编译器给你一个错误(您可以纠正或重写),如果你做一些愚蠢(尝试存储很长的一个int)。

自动浇铸的规则意味着比较正常工作,以及:

if(map.get(1) == 45) // 1 promoted to long, 45 promoted to long...all is well

作为奖励,开销存储器和运行时性能要好得多。

许可以下: CC-BY-SA归因
不隶属于 StackOverflow
scroll top