Mega Code Archive

 
Categories / Java / Collections Data Structure
 

MultiMap is a Java version of the C++ STL class std

/*   * Licensed to the Apache Software Foundation (ASF) under one  * or more contributor license agreements.  See the NOTICE file  * distributed with this work for additional information  * regarding copyright ownership.  The ASF licenses this file  * to you under the Apache License, Version 2.0 (the  * "License"); you may not use this file except in compliance  * with the License.  You may obtain a copy of the License at  *   * http://www.apache.org/licenses/LICENSE-2.0  *   * Unless required by applicable law or agreed to in writing,  * software distributed under the License is distributed on an  * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY  * KIND, either express or implied.  See the License for the  * specific language governing permissions and limitations  * under the License.  *  */ import java.util.Collection; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.Map; import java.util.Set; /**  *   * MultiMap is a Java version of the C++ STL class std::multimap. It allows   * users to associate more than one value with a unique key. Each key points   * to a collection of values that can be traversed independently of the map.   * The integrity of the map is such that it only protects the key values, not   * the values they point to.  * <br><br>  * All entries into the multimap are (Object, java.util.Collection), where   * each key object must be unique.  *   * @author Dan Jemiolo (danj)  *  */   public final class MultiMap extends HashMap {     private static final long serialVersionUID = -9145022530624375133L;     //     // The Collection class that holds the values     //   private Class _collectionClass = null;          //     // The number of KEYS in the map - we need to track this ourselves      // because size() is overridden to return the number of VALUES (the      // number of values is the sum of Collection.size()).     //     private int _keySize = 0;        /**    *     * The default constructor creates a new map using a HashSet as the     * value collection type.      *       * @see MultiMap#MultiMap(Class)    *       */   public MultiMap()   {     this(HashSet.class);   }        /**    *    * @param collectionClass      *        The Collection type to use for the map's values. The Class       *        must be an implementation of java.util.Collection.    *      */   public MultiMap(Class collectionClass)   {         _collectionClass = collectionClass;   }          public final void clear()     {         super.clear();         _keySize = 0;     }      /**      *       * @param value      *     * @return True if one or more of the keys in this map points to the       *         given value.      *       */   public final boolean containsValue(Object value)   {     Iterator i = values().iterator();          //     // look through each map entry's set of values to see if      // the given value is present     //     while (i.hasNext())     {       Collection values = (Collection)i.next();              if (values.contains(value))         return true;           }          return false;   }          /**      *       * @return The number of keys in the map, <b>not</b> the number of       *         entries.      *       * @see #size()      *      */     public final int keySetSize()     {         return _keySize;     }        /**    *    * Associates the value with the given key. If the key is not already in       * the map, it is added; otherwise, the value is simply added to the       * key's collection.      *       * @return The value reference on success or null if the value was       *         already associated with the key.    *      */   public final Object put(Object key, Object value)   {     Collection values = (Collection)get(key);          //     // if the key wasn't found - add it!     //     if (values == null)     {       values = (Collection)newInstance(_collectionClass);             super.put(key, values);             ++_keySize;     }          //     // try to add the value to the key's set     //     boolean success = values.add(value);     return success ? value : null;   }   /**    *     * Invokes the Class.newInstance() method on the given Class.    *     * @param theClass    *            The type to instantiate.    *     * @return An object of the given type, created with the default    *         constructor. A RuntimeException is thrown if the object could not    *         be created.    *     */   public static Object newInstance(Class theClass)   {       try       {           return theClass.newInstance();       }       catch (InstantiationException error)       {           Object[] filler = { theClass };           String message = "ObjectCreationFailed";           throw new RuntimeException(message);       }       catch (IllegalAccessException error)       {           Object[] filler = { theClass };           String message = "DefaultConstructorHidden";           throw new RuntimeException(message);       }   }     /**    *    * Adds all of the entries in a generic map to the multimap.    * <br><br>    * NOTE: Because we cannot guarantee that the implementation of the     * base class putAll(Map) simply calls the put method in a loop, we must     * override it here to ensure this happens. Otherwise, HashMap might     * associate the values directly with the keys, breaking the unofficial     * key -> Collection system.      *       * @param map      *        The Map to copy - this does not have to be another MultiMap.    *       */   public final void putAll(Map map)   {     Set keys = map.keySet();     Iterator i = keys.iterator();          //     // if the argument is a multi-map, we want to add all of the      // values individually, otherwise each key will have a set      // of values whose only value is... the collection of values.     // that is, instead of key -> collection<value>, we'd have      // key -> collection<collection<value>>     //     if (map instanceof MultiMap)     {       while (i.hasNext())       {         Object key = i.next();         Object value = map.get(key);                  Collection setOfValues = (Collection)value;         Iterator j = setOfValues.iterator();                          while (j.hasNext())           put(key, j.next());        }     }          //     // it's a "normal" map - just add the name-value pairs     //     else     {       while (i.hasNext())       {         Object key = i.next();         put(key, map.get(key));       }     }   }          /**      *       * Removes <b>all</b> values associated with the given key.      *       * @param key      *       * @return The Collection of values previously associated with the key.      *       */     public final Object remove(Object key)     {         Object value = super.remove(key);         --_keySize;         return value;     }        /**    *    * NOTE: This method takes O(n) time, where n is the number of keys in     * the map. It would be more efficient to keep a counter for the size,     * but this would require overriding more methods and dealing with the     * complicated issue of map integrity and entrySet(). This implementation     * is the most robust when you consider that all Maps allow users to     * modify their contents without using the interface directly.    *      * @return The sum of the sizes of all the map entries (value collections).      *       */   public final int size()   {     Iterator i = values().iterator();     int count = 0;          //     // for each key, add the number of values to the count      //     while (i.hasNext())     {       Collection values = (Collection)i.next();       count += values.size();     }          return count;   } }