简体   繁体   English

如何使用接口作为地图的密钥

[英]How to use an Interface as Map’s Key

I am looking for help on the subject how to use an Interface as Maps Key. 我正在寻找有关如何使用接口作为地图密钥的主题的帮助。 I tried to implement a solution, and get no compiletime errors but runtime errors when running my integration tests. 我尝试实现一个解决方案,并且在运行集成测试时没有编译时错误但是运行时错误。 Is it not possible to use an Interface as a Key, or is it my tests there is something wrong with? 是否不能将接口用作密钥,或者我的测试是否存在问题?

My code looks something like this 我的代码看起来像这样

private Map<AInterface, Values> myMap = new HashMap<AInterface, Values>();

Upon retreiving the set of keys from myMap they do contain objects with expected Id, but are compared to not-equal. 在从myMap中检索该组键时,它们确实包含具有预期Id的对象,但是被比较为不相等。 So when using myMap.get(Object key) i get null, eventhough an object with the same id is there. 因此,当使用myMap.get(Object key)时,我得到null,尽管有一个具有相同id的对象。 When using the concrete class instead of the interface all tests pass: 当使用具体类而不是接口时,所有测试都通过:

private Map<AClass, Values> myMap = new HashMap<AClass, Values>();

I've read Generics where it states that for a Map, you are required to replace the type variables K and V by concrete types that are subtypes of Object. 我读过Generics ,它指出对于Map,你需要用Object的子类型的具体类型替换类型变量K和V.

Since the compiler does not give me any warnings when using an Interface for K, my guess would have been that the tests have errors. 由于编译器在使用Interface for K时没有给我任何警告,我的猜测是测试有错误。

Does anybody have any experience with using Interfaces as Key in a Map? 有没有人有使用Interfaces作为地图中的键的经验? And could give me any hints on what I am doing wrong? 我可以给我任何关于我做错的提示吗?

你的类必须实现hashCodeequals解释 ;你也应该熟悉Map-interface契约 )。

The objects that are extending your interface should all implement both hashCode and equals . 扩展接口的对象应该都实现hashCodeequals If equals returns true but the hashCode values are not equal, then the appropriate object is not found since the JVM places the objects in 'buckets' (when storing in a Map ) according to their hashCode value. 如果equals返回truehashCode值不相等,则找不到相应的对象,因为JVM根据其hashCode值将对象放置在“桶”中(当存储在Map )。

Both examples should work perfectly. 两个例子都应该完美。 It's OK to use interfaces, abstract or concrete class types in the 'generics'. 在'泛型'中使用接口,抽象或具体类类型是可以的。 I often use the List interface in Maps and never had problems. 我经常在地图中使用List界面,从来没有遇到过问题。

You say you only have to change the type of myMap and the constructor to make your tests pass? 你说你只需要改变myMap的类型和构造函数来让你的测试通过吗? What type of objects do you use as keys in the map, AClass or something else? 您在地图中使用什么类型的对象, AClass或其他东西?

Have a close look at your runtime errors or provide us some details (just the exceptions w/o stacktraces). 仔细查看运行时错误或提供一些细节(只是没有堆栈跟踪的例外)。

For the other problem, as Wesho already answered, implement either both hashcode and equals or (for testing) none of them on the class that you really use for keys in the map. 对于另一个问题,正如Wesho已经回答的那样,在你真正用于地图中的键的类上实现hashcode和equals或者(用于测试)它们中的任何一个。

EDIT 编辑

Knowing from a comment, that hashcode and equals are implemented: there's one possible trap - if you change the UUID after the object has already been put to the map (as a key), then you may not be able to find your values afterwards (although a rehash on the map should make it work again). 从评论可知,该散列码和equals实现:还有一个可能的陷阱-如果你改变了UUID 的对象已经投入到地图(如钥匙),那么你可能无法事后找到你的价值观(虽然在地图上重新拍摄应该让它再次起作用)。

EDIT 2 编辑2

