我的数据是以“密钥”格式组织的,而不是“键值”。它就像一个HashMap,但我需要在两个方向上进行O(1)查找。这种类型的数据结构是否有名称,Java标准库中是否包含这样的名称? (或者也许是Apache Commons?)
我可以编写自己的类,基本上使用两个镜像地图,但我宁愿不重新发明轮子(如果这已经存在,但我只是不寻找合适的术语)。
答案 0 :(得分:105)
Java API中没有这样的类。您想要的Apache Commons类将成为BidiMap的实现之一。
作为一名数学家,我会称这种结构为双射。
答案 1 :(得分:73)
答案 2 :(得分:19)
这是一个我用来完成这个的简单类(我不想再有另一个第三方依赖)。它不提供地图中提供的所有功能,但它是一个良好的开端。
public class BidirectionalMap<KeyType, ValueType>{
private Map<KeyType, ValueType> keyToValueMap = new ConcurrentHashMap<KeyType, ValueType>();
private Map<ValueType, KeyType> valueToKeyMap = new ConcurrentHashMap<ValueType, KeyType>();
synchronized public void put(KeyType key, ValueType value){
keyToValueMap.put(key, value);
valueToKeyMap.put(value, key);
}
synchronized public ValueType removeByKey(KeyType key){
ValueType removedValue = keyToValueMap.remove(key);
valueToKeyMap.remove(removedValue);
return removedValue;
}
synchronized public KeyType removeByValue(ValueType value){
KeyType removedKey = valueToKeyMap.remove(value);
keyToValueMap.remove(removedKey);
return removedKey;
}
public boolean containsKey(KeyType key){
return keyToValueMap.containsKey(key);
}
public boolean containsValue(ValueType value){
return keyToValueMap.containsValue(value);
}
public KeyType getKey(ValueType value){
return valueToKeyMap.get(value);
}
public ValueType get(KeyType key){
return keyToValueMap.get(key);
}
}
答案 3 :(得分:11)
如果没有发生碰撞,您可以随时将两个方向添加到同一个HashMap: - )
答案 4 :(得分:3)
这是我的2美分。
或者您可以使用泛型的简单方法。一块蛋糕。
public static <K,V> Map<V, K> invertMap(Map<K, V> toInvert) {
Map<V, K> result = new HashMap<V, K>();
for(K k: toInvert.keySet()){
result.put(toInvert.get(k), k);
}
return result;
}
当然,您必须拥有具有唯一值的地图。否则,其中一个将被替换。
答案 5 :(得分:0)
这里有一个老问题,但是如果其他人像我刚刚那样有脑力障碍并且偶然发现这个问题,希望这会有所帮助。
我也在寻找双向HashMap,有时它是最简单的最有用的答案。
如果您不想重新发明轮子并且不想在项目中添加其他库或项目,那么如果您的设计需要并行数组(或ArrayLists)的简单实现怎么样。
SomeType[] keys1 = new SomeType[NUM_PAIRS];
OtherType[] keys2 = new OtherType[NUM_PAIRS];
只要知道两个键中的1个索引,就可以轻松地请求另一个键。所以你的查找方法看起来像:
SomeType getKey1(OtherType ot);
SomeType getKey1ByIndex(int key2Idx);
OtherType getKey2(SomeType st);
OtherType getKey2ByIndex(int key2Idx);
这假设您正在使用适当的面向对象结构,其中只有方法修改这些数组/ ArrayLists,保持它们并行非常简单。对于ArrayList来说更容易,因为如果数组的大小发生变化就不必重建,只要你串联添加/删除。
答案 6 :(得分:0)
受到GETah's answer的启发,我决定自己写一些类似的东西并做出一些改进:
Map<K,V>
- 界面put
更改值时,通过照顾它来确保双向性(至少我希望在此保证)用法就像普通地图一样,可以在映射调用getReverseView()
上获得反向视图。不复制内容,仅返回视图。
我不确定这完全是万无一失的(实际上,它可能不是),所以如果您发现任何缺陷,请随时发表评论,我会更新答案。
public class BidirectionalMap<Key, Value> implements Map<Key, Value> {
private final Map<Key, Value> map;
private final Map<Value, Key> revMap;
public BidirectionalMap() {
this(16, 0.75f);
}
public BidirectionalMap(int initialCapacity) {
this(initialCapacity, 0.75f);
}
public BidirectionalMap(int initialCapacity, float loadFactor) {
this.map = new HashMap<>(initialCapacity, loadFactor);
this.revMap = new HashMap<>(initialCapacity, loadFactor);
}
private BidirectionalMap(Map<Key, Value> map, Map<Value, Key> reverseMap) {
this.map = map;
this.revMap = reverseMap;
}
@Override
public void clear() {
map.clear();
revMap.clear();
}
@Override
public boolean containsKey(Object key) {
return map.containsKey(key);
}
@Override
public boolean containsValue(Object value) {
return revMap.containsKey(value);
}
@Override
public Set<java.util.Map.Entry<Key, Value>> entrySet() {
return Collections.unmodifiableSet(map.entrySet());
}
@Override
public boolean isEmpty() {
return map.isEmpty();
}
@Override
public Set<Key> keySet() {
return Collections.unmodifiableSet(map.keySet());
}
@Override
public void putAll(Map<? extends Key, ? extends Value> m) {
m.entrySet().forEach(e -> put(e.getKey(), e.getValue()));
}
@Override
public int size() {
return map.size();
}
@Override
public Collection<Value> values() {
return Collections.unmodifiableCollection(map.values());
}
@Override
public Value get(Object key) {
return map.get(key);
}
@Override
public Value put(Key key, Value value) {
Value v = remove(key);
getReverseView().remove(value);
map.put(key, value);
revMap.put(value, key);
return v;
}
public Map<Value, Key> getReverseView() {
return new BidirectionalMap<>(revMap, map);
}
@Override
public Value remove(Object key) {
if (containsKey(key)) {
Value v = map.remove(key);
revMap.remove(v);
return v;
} else {
return null;
}
}
}