BidiMap
in Commons Collections provides an
implementation of Map
, which can be reversed if
both the keys and values are unique; you can use a
BidiMap
to retrieve a value for a key or a key for a value. The following
example demonstrates the use of a BidiMap
to
access state names by state abbreviation and state abbreviations by
state names:
BidiMap bidiMap = new DualHashBidiMap( ); bidiMap.put( "il", "Illinois" ); bidiMap.put( "az", "Arizona" ); bidiMap.put( "va", "Virginia" ); // Retrieve the key with a value via the inverse map String vaAbbreviation = bidiMap.inverseBidiMap( ).get( "Virginia" ); // Retrieve the value from the key String illinoisName = bidiMap.get( "il" );
DualHashBidiMap
stores keys and values in two
HashMap
instances. One HashMap
stores keys as keys and values as values, and the other
HashMap
stores the inverse—values as keys
and keys as values.
In Example 5-11, a BidiMap
is used
to store country names and country codes; an application stores ISO
country codes and translates between ISO country codes and country
names to present intelligible
output—“us” is translated to
“United States.” Alternatively,
when a user types in a name of a country, the application needs to be
able to produce the country code for that country
name—“United States” must be
translated back to “us.”
Example 5-11. Storing ISO country codes in a BidiMap
package com.discursive.jccook.collections.bidi; import org.apache.commons.collections.BidiMap; import org.apache.commons.collections.bidimap.DualHashBidiMap; public class BidiMapExample { private BidiMap countryCodes = new DualHashBidiMap( ); public static void main(String[] args) { BidiMapExample example = new BidiMapExample( ); example.start( ); } private void start( ) { populateCountryCodes( ); String countryName = (String) countryCodes.get( "tr" ); System.out.println( "Country Name for code 'tr': " + countryName ); String countryCode = (String) countryCodes.inverseBidiMap( ).get("Uruguay"); System.out.println( "Country Code for name 'Uruguay': " + countryCode ); countryCode = (String) countryCodes.getKey("Ukraine"); System.out.println( "Country Code for name 'Ukraine': " + countryCode ); } private void populateCountryCodes( ) { countryCodes.put("to","Tonga"); countryCodes.put("tr","Turkey"); countryCodes.put("tv","Tuvalu"); countryCodes.put("tz","Tanzania"); countryCodes.put("ua","Ukraine"); countryCodes.put("ug","Uganda"); countryCodes.put("uk","United Kingdom"); countryCodes.put("um","USA Minor Outlying Islands"); countryCodes.put("us","United States"); countryCodes.put("uy","Uruguay"); } }
The previous example makes sense because country codes and country
names are both unique; there is only one entry for
“Djibouti,”
“dj,"and no other country has an
overlapping code because country codes are defined by an
International Organization for Standardization (ISO) standard, ISO
3166. If you attempt to insert a duplicate key in a regular map, the
existing entry with the same key would be replaced by the new value.
In a BidiMap
, if you insert a duplicate value, or
a duplicate key, the entry holding this value is
replaced by a new entry. The following example illustrates this
concept:
private BidiMap bidiMap = new DualHashBidiMap( ); // Insert initial content { "one:"red", "two":"green", "three":"blue" } bidiMap.put("one","red"); bidiMap.put("two","green"); bidiMap.put("three","blue"); // replace "one" key entry bidiMap.put("one","black"); // replace "green" value entry bidiMap.put("five","green"); // Contents are now { "one":"black", "three":"blue", "five":"green" }
Changing key “one,” value
“black” replaces the original key
“one,” value
“red” because the key is
duplicated; this behavior is consistent with a normal implementation
of Map
. The difference in a
BidiMap
is that when adding key
“five,” value
“green” to a
BidiMap
, the previous key
“two,” value
“green” is replaced with a new
entry because “green” is a
duplicate value. A regular Map
simply adds another
entry, and getting the value of either the
“five,” or
“two,” key would return the value
“green.” Because
“green” already occurs as a key in
the inverse map, the entry corresponding to the
“two,” key is removed and replaced
by a new entry. Bidirectional access to keys by value is only
possible if keys and values form two unique sets.
There are three implementations of the BidiMap
interface: DualHashBidiMap
,
DialTreeBidiMap
, and
TreeBidiMap
. A DualHashBidiMap
is the simplest option, storing keys and values in two separate
instances of HashMap
. When a value is requested by
key, one
HashMap
is consulted, and when a key is requested by value, the inverse
HashMap
is consulted. The
DualHashMap
is likely to be your implementation of
choice if it is not important to keep track of the insertion order;
it has a straightforward implementation using the familiar
HashMap
.
If you need to preserve the order of insertion, a
DualTreeBidiMap
uses two separate
TreeMap
instances to hold the regular and inverse
mappings. This implementation implements the
SortedMap
interface that keeps track of the order
of insertion and provides subMap( )
,
headMap( )
, and tailMap( )
methods. A third implementation, TreeBidiMap
,
implements BidiMap
without maintaining two
internal storage maps. Instead, TreeBidiMap
stores
nodes in a red-black tree, identifying each node as both a key and a
value; it costs twice as much to put( )
into a
TreeBidiMap
, but this implementation comes in
handy if you are worried about memory consumption—it does not
need to store each key and value twice in two maps.
This example used ISO 3166 country codes, a list of every country and corresponding two letter country code. If you are writing an application for a worldwide audience, you may find the following list helpful: http://www.iso.ch/iso/en/prods-services/iso3166ma/02iso-3166-code-lists/list-en1.html.
3.21.244.217