Статьи

Двунаправленные карты Гуавы

Google Guava может многое предложить разработчику Java, работающему с J2SE 5 , Java SE 6 или Java SE 7 . Чем старше их версия, тем полезнее может быть Guava. Хотя Java SE 7 предоставляет некоторые функции, предоставляемые Guava, языку программирования Java в качестве стандартной части языка (например, новый класс Objects ), по-прежнему существует множество функций Guava, которые недоступны даже в JDK 7. В этом посте Я сосредоточен на поддержке двунаправленных карт в Гуаве .

Наследие Гуавы находится в « древнем и не поддерживаемом » проекте Google Collections, и поддержка двунаправленной карты Гуавы происходит из этого наследия Google Collections . Документация API для интерфейса com.google.common.collect.BiMap в Guava предоставляет хорошее краткое определение двунаправленной карты:


Двунаправленная карта (или «двунаправленная карта») — это карта, которая сохраняет уникальность своих значений, а также ключей.
Это ограничение позволяет бимапам поддерживать «инверсное представление», которое является еще одним бимапом, содержащим те же записи, что и этот бимап, но с обращенными ключами и значениями.

В моей карьере я столкнулся с несколькими ситуациями, когда поддержка двунаправленной карты помогает сделать более понятный и читаемый код. Я даже построил пользовательские реализации двунаправленных карт, но больше не делать , что благодаря наличию гуавы (и ранее в Google Коллекции и Apache Commons «ы BidiMap). Следующий листинг кода показывает простую ситуацию, когда двунаправленная карта полезна. Этот пример отображает нации в их столицы. Прелесть двунаправленной карты в том, что я могу искать столицу по названию ее нации или я могу искать по названию нации по названию столицы. Важной характеристикой двунаправленной карты является то, что сторона «значения» двунаправленной карты требует уникальных значений в дополнение к более типичной «ключевой» стороне двунаправленной карты, требующей уникальных значений.

TwoMonoDirectionalMaps

package dustin.examples;

import static java.lang.System.out;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * Demonstrate simplistic implementation of functionality equivalent to that
 * provided by bidirectional map via two monodirectional maps. This class has
 * some intentional problems to illustrate the maintenance disadvantages of this
 * approach. For example, there is a mismatch between the two single-direction
 * maps for "London" (UK in one case and England in the other case) and the
 * mapping for France/Paris was left off one of the single-direction maps.
 * 
 * @author Dustin
 */
public class TwoMonoDirectionalMaps
{
   private final static Map<String, String> nationsToCapitals;
   private final static Map<String, String> capitalsToNations;

   static
   {
      final Map<String, String> tempNationsToCapitals = new HashMap<String, String>();
      tempNationsToCapitals.put("Canada", "Ottawa");
      tempNationsToCapitals.put("England", "London");
      tempNationsToCapitals.put("France", "Paris");
      tempNationsToCapitals.put("Mexico", "Mexico City");
      tempNationsToCapitals.put("Portugal", "Lisbon");
      tempNationsToCapitals.put("Spain", "Madrid");
      tempNationsToCapitals.put("United States", "Washington");
      nationsToCapitals = Collections.unmodifiableMap(tempNationsToCapitals);

      final Map<String, String> tempCapitalsToNations = new HashMap<String, String>();
      tempCapitalsToNations.put("Lisbon", "Portugal");
      tempCapitalsToNations.put("London", "United Kingdom");
      tempCapitalsToNations.put("Madrid", "Spain");
      tempCapitalsToNations.put("Mexico City", "Mexico");
      tempCapitalsToNations.put("Ottawa", "Canada");
      tempCapitalsToNations.put("Washington", "United States");
      capitalsToNations = Collections.unmodifiableMap(tempCapitalsToNations);
   }

   /**
    * Print the capital city of the nation whose name is provided.
    * 
    * @param nationName Name of nation for which capital is decided.
    */
   public void printCapitalOfNation(final String nationName)
   {
      out.println(
           "The capital of " + nationName + " is "
         + (nationsToCapitals.containsKey(nationName) ? nationsToCapitals.get(nationName) : "unknown" )
         + ".");
   }

   /**
    * Print the name of the nation whose capital name is provided.
    * 
    * @param capitalName Name of capital city for which nation is desired.
    */
   public void printNationOfCapital(final String capitalName)
   {
      out.println(
           capitalName + " is the the capital of "
         + (capitalsToNations.containsKey(capitalName) ? capitalsToNations.get(capitalName) : "unknown" )
         + ".");
   }

