#java #multimap
#java #multimap
Вопрос:
Я ищу способ хранения пар ключ-значение. Мне нужно, чтобы поиск был двунаправленным, но в то же время мне нужно сохранить несколько значений для одного и того же ключа. Другими словами, что-то вроде BidiMap, но для каждого ключа может быть несколько значений. Например, она должна иметь возможность хранить пары типа: «s1»-> 1, «s2»-> 1, «s3»-> 2, и мне нужно иметь возможность получать значение, сопоставленное каждому ключу, и для каждого значения получать все ключи, связанные с ним.
Комментарии:
1. Вы говорите о необходимости иметь несколько значений для каждого ключа, но в вашем примере у вас нет ключа с несколькими значениями, а одно значение с двумя ключами. Вероятно, вам следует уточнить это. Если ваш пример соответствует вашему вопросу, вы получите лучшие ответы 😉
2. jguru.com/faq/view.jsp?EID=1317828 здесь вы можете найти, как создать multimap
3. @pushy, та же проблема, если я переверну карту и сохраню целые числа как ключи, а не как значения, я получу сопоставление «один ко многим». В любом случае, спасибо за исправление. 🙂
Ответ №1:
Итак, вам нужна поддержка отношений «многие ко многим»? Самое близкое, что вы можете получить, — это Guava, Multimap
как писал @Mechkov, но более конкретно — Multimap
комбинация с Multimaps.invertFrom
. «BiMultimap» еще не реализован, но существует проблема с запросом этой функции в библиотеке Google Guava.
На данный момент у вас есть несколько вариантов:
-
Если ваша «BiMultimap» будет неизменяемой константой — используйте
Multimaps.invertFrom
иImmutableMultimap
/ImmutableListMultimap
/ImmutableSetMultimap
(каждое из этих трех имеет разные значения для хранения коллекции). Некоторый код (пример взят из приложения, которое я разрабатываю, используетEnum
s иSets.immutableEnumSet
):public class RolesAndServicesMapping { private static final ImmutableMultimap<Service, Authority> SERVICES_TO_ROLES_MAPPING = ImmutableMultimap.<Service, Authority>builder() .put(Service.SFP1, Authority.ROLE_PREMIUM) .put(Service.SFP, Authority.ROLE_PREMIUM) .put(Service.SFE, Authority.ROLE_EXTRA) .put(Service.SF, Authority.ROLE_STANDARD) .put(Service.SK, Authority.ROLE_STANDARD) .put(Service.SFP1, Authority.ROLE_ADMIN) .put(Service.ADMIN, Authority.ROLE_ADMIN) .put(Service.NONE, Authority.ROLE_DENY) .build(); // Whole magic is here: private static final ImmutableMultimap<Authority, Service> ROLES_TO_SERVICES_MAPPING = SERVICES_TO_ROLES_MAPPING.inverse(); // before guava-11.0 it was: ImmutableMultimap.copyOf(Multimaps.invertFrom(SERVICES_TO_ROLES_MAPPING, HashMultimap.<Authority, Service>create())); public static ImmutableSet<Authority> getRoles(final Service service) { return Sets.immutableEnumSet(SERVICES_TO_ROLES_MAPPING.get(service)); } public static ImmutableSet<Service> getServices(final Authority role) { return Sets.immutableEnumSet(ROLES_TO_SERVICES_MAPPING.get(role)); } }
-
Если вы действительно хотите, чтобы ваша Multimap была изменяемой, будет сложно поддерживать оба варианта K-> V и V-> K, если только вы не будете изменять только
kToVMultimap
и вызыватьinvertFrom
каждый раз, когда захотите получить ее инвертированную копию (и делая эту копию неизменяемой, чтобы убедиться, что вы случайно не изменитеvToKMultimap
то, что не будет обновлятьсяkToVMultimap
). Это не оптимально, но в данном случае должно сработать. -
(Вероятно, не в вашем случае, упоминается в качестве бонуса):
BiMap
интерфейс и реализующие классы имеют.inverse()
метод, который предоставляетBiMap<V, K>
представление изBiMap<K, V>
и себя послеbiMap.inverse().inverse()
. Если с этой проблемой, о которой я упоминал ранее, будет покончено, то, вероятно, будет нечто подобное. -
(РЕДАКТИРОВАТЬ октябрь 2016) Вы также можете использовать новый graph API, который будет присутствовать в Guava 20:
В целом common.graph поддерживает графики следующих разновидностей:
- ориентированные графы
- неориентированные графики
- узлы и / или ребра с соответствующими значениями (веса, метки и т.д.)
- графики, которые допускают / не допускают самоциклы
- графики, которые допускают / не допускают параллельные ребра (графики с параллельными ребрами иногда называют мультиграфами)
- графики, узлы / ребра которых упорядочены по вставке, отсортированы или неупорядочены
Ответ №2:
Что плохого в наличии двух карт, ключ-> значения, values-> ключи?
Комментарии:
1. Я думал, что сохранение двух копий одних и тех же данных будет более подвержено ошибкам. В любом случае, после всех просмотренных мной коллекций я начинаю думать, что это лучшее решение.
2. Просто создайте оболочку для карт, которая поддерживает их синхронизацию.
3. Мне не нравится подход, одобренный этим ответом. В этом есть много потенциально неправильных вещей, включая, возможно, изобретение колеса, написание собственных ошибок по пути, безопасность потоков и т.д.
Ответ №3:
Я надеюсь, что использование MultivaluedMap решит проблему. Пожалуйста, найдите документацию от oracle по ссылке ниже.
http://docs.oracle.com/javaee/6/api/javax/ws/rs/core/MultivaluedMap.html
Комментарии:
1. Это интерфейс. Существуют ли какие-либо реализации?
Ответ №4:
Используя Google Guava, мы можем написать примитивную двумерную карту, как показано ниже.
import java.util.Collection;
import com.google.common.collect.ArrayListMultimap;
import com.google.common.collect.Multimap;
public class BiMultiMap<K,V> {
Multimap<K, V> keyToValue = ArrayListMultimap.create();
Multimap<V, K> valueToKey = ArrayListMultimap.create();
public void putForce(K key, V value) {
keyToValue.put(key, value);
valueToKey.put(value, key);
}
public void put(K key, V value) {
Collection<V> oldValue = keyToValue.get(key);
if ( oldValue.contains(value) == false ) {
keyToValue.put(key, value);
valueToKey.put(value, key);
}
}
public Collection<V> getValue(K key) {
return keyToValue.get(key);
}
public Collection<K> getKey(V value) {
return valueToKey.get(value);
}
@Override
public String toString() {
return "BiMultiMap [keyToValue=" keyToValue ", valueToKey=" valueToKey "]";
}
}
Надеюсь, это поможет некоторым базовым потребностям двунаправленной мультикарты.
Обратите внимание, что K и V должны правильно реализовать метод hascode и equals
Ответ №5:
Надеюсь, я вас правильно понял
class A {
long id;
List<B> bs;
}
class B {
long id;
List<A> as;
}
Комментарии:
1. Вы слишком умны для этого ответа
2. Я даже не могу вспомнить, как отвечал на это 😶
Ответ №6:
Для этих целей я использую реализацию Guava MultiMap от Google.
Map<Key Collection<Values>>
где коллекцией может быть, например, ArrayList. Это позволяет сопоставлять несколько значений, хранящихся в коллекции, с ключом.
Надеюсь, это поможет!