У меня есть данные, которые организованы в виде "ключевого ключа", а не "ключевого значения". Это похоже на HashMap, но мне понадобится O (1) поиск в обоих направлениях. Есть ли название для такого типа структуры данных и что-то вроде этого включено в стандартные библиотеки Java? (или, может быть, Apache Commons?)
Я мог бы написать свой собственный класс, который в основном использует два зеркальных Карты, но я бы предпочел не изобретать колесо (если это уже существует, но я просто не ищу правильный термин).
Ответ 1
В Java API такого класса нет. Класс Apache Commons, который вы хотите, будет одной из реализаций BidiMap.
Будучи математиком, я бы назвал такую структуру биекцией.
Ответ 2
В дополнение к Apache Commons, Guava также имеет BiMap.
Ответ 3
Вот простой класс, который я использовал для этого (я не хотел иметь другую зависимость от третьей стороны). Он не предлагает все функции, доступные на Картах, но это хороший старт.
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);
}
}
Ответ 4
Если никаких столкновений не происходит, вы всегда можете добавить оба направления в один и тот же HashMap: -)
Ответ 5
Вот мои 2 цента.
Или вы можете использовать простой метод с generics. Кусок торта.
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;
}
Конечно, у вас должна быть карта с уникальными значениями. В противном случае один из них будет заменен.
Ответ 6
Довольно старый вопрос здесь, но если у кого-то есть мозговой блок, как я только что сделал, и наткнулся на это, надеюсь, это поможет.
Я тоже искал двунаправленную 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, так как вам не придется перестраивать, если размер массивов изменяется, если вы добавляете/удаляете в тандеме.
Ответ 7
Вдохновленный ответ GETah Я решил написать что-то подобное сам с некоторыми улучшениями:
- Класс реализует
Map<K,V>
-Interface
- Двунаправленность действительно гарантируется, заботясь об этом при изменении значения на
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;
}
}
}