ONJava.com -- The Independent Source for Enterprise Java
oreilly.comSafari Books Online.Conferences.

advertisement

AddThis Social Bookmark Button

Enhance Collection Performance with this Treasure Trove
Pages: 1, 2

Using a Factory to Allow You to Switch Between Trove and JDK Collections

So, we can add Trove collections to our code fairly easily, but wouldn't it be nice to be able to "flick a switch" to have the program use Trove or JDK collections? Let's create a CollectionFactory to allow this to happen.



The trigger that lets the factory know what type of collection to create is a Java System property. If the user sets the property usetrove to true, the Trove classes will be used. If the property isn't set, or doesn't have the value of true, then the JDK classes will be created instead. The user configures this parameter in two ways:

  • Pass in the property to the JVM:

    % java -Dusetrove=true MyProgram.java
  • Create a trove.properties file in the classpath that contains:

    usetrove=true

How do we code a factory that gives us this functionality? Let's walk through it.

Collection Factory Code

The core of the code is a static {} area that looks for the System property (from the properties file or passed in). This check happens only once (hence, it is static), and sets the boolean value useTrove if usetrove is set to true:

public class CollectionFactory {
	static boolean useTrove = false;

	/**
	 *  Try to find the usetrove property on command line, or  
         *  in a trove.properties in the CLASSPATH
	 *  If usetrove is set to true, then set the useTrove 
         *  boolean, else leave it as false
	 */
	static {
	  try {
	    System.getProperties().load( 
      Thread.currentThread().
      getContextClassLoader().
      getResourceAsStream("trove.properties") );

	    useTrove = "true".equalsIgnoreCase( 
         ((String) System.getProperty("usetrove") ).trim() ) 
         ? true : false;
	  } catch (Exception e) {
	    // do nothing.  keep the default: false
	  }
	}

Now, for the base types, we simple check the useTrove boolean, and return the correct class:


        /**
	 *  Return a hashmap based on the properties
	 */
	public static Map getHashMap() {
		if ( useTrove ) return new THashMap();
		else            return new HashMap();
	}

	/**
	 *  Return a hashset based on the properties
	 */
	public static Set getHashSet() {
		if ( useTrove ) return new THashSet();
		else            return new HashSet();
	}

	/**
	 *  Return a linkedlist based on the properties
	 */
	public static List getLinkedList() {
		if ( useTrove ) return new TLinkedList();
		else            return new LinkedList();
	}

And there we have it, a clean way to get access to either a Trove or JDK class. To use this, you would simply do something like:

 Map map = CollectionFactory.getHashMap();
 map.put("name", "dion");
 System.out.println("name = " + map.get("name"));
 System.out.println("Class is: " + map.getClass());

If I put this in a program, I could run it in two ways to test that it is working OK (assuming no trove.properties file exists):

dion@frag [~]> java CollectionFactory
name = dion
Class is: class java.util.HashMap

--------------------------------------------------------------

dion@frag [~]> java -Dusetrove=true CollectionFactory
name = dion
Class is: class gnu.trove.THashMap

Troves Primitive Collections

So far, we are living in a world that is compatible between Trove and the JDK classes. If performance really becomes an issue (based on profiling the code!) we can jump into Trove's world and enhance the collections even more.

When we store primitives in our collections what do we end up doing? We have to wrap them in their Object wrappers (int -> Integer, float -> Float, etc.). What if we know that either the key or value (or both) should always be integers? If this is the case, we can use a special case collection from Trove.

Imagine if we wanted a map containing objects as keys and integers as values. We would use the following code:

TObjectIntHashMap intmap = new TObjectIntHashMap();
intmap.put("NumInStock", 2);
int numInStock = intmap.get("NumInStock");

Let's look at the performance, comparing a JDK map to the ObjectInt map that we see above:

Comparing TObjectIntHashMap to HashMap:
--------------------------------------------------------------
JDK   average (msec): 502
Trove average (msec): 183

Difference: Trove is 2.74 times faster!

If you compare a HashMap that takes two primitives (e.g., key = int, value = int) then you see a really large performance improvement:

Comparing TIntIntHashMap to HashMap:
--------------------------------------------------------------
JDK   average (msec): 502
Trove average (msec): 67

Difference: Trove is 7.5 times faster!

Another side effect of using these primitive collections is that it enforces rules on that collection. HashMaps work with generic java.lang.Object, so people can put in whatever they want. The TintIntHashMap is going to give you a compiler warning if you try to put something naughty in it.

Note: In future versions of Java we will see "Generics" (kind of like C++ templates), where we will be able to create collections that are tied to a given type, and we won't have to do the nasty casting of objects when we take them out, and we will have checking of values that we put in.

Conclusion

If we need to increase performance with our collections, GNU Trove offers a simple solution. We have seen how we can simply use Trove's classes without even really know they are being used. We have also seen how we can use Trove's primitive collections to get another performance boost if really needed. Check it out!

Dion Almaer is a Principal Technologist for The Middleware Company, and Chief Architect of TheServerSide.Com J2EE Community.


Return to ONJava.com.