Showing posts with label Maps. Show all posts
Showing posts with label Maps. Show all posts

Thursday, November 17, 2011

Case-Insensitive Map


Apache Commons Case-Insensitive Map


Problem
You may have a requirement for fetching values with String keys that will ignore the capitalization of a key when retrieving a value. Say for example to want a Mapping of Currency Names with Currency Code (But the Currency codes should be case insensitive i.e Usd,USD,usD,usd all should fetch me “US Dollar”)

This is useful when an application is requesting a Currency from a user in a form to capture the Payment Details . If a user enters "USD," "Usd," or "usd," you need to be able to return "US Dollar".

Our Own Google.com


Solution to the Problem is ready-to-wear
We can use a CaseInsensitiveMap from the Commons Collections. Let is say Thanks to the developer of Apache Foundation who do this free of “Cost”. This implementation of Map takes String keys and provides case-insensitive access. An entry with a key "USD" can be retrieved with the strings "USD," "Usd," and "USd." Here is a small example demonstrating the case insensitivity:

public static void main(String[] args) {
            CaseInsensitiveMap currencyMapping = new CaseInsensitiveMap();
            currencyMapping.put("USD","US Dollar");
            currencyMapping.put("EuR","Euro");
            currencyMapping.put("gbP","British Pound");
            currencyMapping.put("InR","Indian Rupee");
            currencyMapping.put("AUd","Australian Dollar");
            currencyMapping.put("CAD","Canadian Dollar");
            currencyMapping.put("CHF","Swiss Franc");
            currencyMapping.put("AED","Emirati Dirham");
            currencyMapping.put(1,"Dirty Value");
            System.out.println(currencyMapping.get("USD"));
            System.out.println(currencyMapping.get("usd"));
            System.out.println(currencyMapping.get("INR"));
            System.out.println(currencyMapping.get(1));
           
      }
Output is:
US Dollar        // Because its case insensitive
US Dollar         // Because its case insensitive
Indian Rupee      // Because its case insensitive
Dirty Value       // The map does not stops us from Entering Integer keys  but Output holds good only for String.
Discussion
Above example demonstrates the use of CaseInsensitiveMap to access currency names by currency abbreviations regardless of capitalization. Here is a small TWIST in the below example. For people who understand java well this should be very trivial.

public static void main(String[] args) {
            CaseInsensitiveMap currencyMapping = new CaseInsensitiveMap();
            currencyMapping.put("USD","US Dollar");
            currencyMapping.put("usd","us Dollar");
            currencyMapping.put("UsD","Us Dollar");
            System.out.println(currencyMapping.get("USD"));
            System.out.println(currencyMapping.get("usd"));
            System.out.println(currencyMapping.get("UsD"));
      }

Output is:
Us Dollar //Last Key overwrites the mapping
Us Dollar
Us Dollar

This is due to the fact that CaseInsensitiveMap is a HashMap implementation,before putting any key-value pair in the map it follows the below steps:

    1)      Converts the Key to lower case
    2)      Calls the hashcode on the lower case key
    3)      Now insert the value corresponding to this hashcode.

It performs exactly the same steps while fetching the value from the map. 

    1)      Converts the Key to lower case
    2)      Calls the hashcode on the lower case key
    3)      Fetched the corresponding value stored at this hashcode.

For API Refer below URL

Saturday, November 12, 2011

MultiKey Map

MultiKey Map an Amazing Data Structure

Apache Commons API is just Amazing; it’s worth exploring and if you are not familiar with it try exploring “Google Guava” a much new API. It’s much better than Apache Commons Collections API.
It handles Generics and is thread safety.

Till now you will only know map as a key, value pair. You can get the value by map.get(key). 

Have you ever owned a Locker in a Bank, or have seen big Lockers in Jewellery shops to store diamonds. Those will have multiple keys, here I don’t mean a duplicate key but I want to emphasize that you can unlock them only via a sequence of Key.  In a typical bank locker I key remains with the Bank and the other is given to the Owner. Whenever you visit the bank to take out/keep your valuables the bank official will go along with you and use his key to unlock it. Here the locker is not open yet, after that you have to use your key to unlock the locker. Here this is just done to ensure security. Note that the sequence is very important here.
Multikey Locker