If you receive a NPE directly on myMap.get(AClass key) , then either myMap is null or the key (but that still doesn't solve the other mystery...) 如果您直接在myMap.get(AClass key)上收到NPE,那么myMapnull或键(但仍然无法解决另一个谜......)

EDIT 3 编辑3

Just checked the implementation of hashcode and equals on UUID and that's ok. 刚检查了哈希码的实现,并在UUID上等于,这没关系。 The calculation is limited to the 128 bit UUID only. 计算仅限于128位UUID。 So if you create two UUID objects for the same UUID value and test for equality, then the two UUID objects are not the same but equal. 因此,如果为相同的UUID值创建两个UUID对象并测试相等性,则两个UUID对象不相同但相等。 That's good. 非常好。 If you have a getter for UUID in AClass, then you experiment with a Map like HashMap<UUID, Values> myMap and check if it still works (Maybe, by chance, the code change unhides the real bug ;) ) 如果你在AClass中有一个UUID的getter,那么你试验一个像HashMap<UUID, Values> myMap并检查它是否仍然有效(也许,偶然,代码更改取消隐藏真正的bug;))

[...] it states that for a Map, you are required to replace the type variables K and V by concrete types that are subtypes of Object. [...]它声明对于Map,您需要用作为Object子类型的具体类型替换类型变量K和V.

I was close to asking a question at SO but I think I've got the answer. 我差不多问了一个问题,但我想我已经得到了答案。

concrete type - It sounds a bit like 'concrete class', as if interfaces or abstract classes are not allowed. 具体类型 - 听起来有点像'具体类',就好像不允许接口或抽象类一样。 But it just says, that your not allowed to replace 'K' by another generic type 'S' or so. 但它只是说,你不允许用另一种通用类型'S'代替'K'。 It has to be a 'real' type: interfaces, classes, even enums are concrete enough. 它必须是一个“真正的”类型:接口,类,甚至枚举都足够具体。

subtypes of Object - Again it sounds like interfaces are not allowed because they don't subclass Object. 对象的子类型 - 再次听起来像接口是不允许的,因为它们不是Object的子类。 Yes, but: you can't instantiate an interface anyway, so the real objects that are put into the map are always class instances. 是的,但是:无论如何都无法实例化接口,因此放入地图的真实对象始终是类实例。 The only class of Java types that does not subclass Object are Java primitives , including arrays of Java primitives . 不是Object的子类的唯一Java类是Java原语 ,包括Java原语的数组 So Map<int, String> is not allowed as well as Map<String, int[]> . 因此不允许使用Map<int, String> 以及 Map<String, int[]>

The problem is nothing to do with interfaces. 问题与接口无关。 The generic types are erased at compilation, and at run-time HashMap only deals with Object instances. 泛型类型在编译时被擦除,并且在运行时HashMap仅处理Object实例。

We've no way of knowing what your problem is as you don't show any code, but it's most likely that your keys are mutable and your hashCode is changing. 我们无法知道您的问题是什么,因为您没有显示任何代码,但最有可能的是您的密钥是可变的并且您的hashCode正在发生变化。

For instance: 例如:

class MyKey {
    String name;

    public int hashCode() {
        return name.hashCode();
    }

    // assume suitable equals() implementation
}

Map<MyKey,Integer> myMap = new HashMap();

MyKey key1 = new MyKey();
key1.name = "Jimmy";

myMap.put(key1, 10);

key1.name = "Johnny";

myMap.get(key1);  // return null

If the hashcode of an object changes between it being added to a map, and trying to retrieve it, then the HashMap will (probably) look in the wrong hash bucket, and not find any value. 如果对象的哈希码在添加到地图之间发生变化并尝试检索它,那么HashMap(可能)会查找错误的哈希桶,而不会找到任何值。 You might sometimes get a result, if the old hashcode and the new hashcode resolve to the same hash bucket. 如果旧的哈希码和新的哈希码解析为相同的哈希桶,您有时可能会得到结果。

If you're using Apache Commons lang JAR, perhaps add this to your class. 如果您正在使用Apache Commons lang JAR,可以将其添加到您的班级。

public int hashCode() { 
    return HashCodeBuilder.reflectionHashCode(this);
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM