具有多个值的密钥集合
我正在寻找一个集合类型数据结构来实现以下功能。假设我有这样的课程:具有多个值的密钥集合
class Person() {
String homeTown; // key
String sex; // key
String eyeColour; // key
String name;
long height;
// other stuff....
}
我正在处理多个Person对象。我想把它们组织成集合,每个集合包含具有相同homeTown,sex和eyeColour的Person对象。目前我正在执行这样的事情:
Map<String, HashSet<Person>> = new HashMap<String, HashSet<Person>>;
其中关键是homeTown,sex和eyeColour concavanation。这工作,但似乎有点不整洁 - 任何人都可以建议一个更优雅的解决方案或更好的数据结构来使用,谢谢?
你可以调整你的班级,使其明确。这比简单地连接键值更加健壮,并且避免了在希望将Person实例存储在Map中时的任何额外对象创建开销,因为您已经提前创建了密钥。
public class Person {
public class Key {
private final String homeTown;
private final String sex;
private final String eyeColour;
public Key(String homeTown, String sex, String eyeColour) { ... }
public boolean equals(Object o) { /* Override to perform deep equals. */ }
public int hashCode() { /* Could pre-compute in advance if the key elements never change. */ }
}
private final Key key;
private final String name;
private final long height;
public Person(String homeTown, String sex, String eyeColour, String name, long height) {
this.key = new Key(homeTown, sex, eyeColour);
this.name = name;
this.height = height;
}
public Key getKey() {
return key;
}
public String getName() { return name; }
public long getHeight() { return height; }
}
您可以使用guava's Sets.filter方法来过滤person对象。
实施例:
Person类:
public class Person {
String name;
String hometown;
int age;
public Person(String name, String hometown, int age) {
this.name = name;
this.age = age;
this.hometown = hometown;
}
@Override
public int hashCode() {
int hash = 17;
hash = 37 * hash + name.hashCode();
hash = 37 * hash + hometown.hashCode();
hash = 37 * hash + age;
return hash;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
Person p;
if (obj instanceof Person)
p = (Person) obj;
else
return false;
if (this.name.equals(p.name) && this.hometown.equals(p.hometown)
&& this.age == p.age)
return true;
return false;
}
@Override
public String toString() {
StringBuilder b = new StringBuilder();
b.append("[name = ").append(name).append("\n");
b.append("home town = ").append(hometown).append("\n");
b.append("age = ").append(age).append("]");
return b.toString();
}
}
TestGuavaFilter类:
public class TestGuavaFilter {
public static void main(String[] args) {
Set<Person> set = new HashSet<Person>();
set.add(new Person("emil", "NY", 24));
set.add(new Person("Sam", "NY", 50));
set.add(new Person("george", "LA", 90));
System.out.println(Sets.filter(set, new FilterHomeTown("NY")));
}
}
class FilterHomeTown implements Predicate<Person> {
String home;
public FilterHomeTown(String home) {
this.home = home;
}
@Override
public boolean apply(Person arg0) {
if (arg0.hometown.equals(this.home))
return true;
return false;
}
}
使用过滤器的优点是,可以过滤Person对象在任何方式,假设你想过滤呃只使用家乡,而不是其他2个属性,这将有所帮助。因为番石榴的过滤器只会产生一个真实的Set的视图,所以您可以节省内存。
谢谢埃米尔 - 不知道这是否适合我。你正在传递新的FilterHomeTown(“NY”) - 我正在寻找将具有相同homeTown的元素分组在一起,而不是homeTown为“NY”或“LA”等的所有元素。或者我误解了? – CodeClimber 2010-10-22 15:51:32
创建一个对象来建模您的密钥。例如class PersonKey { String homeTown, sex, eyeColour }
(为简洁起见省略了吸气剂和吸附剂)
对此对象实施equals
和hashCode
方法。
将此对象用作Map
中的键。
从您的Person
对象中删除属性或将其替换为对PersonKey
对象的引用。
此外,考虑将地图的类型设置为以下内容,即不需要指定使用哪种类型的Set
作为地图的关键字。
Map<String, Set<Person>> = new HashMap<String, Set<Person>>();
而且,如果你使用的是Set<Person>
,那么你就需要重写的Person
equals
和hashCode
为好,否则Set
不能正确判断两个Person
对象表示同一人或没有,这是需要确保集合只包含独特的元素。
谢谢艾德里安,同样的建议(或多或少),因为我已经实施了亚当斯基 – CodeClimber 2010-10-22 15:40:32
org.apache.commons.collections.map。MultiValueMap
嗨,先生,谢谢你的建议。看看这个API。我不认为这是我在寻找的东西 - 它使用一个Collection作为Map值,但它并没有给我一个更好的方式来实现我所问的Map键。我可以将它与Adamski和Adrian Smith提出的关键解决方案一起使用。 – CodeClimber 2010-10-22 15:46:02
是的,这是有道理的,谢谢 – CodeClimber 2010-10-22 15:39:33
我还应该指出,使用字符串像性和eyeColour的东西可能是一个坏主意;你应该考虑使用枚举。同样,您可能想要在实例化时检查高度是否定的,也可能将其重命名为明确的;例如“heightInCentimetres”。 – Adamski 2010-10-22 15:44:14
这只是我为了这个问题而编造的一个例子! – CodeClimber 2010-10-22 15:56:25