   /**
    * Main function demonstrating this use of two mono-directional maps.
    * 
    * @param arguments Command-line arguments; none expected.
    */
   public static void main(final String[] arguments)
   {
      final TwoMonoDirectionalMaps me = new TwoMonoDirectionalMaps();
      me.printCapitalOfNation("United States");
      me.printCapitalOfNation("England");
      me.printCapitalOfNation("France");
      me.printNationOfCapital("Washington");
      me.printNationOfCapital("London");
      me.printNationOfCapital("Paris");
   }
}

При выполнении вышеизложенного вывод выглядит так, как показано на следующем снимке экрана. Названия столиц и стран, приведенные в этом примере, предназначены для иллюстрации одной из проблем, связанных с использованием двух однонаправленных карт: они могут быть не синхронизированы. Эта проблема может быть исправлена ​​с помощью двунаправленной карты, как показано в следующем примере кода, в котором используется реализация ImmutableBiMap интерфейса BiMap.

GuavaBiMapDemo

package dustin.examples;

import static java.lang.System.out;
import com.google.common.collect.BiMap;
import com.google.common.collect.ImmutableBiMap;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;

/**
 * Simple demonstration of Google Guava's bidirectional map support.
 * 
 * @author Dustin
 */
public class GuavaBiMapDemo
{
   private final static BiMap<String, String> nationsToCapitals;

   static
   {
      final Map<String, String> tempNationsToCapitals = new HashMap<String, String>();
      tempNationsToCapitals.put("Canada", "Ottawa");
      tempNationsToCapitals.put("England", "London");
      tempNationsToCapitals.put("France", "Paris");
      tempNationsToCapitals.put("Mexico", "Mexico City");
      tempNationsToCapitals.put("Portugal", "Lisbon");
      tempNationsToCapitals.put("Spain", "Madrid");
      tempNationsToCapitals.put("United States", "Washington");
      nationsToCapitals = ImmutableBiMap.copyOf(Collections.unmodifiableMap(tempNationsToCapitals));
   }

   /**
    * Print the capital city of the nation whose name is provided.
    * 
    * @param nationName Name of nation for which capital is decided.
    */
   public void printCapitalOfNation(final String nationName)
   {
      out.println(
           "The capital of " + nationName + " is "
         + (nationsToCapitals.containsKey(nationName) ? nationsToCapitals.get(nationName) : "unknown" )
         + ".");
   }

   /**
    * Print the name of the nation whose capital name is provided.
    * 
    * @param capitalName Name of capital city for which nation is desired.
    */
   public void printNationOfCapital(final String capitalName)
   {
      out.println(
           capitalName + " is the the capital of "
         + (nationsToCapitals.containsValue(capitalName) ? nationsToCapitals.inverse().get(capitalName) : "unknown" )
         + ".");
   }

   /**
    * Main function demonstrating this use of two mono-directional maps.
    * 
    * @param arguments Command-line arguments; none expected.
    */
   public static void main(final String[] arguments)
   {
      final GuavaBiMapDemo me = new GuavaBiMapDemo();
      me.printCapitalOfNation("United States");
      me.printCapitalOfNation("England");
      me.printCapitalOfNation("France");
      me.printNationOfCapital("Washington");
      me.printNationOfCapital("London");
      me.printNationOfCapital("Paris");
   }
}

When the above is executed, the output shows more consistent mappings for London and for Paris/France because there was no need to maintain two separate maps.

Guava’s bidirectional maps provide a safer and more readable approach to implementing data structures that map keys to values and values to key in such a way that one can be accessed via the other. Normal Java Maps only access use of a key to access a value directly, but both directions of access are supported by Guava’s bidirectional maps.

Guava seems to be receiving more attention these days. Google’s guava java: the easy parts was written about one year ago and is a nice introduction to the «easier» portions of Guava. Tom Jefferys‘s September 2011 post Multimaps — Google Guava also provides a nice overview of Guava’s map support with focus on Multimaps followed by separate posts focusing on BiMaps and Multisets. Recent post 5 Reasons to use Guava includes Guava’s map support as one of five reasons that Java developers should embrace Guava. Section 16.9. retrieving a key by value (working with bi-directional maps) of the Java Commons Cookbook (download) also covers Guava’s support for bidirectional maps.

 

From http://marxsoftware.blogspot.com/2011/10/guavas-bidirectional-maps.html