MultiMap is a Java version of the C++ STL class std::multimap : Customized Map « Collections Data Structure « Java






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

    
/* 
 * 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;
  }
}

   
    
    
    
  








Related examples in the same category

1.Ordered Map
2.Case Insensitive Map
3.A Map collection with real-time behavior
4.Cache Map
5.Map implementation Optimized for Strings keys
6.An integer hashmap
7.An IdentityMap that uses reference-equality instead of object-equality
8.Int Object HashMap
9.Concurrent Skip List Map
10.A hash map that uses primitive ints for the key rather than objects.
11.Integer Map
12.Copy On Write Map
13.Expiring Map
14.Array Map
15.Int Object HashMap (from CERN)
16.Int HashMap from jodd.org
17.String Map
18.List Map
19.Map using Locale objects as keys
20.Map with keys iterated in insertion order
21.Most Recently Used Map
22.Multi Map
23.Object Int Map
24.Sequenced HashMap
25.Int Int Map
26.Int Object Map
27.Identity HashMap
28.A java.util.Map interface which can only hold a single object
29.A multi valued Map
30.A simple hashmap from keys to integers
31.A memory-efficient hash map.
32.An implementation of the java.util.Map interface which can only hold a single object.
33.Utility methods for operating on memory-efficient maps.
34.CaseBlindHashMap - a HashMap extension, using Strings as key values.
35.A fixed size map implementation.
36.Int HashMap
37.IntMap provides a simple hashmap from keys to integers
38.Complex Key HashMap
39.A Map with multiple values for a key
40.A Map that accepts int or Integer keys only
41.A Map where keys are compared by object identity, rather than equals()
42.Type-safe Map, from char array to String value
43.A hashtable-based Map implementation with soft keys
44.List ordered map
45.Hash map using String values as keys mapped to primitive int values.
46.Lookup table that stores a list of strings
47.HashNMap stores multiple values by a single key value. Values can be retrieved using a direct query or by creating an enumeration over the stored elements.
48.Combines multiple values to form a single composite key. MultiKey can often be used as an alternative to nested maps.