映射到相同值的两个键.Java



当两个键映射到同一个值时,是否有任何解决方案可以避免使用两个映射?问题是:我们有一个接收两种类型请求的服务器——getDataByUserID(UserID userId)getDataByNodeID(NodeID nodeId),其中userIdnodeId具有一对一映射。不使用数据库,所有数据都存储在内存中。有一个简单的解决方案——使用两个映射——一个UserID/data,另一个NodeID/data,但我希望避免使用两个表进行操作。服务器接口为:

void put (InetSocketAddress nodeId, String clientId, Data data);
Data get (InetSocketAddress nodeId);
Data get (String clientId);

欢迎提出任何建议。

没有智能数据结构可以做到这一点。正如@JavaMan所建议的那样,如果两种类型的键都有一个共同的超类型,那么可以使用一个HashMap。然而,如果您需要并发解决方案,那么在没有竞争条件的情况下,这将很难实现。(您不能自动添加或删除两个条目…(

有一个讨厌的解决方案可以避免这种情况。您可以重新定义NodeIdUserId类来实现一个公共接口;比如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>putget

为什么这么恶心?

  • 因为equalshashCode的重新定义适用于这两个类的所有用途,而不仅仅是这个映射。

  • 因为它违反了equals(Object)的文档语义。javadocs表示,如果thisother具有不同的类,则this.equals(other)应该返回false

然而,这确实建议了一些替代解决方案:

  1. 您可以使用TreeMap而不是HashMap,并提供提供一致排序的Comparator<CommonId>,and将两种类型的等效标识符视为相等。

    • 目前尚不清楚实施订单是否可行
    • 对于getput操作,TreeMap是O(logN)而不是O(1)
  2. 您可以尝试找到一个第三方散列映射实现,它允许您提供散列和等于函数;即类似于向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> userIdToDataMap<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);
}

最新更新