The multi key map also work on the same lines. You can have a set of keys to map a value. Let’s us understand this with below Figure. People who are already aware of Hashed data structures will be able to enjoy this example in a better way.



Syntax of a Normal Java Map
Map map = new HashMap ()// Generics supported
Syntax of a MultikeyMap
MultikeyMap multikeyMap = new MultiKeyMap()// It does not support generics
To understand it you can assume
MultikeyMap multikeyMap  ;
We will be learning more about multyikey map with the below example.
Let us discuss the Example:
We have to fetch the name of the person based on the city and company;
Lets consider the below data

MultiKeyMap multiKeyMap = new MultiKeyMap();
             multiKeyMap.put("New York","IBM","Sam");
             multiKeyMap.put("Sydney","Infosys","Honey");
             multiKeyMap.put("Prague","JP Morgan","Peter");
             multiKeyMap.put("Scotland","RBS","Deny");
             multiKeyMap.put("Paris","Nomura","Lily");
             multiKeyMap.put("Melbourne","Citi Bank","Sandy");
             multiKeyMap.put("Aukland","Bank of America","Tommy");

Values stored in the memory:

Hascode
Multi Keys
Value
11111111
New York,IBM
Sam
22222222
Sydney,Infosys
Honey
33333333
Prague,JP Morgan
Peter
44444444
Scotland,RBS
Deny
55555555
Paris,Nomura
Lily
66666666
Melbourne,Citi Bank
Sandy
77777777
Aukland,Bank of America
Tommy

Now if we say
1)    System.out.println(multiKeyMap.get("Sydney","Infosys"));   
2)    System.out.println(multiKeyMap.get("Paris","Nomura"));
3)    System.out.println(multiKeyMap.get("Nomura","Paris"));
4)    System.out.println(multiKeyMap.get("Sydney","IBM"));


We get the below outputs:
   1)  Honey // match was found
   2)  Lily  // match was found
   3)  null // Because here we reversed the Order of the keys and no hashcode was       found for that multikey mapped
   4)  null // here we gave two different keys and it was not mapped.

Note that MultiKeyMap is not synchronized and is not thread-safe. If you wish to use this map from multiple threads concurrently, you must use appropriate synchronization.
Refer the Apache Commons API for more information






Tuesday, November 8, 2011

Bi-Directional Maps



Playing with Bi-directional maps
Problem Statement
You need to access county via country code and vice versa like
USà United States
United Statesà US

Approach :1

You will create two maps

Map cityCodeMap
Map< cityName, cityCode > cityNameMap
This is definitely not a preferred way.

Approach :2 
We need something like a bidirectional map (We can again give thanks to Apache Foundation)
BidiMap in Apache 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 aBidiMap 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" );
 // Output is va

// Retrieve the value from the key
String illinoisName = bidiMap.get( "il"
 // Output is Illinois

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.
Bi-Directional-Map


Discussion

Very often in an application we may need to store country names and country codes and use them back n forth. Like

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");
    }
}

You can guess the output by now it’s pretty simple:

Country Name for code 'tr': Turkey
Country Code for name 'Uruguay': uy
Country Code for name 'Ukraine': ua




The previous example makes sense because country codes and country names are both unique; 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( );
bidiMap.put("one","red");
bidiMap.put("two","green");
bidiMap.put("three","blue");

System.out.println(bidiMap);
// Output is { "one":"red", "two":"green", "three":"blue" }

// replace "one" key entry
bidiMap.put("one","black");
// replace "green" value entry
bidiMap.put("five","green");

System.out.println(bidiMap);
// Output is { "one":"black", "three":"blue", "five":"green" }

Here we need to observe two very important things
    1)      Inserting a duplicate key, overwrites the original value (Normal implementation of Map)
    2)      Inserting a duplicate value, overwrites its original key (A regular Map simply adds another entry)