Hashtable question

Discussion in 'Java' started by Nathan Bragg, May 11, 2004.

  1. Nathan Bragg

    Nathan Bragg Guest

    Is there a way using a Hashtable, or some other object to give the
    object a value and return all the keys that have that value? So if I
    have something like:

    hashtable.put("A","1");
    hashtable.put("B","1");
    hashtable.put("C","1");
    hashtable.put("D","2");

    I could pass in "1" and it would return a Vector or Array with
    "A","B","C" in it? Thanks for any help in advance.

    - Nathan Bragg
    Nathan Bragg, May 11, 2004
    #1
    1. Advertising

  2. Nathan Bragg wrote:

    > Is there a way using a Hashtable, or some other object to give the
    > object a value and return all the keys that have that value? So if I
    > have something like:
    >
    > hashtable.put("A","1");
    > hashtable.put("B","1");
    > hashtable.put("C","1");
    > hashtable.put("D","2");
    >
    > I could pass in "1" and it would return a Vector or Array with
    > "A","B","C" in it? Thanks for any help in advance.


    Uh...

    switch "key" and "value" in your description, and
    org.apache.commons.collections.MultiHashMap does exactly what you want.
    Michael Borgwardt, May 11, 2004
    #2
    1. Advertising

  3. On 11 May 2004 09:03:00 -0700, Nathan Bragg wrote:

    > Is there a way using a Hashtable, or some other object to give the
    > object a value and return all the keys that have that value? So if I
    > have something like:
    >
    > hashtable.put("A","1");
    > hashtable.put("B","1");
    > hashtable.put("C","1");
    > hashtable.put("D","2");
    >
    > I could pass in "1" and it would return a Vector or Array with
    > "A","B","C" in it? Thanks for any help in advance.


    Nothing like that exists with the standard-classes being
    provided with the collection-framework. But you easily
    can implement your own Map that way with extending from
    HashMap and overwriting put/remove:

    public Object put(Object key, Object o){
    super.put(key, o);
    List l = (List) myValMap.get(o);
    if (l == null){
    l = new LinkedList();
    myValMap.put(o, l);
    }
    l.add(key);
    }

    This is just to be regarded as hint. You have to
    cope with changing and removing values.


    Regards, Lothar
    --
    Lothar Kimmeringer E-Mail:
    PGP-encrypted mails preferred (Key-ID: 0x8BC3CD81)

    Always remember: The answer is forty-two, there can only be wrong
    questions!
    Lothar Kimmeringer, May 11, 2004
    #3
  4. Nathan Bragg

    Roedy Green Guest

    On 11 May 2004 09:03:00 -0700, (Nathan Bragg)
    wrote or quoted :

    >hashtable.put("A","1");
    >hashtable.put("B","1");
    >hashtable.put("C","1");
    >hashtable.put("D","2");


    you would need to create a second Hashtable with the value as key.
    Hashtable won't let you have duplicate keys, you so you need to create
    a array[] object when there are dups, and keep replacing it with a
    bigger array every time. You could use ArrayLists as your values.

    Here is some code that does that sort of thing:

    package com.mindprod.replicator;

    import java.io.BufferedReader;
    import java.io.FileInputStream;
    import java.io.IOException;
    import java.io.InputStream;
    import java.io.InputStreamReader;
    import java.util.Enumeration;
    import java.util.Hashtable;
    import java.util.regex.Pattern;

    /**
    * This class is similar to java.util.Properties.
    * The properties file has a similar key=value format.
    * However, for MultiProperties,
    * spaces in the values are not coded
    * as "\ ", just as plain " ".
    * Values can be a tuple separated by commas.
    * The inherited Hashtable get returns a String[].
    * There is no support for extended chars such as \n.
    * There is no support for \ as a continuation character.
    * Comment lines begin with #.
    * Blank lines are ignored.
    * The file must end with a line separator.
    * All values are trimmed of leading and trailing spaces.
    * All Strings are interned, so they behave just like
    * hard-coded String static finals.
    *
    * It is basically just a Hashtable with a load method
    * and a few conveniences added to the get method.
    *
    * @author Roedy Green
    * @version 1.0
    * @since 2003-08-03
    */
    public class MultiProperties extends Hashtable
    {

    /**
    * true if you want the debugging harness
    */
    private static final boolean DEBUG=false;

    /**
    * Constructs a new, empty hashtable with
    * the specified initial
    * capacity and the specified load factor.
    * See http://mindprod.com/jgloss/hashtable.html
    * for a full description of
    * what the initialCapacity and loadFactors mean.
    *
    * @param initialCapacity the initial capacity of the
    hashtable.
    * @param loadFactor the load factor of the hashtable.
    * @exception IllegalArgumentException if the initial capacity is
    less
    * than zero, or if the load factor is nonpositive.
    */
    public MultiProperties ( int initialCapacity, float loadFactor )
    {
    super( initialCapacity, loadFactor );
    }

    /**
    * Load the properties hashtable
    * from a text file of key=value pairs.
    *
    * @param in where to load the textual key=value pairs from.
    * @exception IOException
    */
    public void load ( InputStream in ) throws IOException
    {
    BufferedReader br = new BufferedReader( new InputStreamReader(
    in ) );

    while ( true )
    {
    String line = br.readLine();
    if ( line == null )
    {
    /* eof */
    break;
    }
    if ( line.startsWith( "#" ) )
    {
    /* ignore comments */
    continue;
    }
    line = line.trim();
    if ( line.length() == 0 )
    {
    /* ignore blank lines */
    continue;
    }

    // split line into key and value
    String[] keyValue = keyValueSplitter.split( line );
    switch ( keyValue.length )
    {
    case 1:
    {
    // key=nothing
    String key = keyValue[0].trim().intern();
    if ( key.length() == 0 )
    {
    complain( line );
    }
    this.put( key, dummy );
    }
    break;

    case 2:
    {
    // key=value
    String key = keyValue[0].trim().intern();
    if ( key.length() == 0 )
    {
    complain( line );
    }
    // Split value into subfields
    String[] values = subFieldSplitter.split(
    keyValue[1].trim() );

    // trim the multiple values
    for ( int i=0; i<values.length; i++ )
    {
    values = values.trim().intern();
    }
    // save in the underlying Hashtable.
    this.put ( key, values );
    }
    break;

    default:
    case 0:
    complain( line );
    }
    } // end while
    br.close();
    } // end load

    /**
    * Complain about malformed data.
    *
    * @param line Line of key=value that has a problem.
    */
    private static void complain ( String line )
    {
    throw new IllegalArgumentException( "MultiProperties: malformed
    key=value : "
    + line );
    }

    /**
    * Get values associated with key.
    *
    * @param key Key, case sensitive.
    *
    * @return array of associated Strings, possibly dimension 0.
    * If key is undefined returns empty array, not null.
    */
    public String[] getMultiple ( String key )
    {
    String[] value = (String[]) get( key );
    if ( value == null )
    {
    return dummy;
    }
    else
    {
    return value;
    }
    }

    /**
    * Get value associated with key.
    *
    * @param key Key, case sensitive.
    * @param defaultValue
    * Value for the default if the key is not defined.
    * key=nothing returns "", not the default value.
    * @return String for a single value, or the first of a set of
    multiple values, or "".
    */
    public String get ( String key, String defaultValue )
    {
    Object value = get( key );
    if ( value == null )
    {
    return defaultValue.intern();
    }
    else
    {
    String[] array = ((String[])value);
    if ( array.length != 0 )
    {
    return array[0];
    }
    else
    {
    return ""; // not defaultValue!
    }
    }
    }

    /**
    * Get single integer value associated with key.
    *
    * @param key Key, case sensitive.
    * @param defaultValue
    * Value for the default if the key is not defined.
    * @return integer value of the key, or defaultValue if not defined
    or if key=
    * @exception NumberFormatException
    * if the value is not a valid integer.
    */
    public int getInt ( String key, int defaultValue ) throws
    NumberFormatException
    {
    Object value = get( key );
    if ( value == null )
    {
    return defaultValue;
    }
    else
    {
    String[] array = ((String[])value);
    if ( array.length != 0 )
    {
    return Integer.parseInt ( array[0] );
    }
    else
    {
    return defaultValue;
    }
    }

    }

    /**
    * Get boolean value associated with key.
    * Valid values for key are true, false, yes, no, case insensitive.
    *
    * @param key Key, case sensitive.
    * @param defaultValue
    * Value for the default if the key is not defined.
    * @return boolean value of the key, or defaultValue if not defined
    or if key=
    */
    public boolean getBoolean ( String key, boolean defaultValue )
    {
    Object value = get( key );
    if ( value == null )
    {
    return defaultValue;
    }
    else
    {
    String[] array = ((String[])value);
    if ( array.length != 0 )
    {
    return array[0].equalsIgnoreCase( "true" ) ||
    array[0].equalsIgnoreCase( "yes" );
    }
    else
    {
    return defaultValue;
    }
    }

    }

    /**
    * A dummy empty array of Strings.
    * No point is allocating a fresh one every time it is needed.
    */
    private static String[] dummy = new String[ 0 ];

    // Pattern to split line into key and value at the =
    private static Pattern keyValueSplitter = Pattern.compile ( "=" );

    /**
    * Pattern to split into words separated by commas.
    * Two commas in a row in the String to be matched gives an empty
    field
    */
    private static Pattern subFieldSplitter = Pattern.compile ( "," );

    /**
    * test harness
    *
    * @param args not used
    */
    public static void main ( String[] args )
    {
    if ( DEBUG )
    {
    MultiProperties m = new MultiProperties ( 100, .75f );
    try
    {
    m.load( new FileInputStream ( "replicator.properties" ) );
    }
    catch ( IOException e )
    {
    Persist.fatal( "Unable to read replicator.properties
    file."
    + "\n"
    + e.getMessage() );
    }
    for ( Enumeration e = m.keys(); e.hasMoreElements(); )
    {
    String key = (String) e.nextElement();
    String[] values = m.getMultiple( key );
    System.out.println( key + " =");
    for ( int i=0; i<values.length; i++ )
    {
    System.out.println ( values );
    }
    } // end for
    } // end if DEBUG
    } // end main
    } // end MultiProperties


    --
    Canadian Mind Products, Roedy Green.
    Coaching, problem solving, economical contract programming.
    See http://mindprod.com/jgloss/jgloss.html for The Java Glossary.
    Roedy Green, May 11, 2004
    #4
  5. Nathan Bragg

    Roedy Green Guest

    On Tue, 11 May 2004 20:59:09 GMT, Roedy Green
    <> wrote or quoted :

    >Here is some code that does that sort of thing:

    here is some more
    // com.mindprod.pim.PIMEngine.java

    package com.mindprod.pim;
    import java.io.BufferedInputStream;
    import java.io.BufferedOutputStream;
    import java.io.File;
    import java.io.FileInputStream;
    import java.io.FileNotFoundException;
    import java.io.FileOutputStream;
    import java.io.IOException;
    import java.io_ObjectInputStream;
    import java.io_ObjectOutputStream;
    import java.io_OptionalDataException;
    import java.util.Arrays;
    import java.util.Collection;
    import java.util.Iterator;
    import java.util.TreeMap;

    /**
    * Engine for a ram-based Personal Information Manager.
    * Internally it has a HashMap of Items and a TreeMap of Keys.
    * Each key can point directly to several Items, and each Item can
    point to several keys.
    * @author copyright (c) 1999-2004 Roedy Green, Canadian Mind
    Products
    * may be copied and used freely for any purpose but military.
    *
    * Roedy Green
    * Canadian Mind Products
    * #327 - 964 Heywood Avenue
    * Victoria, BC Canada V8V 2Y5
    * tel: (250) 361-9093
    * mailto:
    * http://mindprod.com
    *
    * version 1.0 1999 October 12 8:30 PM -- 9:30 PM
    * version 1.1 1999 October 13 - 9:30 PM -- 12:20 PM
    * - clean compile of code, no testing.
    * - no pickle
    * version 1.2 1999 October 13 - 9:10 AM -- 10:10 AM
    * - add find all strings for item
    * - add sort to keep Keys per item in
    order
    * - remove requirement for makeUnique.
    * - get rid of compiler errors
    * - deal with null initial case in
    addKey and addItem
    * version 1.3 1999 October 13 - 10:40 AM -- 11:15 AM
    * - make allKeys use String -> key
    consistently.
    * - crude test of add/display functions.
    * version 1.4 1999 October 13 - 2:20 PM -- 3:20 PM 7:00 PM -- 8:00
    PM
    * - add pickle/reconstitute
    * - only sort keyList just prior to
    display to save overhead
    * of repeated sorts during
    reconstitution.
    * - change constructor to take String
    instead of File.
    * - add showAll debug tool
    * - clone rather than give access to
    internal array in find.
    *
    */
    public class PIMEngine implements java.io.Serializable
    {

    /**
    * public constructor.
    * @param modelName is a filename less the .pim extension where the
    PIM data are stored,
    * e.g. "C:\\myDir\\plays"
    */
    public PIMEngine(String modelName) throws IOException,
    FileNotFoundException {
    this.modelName = modelName;
    this.load();
    } // end constructor

    /**
    * load a previously saved pickled version of the entire data
    structure.
    */
    public void load() throws IOException {
    File current = new File(modelName + ".pim");
    if ( current.exists() )
    {
    // O P E N
    try
    {
    FileInputStream fis = new FileInputStream(current);
    BufferedInputStream bis = new BufferedInputStream(fis,
    4096 /* buffsize */);
    ObjectInputStream ois = new ObjectInputStream(bis);
    // R E A D
    // reconstitute fields individually rather than
    // "this" because of readObject/writeObject asymmetry
    allKeys = (TreeMap) ois.readObject();
    // restore transient fields, namely the allItems HashMap
    and the
    // Item.keyList[] back pointers.
    allItems = new java.util.HashMap();
    // enumerate all the Key objects in the TreeMap
    Collection keys = allKeys.values();
    Iterator eachKey = keys.iterator();
    while ( eachKey.hasNext() )
    {
    Key key = (Key)eachKey.next();
    int numItems = key.itemList.length;
    for ( int i=0; i<numItems; i++ )
    {
    // itemList is already unique, we use makeUnique
    for the side effect
    // of adding the item to the allItems HashMap
    Item item = makeUnique(key.itemList);
    // build item.keyList[] back pointer
    item.addKey(key);
    } // end for
    } // end while
    // C L O S E
    ois.close();
    }
    catch ( ClassNotFoundException e )
    {
    throw new IOException("PIM file corrupt");
    }
    catch ( OptionalDataException e )
    {
    throw new IOException("PIM file corrupt");
    }
    catch ( IOException e )
    {
    throw new IOException("PIM file corrupt");
    }
    }
    else
    {
    // starting from scratch
    allKeys = new java.util.TreeMap();
    allItems = new java.util.HashMap();
    }
    } // end load

    /**
    * save the entire data structure in pickled form in a file.
    * @param File to store program state in pickled form.
    */
    public void save() throws IOException {
    // rename old, as .old save as .pim
    File prev = new File(modelName + ".old");
    File current = new File(modelName + ".pim");;
    prev.delete();
    current.renameTo(prev);
    // O P E N
    FileOutputStream fos = new FileOutputStream(current);
    BufferedOutputStream bos = new BufferedOutputStream(fos, 4096 /*
    buffsize */);
    ObjectOutputStream oos = new ObjectOutputStream(bos);
    // W R I T E
    // pickle fields individually rather than
    // "this" because of readObject/writeObject asymmetry
    // Write out the allKeys TreeMap but leave behind the allItems
    HashMap
    // Also leave out the Key[] Item.KeyList back pointers.
    oos.writeObject(allKeys);
    // C L O S E
    oos.close();
    } // end save

    /**
    * create an association between the given key and the given item.
    * Items may have many associated keys, and keys may associate to
    many items.
    * @param keystring key for the association
    * @param item data object derived from Item class.
    */
    public void add(String keyString, Item item)
    {
    Item uniqueItem = makeUnique(item);
    Key key = lookup(keyString);
    if ( key == null )
    {
    key = new Key(keyString);
    allKeys.put(keyString, key);
    }
    key.addItem(uniqueItem);
    uniqueItem.addKey(key);
    } // end add

    /**
    * create an association between the given list of keys and the
    given item.
    * Items may have many associated keys, and keys may associate to
    many items.
    * @param keystring list of keys for the association
    * @param item data object derived from Item class.
    */
    public void add(String[] keyString, Item item)
    {
    Item uniqueItem = makeUnique(item);
    int numKeyStrings = keyString.length;
    for ( int i=0; i<numKeyStrings; i++ )
    {
    add(keyString, uniqueItem);
    } // end for
    } // end add

    /**
    * Break an existing association between a key and an item.
    * This will not destroy either the key or the item, though
    * it will remove references to them, which may eventually lead to
    * them being garbage collected.
    * It is not considered an error to remove a pair that has not
    been previously added.
    * @param keystring key for the association.
    * @param item data object derived from Item class.
    */
    public void remove(String keyString, Item item)
    {
    Item uniqueItem = makeUnique(item);
    Key key = lookup(keyString);
    if ( key == null )
    {
    // was no such keyString
    return;
    }
    key.removeItem(uniqueItem);
    if ( key.itemList == null )
    {
    allKeys.remove(key.keyString);
    }
    uniqueItem.removeKey(key);
    if ( uniqueItem.keyList == null )
    {
    allItems.remove(uniqueItem);
    }
    } // end remove

    /**
    * Break an existing association between a list of keys and an
    item.
    * This will not destroy either the key or the item, though
    * it will remove references to them, which may eventually lead to
    * them being garbage collected.
    * It is not considered an error to remove a pair that has not
    been previously added.
    * @param keystring list of keys for the association.
    * @param item data object derived from Item class.
    */
    public void remove(String[] keyString, Item item)
    {
    Item uniqueItem = makeUnique(item);
    int numKeyStrings = keyString.length;
    for ( int i=0; i<numKeyStrings; i++ )
    {
    remove(keyString, uniqueItem);
    }
    } // end remove

    /**
    * Break all existing associations between the given key and any
    items.
    * This will not destroy either the key or the item, though
    * it will remove references to them, which may eventually lead to
    * them being garbage collected.
    * It is not considered an error to remove a key that has not been
    previously added.
    * @param keystring key to remove.
    */
    public void remove(String keyString)
    {
    Key key = lookup(keyString);
    if ( key == null )
    {
    // was no such keyString
    return;
    }
    int numItems = key.itemList.length;
    for ( int i=0; i<numItems; i++ )
    {
    Item item = key.itemList;
    item.removeKey(key);
    if ( item.keyList == null )
    {
    allItems.remove(item);
    }
    } // end for
    key.itemList = null;
    allKeys.remove(key.keyString);
    } // end remove

    /**
    * Break all existing associations between the given item and all
    associated keys.
    * This will not destroy either the key or the item, though
    * it will remove references to them, which may eventually lead to
    * them being garbage collected.
    * It is not considered an error to remove a key that has not been
    previously added.
    * @param item data object derived from Item class.
    */
    public void remove(Item item)
    {
    Item uniqueItem = makeUnique(item);
    int numKeys = uniqueItem.keyList.length;
    for ( int i=0; i<numKeys; i++ )
    {
    Key key = uniqueItem.keyList;
    key.removeItem(uniqueItem);
    if ( key.itemList == null )
    {
    allKeys.remove(key.keyString);
    }
    } // end for
    uniqueItem.keyList = null;
    allItems.remove(uniqueItem);
    } // end remove

    /**
    * Find all items associated with the given key.
    * @param keyString the string to lookup by.
    */
    public Item[] find(String keyString)
    {
    Key key = lookup(keyString);
    if ( key == null )
    {
    System.out.println("lookup fail");
    return null;
    }
    Item[] itemList = key.itemList;
    if ( itemList == null )
    {
    return null;
    }
    int numItems = itemList.length;
    // can't use Item[].clone because it is protected.
    Item[] clonedItemList = new Item[numItems];
    System.arraycopy(itemList, 0, clonedItemList, 0, numItems);
    return clonedItemList;
    } // end find

    /**
    * Find all KeyStrings associated with the given item
    * @param item data object derived from Item class.
    * @return array of Strings representing associated keys.
    */
    public String[] find(Item item)
    {
    Item uniqueItem = makeUnique(item);
    Key[] keyList = uniqueItem.keyList;
    if ( keyList == null )
    {
    return null;
    }
    // put in alphabetical order, preparatory to display
    Arrays.sort(keyList);
    int numKeyStrings = keyList.length;
    String[] keyStringList = new String[numKeyStrings];
    for ( int i=0; i<numKeyStrings; i++ )
    {
    keyStringList = keyList.keyString;
    }
    return keyStringList;
    } // end find

    /**
    * Ensure the Item is unique by returning a reference to the master
    version which
    * already lives in the allKeys HashTable. If the Item has never
    been seen before,
    * it is added to the HashTable.
    * @param item data object derived from Item class.
    */
    public Item makeUnique(Item item)
    {
    Item uniqueItem = (Item) allItems.get(item);
    if ( uniqueItem != null )
    {
    return uniqueItem;
    }
    uniqueItem = item;
    allItems.put(uniqueItem, uniqueItem);
    return uniqueItem;
    }

    /**
    * lookup the given Keystring to find the associated Key object.
    * @param keyString the lookup string.
    * @return Key object, or null if not found.
    */
    private Key lookup(String keyString)
    {
    return(Key) allKeys.get(keyString);
    } // end lookup

    /**
    * Lookup structure keys -> Items.
    * Access either directy by key or by alphabetic order.
    * indexed by KeyString, gives Key, which then points to Items.
    */
    protected java.util.TreeMap allKeys;

    /**
    * Lookup structure data contents -> Item.
    * Only used internally. Helps prevent duplicate Items.
    * indexed by Item, gives Item, which then points to assocated Keys
    which then give keyStrings.
    */
    transient protected java.util.HashMap allItems;

    /**
    * the filename, without .pim extension where the model is stored,
    in pickled form.
    */
    transient protected String modelName;

    /**
    * debugging tool that displays all the Key and Item objects.
    */
    public void showAll()
    {
    if ( true )
    {
    if ( allKeys != null )
    {
    System.out.println("BY KEY");
    Collection keys = allKeys.values();
    Iterator eachKey = keys.iterator();
    while ( eachKey.hasNext() )
    {
    Key key = (Key)eachKey.next();
    System.out.println("key " + key.keyString);
    if ( key.itemList != null )
    {
    int numItems = key.itemList.length;
    for ( int i=0; i<numItems; i++ )
    {
    TextItem item = (TextItem) key.itemList;
    System.out.println(" item " + item.getText());
    } // end for
    } // if
    } // end while
    } // end if allKeys
    if ( allItems != null )
    {

    System.out.println("BY ITEM");
    Collection items = allItems.values();
    Iterator eachItem = items.iterator();
    while ( eachItem.hasNext() )
    {
    TextItem item = (TextItem)eachItem.next();
    System.out.println("item " + item.getText());
    String [] keyStrings = find(item);
    if ( keyStrings != null )
    {
    int numKeyStrings = keyStrings.length;
    for ( int j=0; j<numKeyStrings; j++ )
    {
    System.out.println(" key " + keyStrings[j]);
    } // end for
    } // end if
    } // end while
    } // end if allItems
    } // end if

    } // end showAll

    public static void main (String[] args)
    {
    try
    {
    PIMEngine p = new PIMEngine("C:\\temp\\temp1");
    Item t1 = new TextItem("Love's Labour Lost");
    Item t2 = new TextItem("The Tempest");
    Item t3 = new TextItem("Macbeth");
    Item t4 = new TextItem("The Tempest");
    Item t5 = new TextItem("Duncan the Wonder Dog");
    p.add("Duncan",t3);
    p.add("Duncan",t5);
    p.add("Banquo",t3);
    p.add("Ariel",t4);
    System.out.println("corresponding keys to item Macbeth");
    String[] foundKeyStrings = p.find(t3);
    if ( foundKeyStrings != null )
    {
    for ( int i=0; i<foundKeyStrings.length; i++ )
    {
    System.out.println(foundKeyStrings);
    }
    }

    System.out.println("corresponding items to key Duncan");
    Item[] foundItems = p.find("Duncan");
    if ( foundItems != null )
    {
    for ( int i=0; i<foundItems.length; i++ )
    {

    System.out.println(((TextItem)foundItems).getText());
    }
    }
    p.showAll();
    p.save();
    }
    catch ( IOException e )
    {
    System.out.println(e);
    }

    } // end main

    } // end class PIMEngine

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

    // com.mindprod.pim.Item.java
    package com.mindprod.pim;

    /**
    * Base class to represent an data item in the PIM. It might
    represent a
    * URL, the name of file, a hunk of text, a GIF, etc. Different items
    types
    * would hold different types of data. All Item types are derived
    from this
    * base class.
    */
    public abstract class Item implements java.io.Serializable
    {
    /**
    * List of associated Key objects, not key Strings.
    * Put in alphabetical order, just prior to display, but otherwise
    we allow it to
    * be in any order.
    * Array is compact even if a bit clumsy to add and remove an
    element.
    * We don't include this in the pickle, because the recursion could
    overflow
    * the stack. PIMEngine.load reconstitutes these, not
    Item.readObject.
    */
    transient protected Key[] keyList;

    /**
    * add given key to the keylist if it is not already there.
    */
    protected void addKey(Key key)
    {
    int len;
    if ( keyList == null )
    {
    len = 0;
    }
    else
    {
    len = keyList.length;
    }
    for ( int i=0; i<len; i++ )
    {
    // since unique, can use == instead of equals.
    if ( key == keyList )
    {
    // already in list
    return;
    }
    } // end for
    // grow the array to make room to add item at the end
    Key[] newKeyList = new Key[len+1];
    if ( len > 0 )
    {
    System.arraycopy(keyList, 0, newKeyList, 0, len);
    }
    newKeyList[len] = key;
    // allow to get out of alphabetical order
    keyList = newKeyList;
    } // end addKey

    /**
    * remove given key from the keylist.
    * If it is already removed, that's ok.
    */
    protected void removeKey(Key key)
    {
    int len = keyList.length;
    for ( int i=0; i<len; i++ )
    {
    // since unique, can use == instead of equals.
    if ( key == keyList )
    {
    // found in list
    if ( len == 1 )
    {
    keyList = null;
    return;
    }
    // shrink the array to remove ith element
    Key[] newKeyList = new Key[len-1];
    // copy elts below i, if any
    if ( i > 0 )
    {
    System.arraycopy(keyList, 0, newKeyList, 0, i);
    }
    // copy elts above i, if any
    int upper = len-i;
    if ( upper > 0 )
    {
    System.arraycopy(keyList, i+1, newKeyList, i, upper);
    }
    keyList = newKeyList;
    // won't disturb sort order
    return;
    } // end if
    } // end for
    // not in list, nothing to do
    } // end removeKey

    /**
    * Returns a hashcode based solely on the data contents of the
    Item, not the keylist.
    *
    * @return a hash code value for this object.
    */
    public abstract int hashCode();

    /**
    * Compares two Items, basing equality on the data contents of the
    Item, not the keylist.
    * @param anObject other Item to compare to.
    * @return true if the Items are equal. Return false if anObject is
    not of the same type.
    */
    public abstract boolean equals(Object anObject);

    } // end class Item

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

    // com.mindprod.pim.Key.java
    package com.mindprod.pim;

    /**
    * The Key our lookup engine works with in the TreeMap. It points to
    several Items.
    */
    public final class Key implements Comparable, java.io.Serializable
    {

    /**
    * public constructor
    */
    public Key (String keyString)
    {
    this.keyString = keyString;
    this.itemList = null;
    } // end constructor

    /**
    * the lookup key
    */
    protected String keyString;

    /**
    * List of associated Items
    * Array is compact even if a bit clumsy to add and remove an
    element.
    */
    protected Item[] itemList;

    /**
    * Returns a hashcode based solely on the lookup key, not the
    items.
    *
    * @return a hash code value for this object.
    */
    public int hashCode()
    {
    return keyString != null ? keyString.hashCode(): 0;
    }

    /**
    * Compares two Keys, basing equality on the key, not the Itemlist.
    * @param anObject other Item to compare to.
    * @return true if the Items are equal. Return false if anObject is
    not of the same type.
    */
    public boolean equals(Object anObject)
    {
    if ( keyString == null ) return false;
    if ( anObject instanceof Key )
    {
    return this.keyString.equals(((Key)anObject).keyString);
    }
    if ( anObject instanceof String )
    {
    return this.keyString.equals((String)anObject);
    }
    return false;
    } // end equals

    /**
    * add an Item to the ItemList if it is not already there.
    * @param Item to add, must be unique.
    */
    protected void addItem(Item uniqueItem)
    {
    int len;
    if ( itemList == null )
    {
    len = 0;
    }
    else
    {
    len = itemList.length;
    }
    for ( int i=0; i<len; i++ )
    {
    // since unique, can use == instead of equals.
    if ( uniqueItem == itemList )
    {
    // already in list
    return;
    }

    } // end for
    // grow the array to make room to add item at the end
    Item[] newItemList = new Item[len+1];
    int below = len;
    if ( below > 0 )
    {
    System.arraycopy(itemList, 0, newItemList, 0, below);
    }
    newItemList[len] = uniqueItem;
    itemList = newItemList;
    // order does not matter
    } // end addItem

    /**
    * remove given item from this key's itemList.
    * If it is already removed, that's ok.
    */
    protected void removeItem(Item uniqueItem)
    {
    int len = itemList.length;
    for ( int i=0; i<len; i++ )
    {
    // since unique, can use == instead of equals.
    if ( uniqueItem == itemList )
    {
    // found in list
    if ( len == 1 )
    {
    itemList = null;
    return;
    }
    // shrink the array to remove ith element
    Item[] newItemList = new Item[len-1];
    // copy elts below i, if any
    int below = i;
    if ( below > 0 )
    {
    System.arraycopy(itemList, 0, newItemList, 0, below);
    }
    // copy elts above i, if any
    int above = len-i;
    if ( above > 0 )
    {
    System.arraycopy(itemList, i+1, newItemList, i, above);
    }
    itemList = newItemList;
    // existing order is just fine
    return;
    } // end if
    } // end for
    // not in list, nothing to do
    } // end removeItem

    /* compares this Key's Keystring to another's.
    * @param o Key to be compared with this one.
    * @return +ve if this one is bigger, 0 if equal, -ve if smaller.
    */
    public int compareTo (Object o) throws ClassCastException
    {
    return this.keyString.compareTo(((Key)o).keyString);
    }

    /**
    * @return a String that represents this Key
    */
    public String toString()
    {
    return keyString;
    }

    } // end class Key

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

    // com.mindprod.pim.TextItem.java
    package com.mindprod.pim;

    /**
    * Item to associate, holds simple string, without embedded \n chars.
    */

    public class TextItem extends Item
    {

    /**
    * public constructor
    */
    public TextItem (String text)
    {
    super();
    this.text = text;
    } // end constructor

    /**
    * text data to be associated with keys
    */
    private String text;

    /**
    * get text data string for this TextItem
    * Note there is no corresponding setText, since the data must be
    immutable.
    * @return text string.
    */
    public String getText()
    {
    return text;
    } // end getText

    /**
    * @return a String that represents this TextItem
    */
    public String toString()
    {
    return text;
    }

    /**
    * Returns a hashcode based solely on the data contents of the
    Item, not the keylist.
    *
    * @return a hash code value for this object.
    */
    public int hashCode()
    {
    return text.hashCode();
    }

    /**
    * Compares two Items, basing equality on the data contents of the
    Item, not the keylist.
    * @param anObject other Item to compare to.
    * @return true if the Items are equal. Return false if anObject is
    not of the same type.
    */
    public boolean equals(Object anObject)
    {
    if ( text == null ) return false;
    if ( anObject instanceof TextItem )
    {
    return text.equals(((TextItem)anObject).text);
    }
    if ( anObject instanceof String )
    {
    return text.equals((String)anObject);
    }
    return false;
    } // end equals

    } // end class TextItem

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

    I have two other variants, one uses a TreeMap and one supports Many to
    One mappings, not many to many.




    --
    Canadian Mind Products, Roedy Green.
    Coaching, problem solving, economical contract programming.
    See http://mindprod.com/jgloss/jgloss.html for The Java Glossary.
    Roedy Green, May 11, 2004
    #5
    1. Advertising

