2010-10-22 67 views
2

我正在寻找一个集合类型数据结构来实现以下功能。假设我有这样的课程:具有多个值的密钥集合

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。这工作,但似乎有点不整洁 - 任何人都可以建议一个更优雅的解决方案或更好的数据结构来使用,谢谢?

回答

4

你可以调整你的班级,使其明确。这比简单地连接键值更加健壮,并且避免了在希望将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; } 
} 
+0

是的,这是有道理的,谢谢 – CodeClimber 2010-10-22 15:39:33

+0

我还应该指出,使用字符串像性和eyeColour的东西可能是一个坏主意;你应该考虑使用枚举。同样,您可能想要在实例化时检查高度是否定的,也可能将其重命名为明确的;例如“heightInCentimetres”。 – Adamski 2010-10-22 15:44:14

+0

这只是我为了这个问题而编造的一个例子! – CodeClimber 2010-10-22 15:56:25

0

您可以使用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的视图,所以您可以节省内存。

+0

谢谢埃米尔 - 不知道这是否适合我。你正在传递新的FilterHomeTown(“NY”) - 我正在寻找将具有相同homeTown的元素分组在一起,而不是homeTown为“NY”或“LA”等的所有元素。或者我误解了? – CodeClimber 2010-10-22 15:51:32

2

创建一个对象来建模您的密钥。例如class PersonKey { String homeTown, sex, eyeColour }(为简洁起见省略了吸气剂和吸附剂)

对此对象实施equalshashCode方法。

将此对象用作Map中的键。

从您的Person对象中删除属性或将其替换为对PersonKey对象的引用。

此外,考虑将地图的类型设置为以下内容,即不需要指定使用哪种类型的Set作为地图的关键字。

Map<String, Set<Person>> = new HashMap<String, Set<Person>>(); 

而且,如果你使用的是Set<Person>,那么你就需要重写的PersonequalshashCode为好,否则Set不能正确判断两个Person对象表示同一人或没有,这是需要确保集合只包含独特的元素。

+0

谢谢艾德里安,同样的建议(或多或少),因为我已经实施了亚当斯基 – CodeClimber 2010-10-22 15:40:32

0

org.apache.commons.collections.map。MultiValueMap

+0

嗨,先生,谢谢你的建议。看看这个API。我不认为这是我在寻找的东西 - 它使用一个Collection作为Map值,但它并没有给我一个更好的方式来实现我所问的Map键。我可以将它与Adamski和Adrian Smith提出的关键解决方案一起使用。 – CodeClimber 2010-10-22 15:46:02