当两个键映射到同一个值时,是否有任何解决方案可以避免使用两个映射?问题是:我们有一个接收两种类型请求的服务器——getDataByUserID(UserID userId)
和getDataByNodeID(NodeID nodeId)
,其中userId
和nodeId
具有一对一映射。不使用数据库,所有数据都存储在内存中。有一个简单的解决方案——使用两个映射——一个UserID/data
,另一个NodeID/data
,但我希望避免使用两个表进行操作。服务器接口为:
void put (InetSocketAddress nodeId, String clientId, Data data);
Data get (InetSocketAddress nodeId);
Data get (String clientId);
欢迎提出任何建议。
没有智能数据结构可以做到这一点。正如@JavaMan所建议的那样,如果两种类型的键都有一个共同的超类型,那么可以使用一个HashMap
。然而,如果您需要并发解决方案,那么在没有竞争条件的情况下,这将很难实现。(您不能自动添加或删除两个条目…(
有一个讨厌的解决方案可以避免这种情况。您可以重新定义NodeId
和UserId
类来实现一个公共接口;比如CCD_ 10。然后,您需要重新定义equals(Object)
和hashCode()
方法,以便它们将这两种标识符视为等效标识符。
例如:
UserId a = ...
NodeId b = ... // representing the same user as 'a'
然后
a.equals(b) => true
b.equals(a) => true
a.hashCode() == b.hashCode()
除了equals/hashcode合约的其他方面之外。
重要注意:这假设有一种高效的方式来实现上述语义,而不依赖于我们正在定义的映射。
然后,可以使用UserId
实例或NodeId
实例作为关键字,将映射更改为HashMap<CommonId, YourValueClass>
、put
和get
。
为什么这么恶心?
因为
equals
和hashCode
的重新定义适用于这两个类的所有用途,而不仅仅是这个映射。因为它违反了
equals(Object)
的文档语义。javadocs表示,如果this
和other
具有不同的类,则this.equals(other)
应该返回false
。
然而,这确实建议了一些替代解决方案:
您可以使用
TreeMap
而不是HashMap
,并提供提供一致排序的Comparator<CommonId>
,and将两种类型的等效标识符视为相等。- 目前尚不清楚实施订单是否可行
- 对于
get
和put
操作,TreeMap是O(logN)
而不是O(1)
您可以尝试找到一个第三方散列映射实现,它允许您提供散列和等于函数;即类似于向CCD_ 33提供CCD_。
也许可以在UserId和NodeId中添加一个接口。
类似这样的东西:
void test()
{
UserId userId = new UserId();
NodeId nodeId = new NodeId();
String userData = "xyz";
HashMap<IdValue, String> idToDataMap = new HashMap<>();
idToDataMap.put(userId,userData);
idToDataMap.put(nodeId,userData);
}
class UserId implements IdValue {
//...
}
class NodeId implements IdValue {
//...
}
interface IdValue {
}
当然。很多解决方案。
制作自己的课程
上一堂课。这个类内部有两个字段:Map<UserId, Data> userIdToData
和Map<NodeId, Data> nodeIdToData
。这些都是私人领域。该类本身并不实现java.util.Map
,但它有许多方法,例如size()
(它只实现return userIdToData.size();
(。它还有:
public void put(UserId userId, NodeId nodeId, Data data) {
userIdToData.put(userId, data);
nodeIdToData.put(nodeId, data);
}
public Data getByUserId(UserId userId) {
return userIdToData.get(userId);
}
// ... and getByNodeId, and getByUserIdOrDefault, etcetera.
THIS代码将扰乱2个映射,需要确保它们保持同步,但您可以为该代码编写大量测试,整个项目中的所有其他代码都不需要担心。
将UserId映射到NodeId,然后将NodeId映射到数据
有2张地图:
Map<UserId, NodeId> userToNode;
Map<NodeId, Data> nodeToData;
以及一种方法:
public Data getByUserId(UserId id) {
NodeId node = userToNode.get(id);
if (node == null) return null;
return nodeToData.get(node);
}