我正在使用
Java 6.
假设我有一个类,我想将其实例保存到地图中.稍后我想仅使用“关键字段”检索实例.我将忽略字段修饰符,getter和setter以简洁.
class A {
String field1;
String field2;
String field3;
String field4;
//more fields
public int hashCode(){
//uses only field1 and field2
}
public boolean equals(Object o){
//uses only field1 and field2
}
}
由于Java的标准API没有MultikeyMap,我不想使用第三方库,我可以选择
1)创建一个新类KeyA来表示地图的关键
2)当我需要从地图中检索对象时,使用A本身作为键并仅填充“关键字段”
3)嵌套地图,例如HashMap< String,HashMap< String,A>>
4)其他解决方法
人们通常使用什么,何时使用?
最佳答案 鉴于您最近的编辑,在这种情况下,您应该可以使用A类的实例作为键.查找将基于equals()和hashCode()的语义完成,因此这将导致仅通过“关键字段”检索实例.因此,以下代码将按您的意图工作:
final Map<A, String> map = new HashMap<A, Object>();
final A first = new A("fe", "fi", "fo", "fum");
map.put(first, "success");
// later on
final A second = new A ("fe", "fi", "foo", "bar");
System.out.println(map.get(second)); // prints "success";
话虽如此,你对选项2的描述让我有点担心这可能不是最明智的选择.如果您创建Map< A,String>,那就是从A类实例到字符串的映射.然而,你的第二点暗示你想把它想象成从关键字段到字符串的映射.如果您通常会根据一些“原始”字符串查找值,那么我建议不要这样做.对我来说,创建A的“假”实例只是为了进行查找是错误的 – 所以在这种情况下,你可能应该创建一个包含选项1中描述的字符串对的键类.(你可以甚至在你的A对象中嵌入这些实例以保存关键字段).
对选项3也有类似的论据.如果字符串确实在概念上是分层的,那么它可能很有意义.例如,如果field1是Country,而field2是Town,那么肯定可以说嵌套地图是有意义的 – 你有一个从国家到城镇地图的映射 – >该国家内的关系.但是如果你的键不以这种方式自然地组成(比如,如果它们是(x,y)坐标),那么这也不是表示数据的非常自然的方式,而是从XYPoint到值的单级映射会更明智. (同样地,如果你从不使用两级地图,除了总是直接穿过两个层,人们可能会认为单层地图仍然更有意义.)
最后,对于选项4 – 如果你总是映射到A本身,并将密钥存储为它自己的值(例如,如果你想规范你的A实例,有点像String.intern())那么就像指出的那样你根本不需要使用Map,而Set会在这里完成工作.当您想要在不同对象之间建立关系时,Map非常有用,而Set会自动为您提供对象的唯一性,而不会产生任何额外的概念开销.
如果您确实使用类本身作为键,请注意,如果对象通常只用作键,如果它们的hashCode(和equals的行为)不会随着时间的推移而改变.通常这意味着密钥是不可变的,但在这里你可以负担得到可变的“非密钥”字段.如果你打破这个规则,你会看到奇怪的行为,如下所示:
// Populate a map, with an A as the key
final Map<A, String> map = new HashMap<A, Object>();
final A a = new A("one", "two", "three", "four");
map.put(a, "here");
// Mutate a
a.setField1("un");
// Now look up what we associated with it
System.out.println(map.get(a)); // prints "null" - huh?
System.out.println(map.containsKey(a)); // prints "false"