Want to reply to this thread or ask your own question?

It takes just 2 minutes to sign up (and it's free!). Just click the sign up button to choose a username and then you can ask your own questions on the forum.
Similar Threads
  1. Guillermo

    Problem with hashTable

    Guillermo, Mar 4, 2004, in forum: Perl
    Replies:
    1
    Views:
    604
    Gunnar Hjalmarsson
    Mar 4, 2004
  2. Jonathan Wolfson

    vbc compilation fails when using Hashtable

    Jonathan Wolfson, Jun 27, 2003, in forum: ASP .Net
    Replies:
    1
    Views:
    543
    Tu-Thach
    Jun 27, 2003
  3. John E

    Get Hashtable Object Directly

    John E, Oct 8, 2003, in forum: ASP .Net
    Replies:
    1
    Views:
    2,318
    Nicholas Paldino [.NET/C# MVP]
    Oct 8, 2003
  4. Steve

    Hashtable question

    Steve, Aug 21, 2006, in forum: ASP .Net
    Replies:
    3
    Views:
    312
    =?Utf-8?B?RGF2aWQgSmVzc2Vl?=
    Aug 21, 2006
  5. Chuck Insight

    Three-Part HashTable Question

    Chuck Insight, Mar 10, 2005, in forum: ASP .Net Web Controls
    Replies:
    0
    Views:
    109
    Chuck Insight
    Mar 10, 2005
Loading...

Share This Page