两个键映射到相同的值。爪哇

时间:2020-11-07 12:46:22

标签: java hashmap

是否有任何解决方案可以避免在两个键映射到相同值的情况下避免使用两个映射?问题是:我们有一台服务器,它接收两种类型的请求-getDataByUserID(UserID userId)getDataByNodeID(NodeID nodeId),其中userIdnodeId具有一对一的映射。没有使用DB,所有数据都存储在内存中。有一个简单的解决方案-使用两个映射UserID/data和第二个NodeID/data,但是我想避免使用两个表进行操作。 服务器界面为:

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

欢迎提出任何建议。

3 个答案:

答案 0 :(得分:1)

也许可以在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 {
}

答案 1 :(得分:1)

没有智能数据结构可以执行此操作。如@JavaMan所建议,如果键具有相同的超类型,则可以对两个类型的键使用一个HashMap。但是,如果您需要并发解决方案,那么在没有竞争条件的情况下很难实现这一点。 (您不能自动添加或删除两个条目...)

有一个避免这种情况的讨厌解决方案。您可以重新定义NodeIdUserId类以实现一个公共接口。说CommonId。然后,您需要重新定义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合同的其他方面。

重要说明:这是假设存在一种有效的方式来实现上述语义,该方式不依赖于我们正在定义的映射。

然后,您可以使用HashMap<CommonId, YourValueClass>实例或put实例作为键,将地图更改为getUserIdNodeId。 / p>

为什么这很讨厌?

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

  • 因为它违反了equals(Object)的已记录语义。 Javadocs指出,如果this.equals(other)false具有不同的类,则this 应该返回other

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

  1. 您可以使用TreeMap代替HashMap,并提供Comparator<CommonId>来提供一致的顺序,并且将两种类型的等效标识符视为相等。

    • 尚不清楚实施该命令是否可行。
    • 对于O(logN)O(1)操作,TreeMap是get而不是put
  2. 您可以尝试找到一个第三方哈希映射实现,该实现允许您提供哈希和等于函数;即类似于向Comparator提供TreeMap

答案 2 :(得分:0)

好的。很多解决方案。

上自己的课

上课。此类内部有2个字段: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);
}