Java 多键哈希映射
最近我参加了一次面试,将大量员工的详细信息保存在DS中 我以Hashmap的形式给出了解决方案,emp Id作为密钥 接下来的问题是,用户是否希望基于名称进行搜索,以及如何实现它。我建议使用emp名称作为键,并使用与Arraylist相同的名称保存所有员工 接下来的问题很棘手,需要创建一个地图,用户可以根据emp Id或emp名称进行搜索。如何在map中实现这一点Java 多键哈希映射,java,Java,最近我参加了一次面试,将大量员工的详细信息保存在DS中 我以Hashmap的形式给出了解决方案,emp Id作为密钥 接下来的问题是,用户是否希望基于名称进行搜索,以及如何实现它。我建议使用emp名称作为键,并使用与Arraylist相同的名称保存所有员工 接下来的问题很棘手,需要创建一个地图,用户可以根据emp Id或emp名称进行搜索。如何在map中实现这一点 在内存中高效地实现它。这是一个肮脏的解决方案(是的——非常肮脏,永远不要在生产环境中这样做!),但如果键是不同类型的,并且一个不是另
在内存中高效地实现它。这是一个肮脏的解决方案(是的——非常肮脏,永远不要在生产环境中这样做!),但如果键是不同类型的,并且一个不是另一个的子类型(例如
长和字符串
),它将起作用。按两个键放置每个员工,并按提供的键获取,可以是id
或name
:
Map<?, List<Employee>> map = new HashMap<>();
public void putEmployee(Employee e) {
map.put(e.id, Arrays.asList(e)); // put by id
if (!map.containsKey(e.name)) {
map.put(e.name, new ArrayList<>());
}
map.get(e.name).add(e); // put by name
}
public Employee getById(long id) {
return map.containsKey(id) ? map.get(id).get(0) : null;
}
public List<Employee> getByName(String name) {
return map.containsKey(name) ? map.get(name) : Collections.emptyList();
}
Map一种方法是创建一个Key
对象,该对象可以通过名称或id进行搜索:
public enum KeyType {
ID, NAME;
}
public class SearchKey {
private KeyType keyType;
private String value;
// constructor and getters snipped for brevity's sake
@Override
public boolean equals(Object o) {
if (this == o) {
return true;
}
if (o == null || getClass() != o.getClass()) {
return false;
}
SearchKey searchKey = (SearchKey) o;
return keyType == searchKey.keyType && value.equals(searchKey.value);
}
@Override
public int hashCode() {
int result = keyType.hashCode();
result = 31 * result + value.hashCode();
return result;
}
public class Directory {
private Map<SearchKey, Set<Employee>> directory = new HashMap<>();
public void addEmployee(Employee e) {
// Add search by id
directory.put
(new SearchKey(KeyType.ID, e.getId()), Collections.singleton(e));
// Add search by name
SearchKey searchByName = new SearchKey(KeyType.NAME, e.getName());
Set<Employee> employees = directory.get(searchByName);
if (employees == null) {
employees = new HashSet<>();
directory.put(searchByName, employees);
}
employees.add(e);
}
public Employee getById (String id) {
// Assume that the ID is unique
return directory.get(new SearchKey(KeyType.ID, id)).iterator().next();
}
public Set<Employee> getByName (String name) {
return directory.get(new SearchKey(KeyType.NAME, name));
}
}
公共枚举密钥类型{
身份证、姓名;
}
公共类搜索密钥{
私钥类型;
私有字符串值;
//为了简洁起见,构造函数和getter被剪掉了
@凌驾
公共布尔等于(对象o){
if(this==o){
返回true;
}
如果(o==null | | getClass()!=o.getClass()){
返回false;
}
SearchKey SearchKey=(SearchKey)o;
返回keyType==searchKey.keyType&&value.equals(searchKey.value);
}
@凌驾
公共int hashCode(){
int result=keyType.hashCode();
result=31*result+value.hashCode();
返回结果;
}
公共类目录{
私有映射目录=new HashMap();
公共无效添加员工(员工e){
//添加按id搜索
目录.put
(新的SearchKey(KeyType.ID,e.getId()),Collections.singleton(e));
//按名称添加搜索
SearchKey searchByName=新的搜索键(KeyType.NAME,e.getName());
设置employees=directory.get(searchByName);
if(employees==null){
employees=newhashset();
directory.put(searchByName,employees);
}
增加(e);
}
公共雇员getById(字符串id){
//假设ID是唯一的
return directory.get(newsearchkey(KeyType.ID,ID)).iterator().next();
}
公共集getByName(字符串名称){
返回directory.get(新的SearchKey(KeyType.NAME,NAME));
}
}
这是一个非常容易回答的问题:只需将id转换为字符串,并将员工存储两次——一次在名称下,另一次在id下作为字符串
您使用列表作为值的想法很好-对于IDs,列表的大小为1
请注意,最好使用两个映射,因为每个ID只有一名员工,并且不必将大小为1的列表作为退化情况处理,因此:
Map<Integer, Employee> employeesById;
Map<String, Set<Employee>> employeesByName;
Map employeesById;
映射employeesByName;
特别要注意的是,只使用一个映射不会占用更少的内存。事实上,与在单独的映射中存储员工的ID键和姓名键相比,您会占用更多的内存。我想出了一个解决方案。请发表您的建议
步骤1:以emp id为键,emp对象为值,形成hashmap
步骤2:为相同的名称创建一个与名称匹配的emp id列表,例如:name='XYZ'id={101102103,…}
步骤3:将此名称作为键和arraylist作为值插入同一映射
在这里,我们不会两次存储完整的员工详细信息。只是尝试维护姓名和id之间的关系。因此相对而言,它可以节省内存。那么,您将如何开始这样做?一个技巧是使用[name+id]作为键。我想如果id是整数并且命名为字符串,冲突是不可能的。可能更适合程序员网站,因为这个问题不涉及在编程问题上寻求帮助。@Tunaki它将如何帮助?假设我需要检索所有Peter
s,我应该请求什么键?@Tunaki那么如果我按名称搜索??如何过滤?我会为一家公司运行一种方法,它会对这样的东西感到满意。@async我相信问题是关于Java功能的,而不是关于编写干净的代码。我需要强调答案中的第一句和最后一句。我相信这个问题只是一个愚蠢的问题。如果他们想知道候选人是否了解Java功能,那么y应该明确地问这个问题,而不是发明这个场景。顺便说一句,这里不是判断你的解决方案,而是问题。@Sasha:谢谢你花时间给出解决方案。但我觉得这就像创建一个包含两次记录的地图一样。(一个作为id,一个作为名称).键入映射的安全变体谢谢你的想法。但是存储员工详细信息两次会让我们消耗更多内存。有没有更好的解决方案?@Lathy没有,你不会消耗更多内存!映射中只存储引用,而不存储对象本身(内存中每个员工只有一个副本),因此,您所拥有的只是另一个映射中的一个映射条目(实际上只是一对引用)(不管它在哪个映射中,您都需要该条目)。另外,您不必创建不必要的列表来存储单个条目。使用两个映射会减少内存使用,而不是更多。嗯,我同意。您对我的解决方案有什么看法?@lathy您的解决方案需要一个映射
-混合键类型和混合值类型。如果这样做了,为什么不这样做呢,但您必须牺牲java的强大功能如果面试问题是一个“谜题”不打算在生产中使用,那么你的吸力是一个很好的吸力-这并不比你只使用一个贴图的人工和糟糕的设计要求更糟糕。我有一个疑问,你说在你上面提到的解决方案中,你会将名称保存为键,但新对象(值)不会覆盖旧值?