2011-11-09 2 views
19

Я ищу способ хранения пар ключ-значение. Мне нужно, чтобы поиск был двунаправленным, но в то же время мне нужно сохранить несколько значений для одного и того же ключа. Другими словами, что-то вроде BidiMap, но для каждого ключа может быть несколько значений. Например, он должен иметь возможность удерживать пары, такие как: «s1» -> 1, «s2» -> 1, «s3» -> 2, и мне нужно получить значение, сопоставленное каждому ключу, и для каждого значения, получите все связанные с ним ключи.Двунаправленная многозначная карта в Java

+3

Вы говорите о необходимости иметь несколько значений на ключ, но в вашем примере у вас нет ключа с несколькими значениями, но одно значение с двумя ключами. Вероятно, вы должны это разъяснить. Если ваш пример подходит к вашему вопросу, вы получите ответы на более точные ответы ;-) – pushy

+0

http://www.jguru.com/faq/view.jsp?EID=1317828 здесь вы можете найти, как создать multimap – maks

+0

@pushy, та же проблема, если я отменяю карту и сохраняю целые числа как ключи, а не как значения, я получаю отображение «один ко многим». Во всяком случае, спасибо за исправление. :) –

ответ

19

Значит, вам нужна поддержка отношений «многие ко многим»? Ближе всего вы можете получить GuavaMultimap как @Mechkov написал - но точнее Multimap сочетание с Multimaps.invertFrom. «BiMultimap» еще не реализован, но есть an issue, запрашивающий эту функцию в библиотеке Google Guava.

На данный момент у вас есть несколько вариантов:

  1. Если ваш «BiMultimap» будет непреложной константы - использовать Multimaps.invertFrom и ImmutableMultimap/ImmutableListMultimap/ImmutableSetMultimap (каждый из theese трех имеет различные значения коллекции хранящих). Часть кода (пример взят из приложения I разработки, использует 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)); 
        } 
    } 
    
  2. Если вы действительно хотите, чтобы ваш Multimap быть изменяемым, то это будет трудно поддерживать как K-> V и V-> K, если вы не будете изменять только kToVMultimap и звоните invertFrom каждый раз, когда вы хотите иметь свою инвертированную копию (и сделать эту копию немодифицируемой, чтобы убедиться, что вы случайно не изменяете vToKMultimap, что бы не обновляло kToVMultimap). Это не оптимально, но в этом случае должно быть сделано.

  3. (не ваш случай, вероятно, упомянут в качестве бонуса): BiMap интерфейс и реализации классов имеет .inverse() метод, который дает BiMap<V, K> вид из BiMap<K, V> и себя после biMap.inverse().inverse(). Если this issue, о котором я упомянул ранее, будет, похоже, что-то подобное.

  4. (EDIT октября 2016) Вы также можете использовать new graph API, которые будут присутствовать в Guava 20:

    В целом, общий.График поддерживает графики следующих разновидностей:

    • ориентированные графы
    • неориентированных графов
    • узлов и/или ребер с соответствующими значениями (весами, этикеток и т.д.)
    • графиков, которые делают/не делают позволяют себя петле
    • график, которые делают/не допускают параллельные кромок (графы с параллельными кромками иногда называют мультиграфы)
    • графики, чьи узлы/края вставка упорядоченной, сортируются, или неупорядоченное
-1

Надежда Я вам

class A { 
    long id; 
    List<B> bs; 
} 

class B { 
    long id; 
    List<A> as; 
} 
2

Что плохого в две карты, клавишные> значения с ценностно клавиш вправо,>?

+3

Я думал, что сохранение двух копий одних и тех же данных будет более подверженным ошибкам. В любом случае, после всех коллекций, на которые я смотрел, я начинаю думать, что это лучшее решение. –

+2

Просто создайте оболочку для карт, которые синхронизируют их. – Stefan

+10

Мне не нравится подход, одобренный этим ответом. В этом есть много вещей, которые могут быть ошибочными, в том числе, возможно, изобретать колесо, писать собственные ошибки на этом пути, безопасность потоков и т. Д. – bacar

-3

Реализация Guava MultiMap от Google - это то, что я использую для этих целей.

Map<Key Collection<Values>> 

где Collection может быть ArrayList, например. Он позволяет сопоставить несколько значений, хранящихся в коллекции, с ключом. Надеюсь, это поможет!

+0

Не двунаправленный. – Stefan

1

Использование Google Guava мы можем написать примитивную BiMulitMap, как показано ниже.

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

Смежные вопросы