You are viewing a plain text version of this content. The canonical link for it is here.
Posted to dev@commons.apache.org by sc...@apache.org on 2003/12/03 16:16:49 UTC

cvs commit: jakarta-commons/collections/src/java/org/apache/commons/collections/map StaticBucketMap.java package.html

scolebourne    2003/12/03 07:16:49

  Modified:    collections/src/java/org/apache/commons/collections
                        StaticBucketMap.java
               collections/src/test/org/apache/commons/collections/map
                        TestAll.java
               collections/src/java/org/apache/commons/collections/map
                        package.html
  Added:       collections/src/test/org/apache/commons/collections/map
                        TestStaticBucketMap.java
               collections/src/java/org/apache/commons/collections/map
                        StaticBucketMap.java
  Log:
  Refactor StaticBucketMap to map subpackage
  
  Revision  Changes    Path
  1.13      +3 -2      jakarta-commons/collections/src/java/org/apache/commons/collections/StaticBucketMap.java
  
  Index: StaticBucketMap.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/collections/src/java/org/apache/commons/collections/StaticBucketMap.java,v
  retrieving revision 1.12
  retrieving revision 1.13
  diff -u -r1.12 -r1.13
  --- StaticBucketMap.java	31 Aug 2003 17:26:44 -0000	1.12
  +++ StaticBucketMap.java	3 Dec 2003 15:16:49 -0000	1.13
  @@ -129,6 +129,7 @@
    * iterations, or if you can make your own guarantees about how bulk 
    * operations will affect the map.<p>
    *
  + * @deprecated Moved to map subpackage. Due to be removed in v4.0.
    * @since Commons Collections 2.1
    * @version $Revision$ $Date$
    * 
  
  
  
  1.7       +6 -4      jakarta-commons/collections/src/test/org/apache/commons/collections/map/TestAll.java
  
  Index: TestAll.java
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/collections/src/test/org/apache/commons/collections/map/TestAll.java,v
  retrieving revision 1.6
  retrieving revision 1.7
  diff -u -r1.6 -r1.7
  --- TestAll.java	2 Dec 2003 21:57:08 -0000	1.6
  +++ TestAll.java	3 Dec 2003 15:16:49 -0000	1.7
  @@ -83,11 +83,13 @@
       public static Test suite() {
           TestSuite suite = new TestSuite();
           
  -        suite.addTest(TestFixedSizeMap.suite());
  -        suite.addTest(TestFixedSizeSortedMap.suite());
           suite.addTest(TestFlat3Map.suite());
           suite.addTest(TestHashedMap.suite());
           suite.addTest(TestIdentityMap.suite());
  +        suite.addTest(TestStaticBucketMap.suite());
  +        
  +        suite.addTest(TestFixedSizeMap.suite());
  +        suite.addTest(TestFixedSizeSortedMap.suite());
           suite.addTest(TestLazyMap.suite());
           suite.addTest(TestLazySortedMap.suite());
           suite.addTest(TestListOrderedMap.suite());
  
  
  
  1.1                  jakarta-commons/collections/src/test/org/apache/commons/collections/map/TestStaticBucketMap.java
  
  Index: TestStaticBucketMap.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-commons/collections/src/test/org/apache/commons/collections/map/TestStaticBucketMap.java,v 1.1 2003/12/03 15:16:49 scolebourne Exp $
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2001-2003 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowledgement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgement may appear in the software itself,
   *    if and wherever such third-party acknowledgements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */
  package org.apache.commons.collections.map;
  
  import java.util.Map;
  
  import junit.framework.Test;
  
  import org.apache.commons.collections.BulkTest;
  
  /**
   * Unit tests 
   * {@link org.apache.commons.collections.StaticBucketMap}.
   * 
   * @version $Revision: 1.1 $ $Date: 2003/12/03 15:16:49 $
   * 
   * @author Michael A. Smith
   */
  public class TestStaticBucketMap extends AbstractTestMap {
  
      public TestStaticBucketMap(String name) {
          super(name);
      }
  
      public static Test suite() {
          return BulkTest.makeSuite(TestStaticBucketMap.class);
      }
  
      public static void main(String[] args[]) {
          String[] testCaseName = { TestStaticBucketMap.class.getName() };
          junit.textui.TestRunner.main(testCaseName);
      }
  
      public Map makeEmptyMap() {
          return new StaticBucketMap(30);
      }
  
      public String[] ignoredTests() {
          String pre = "TestStaticBucketMap.bulkTestMap";
          String post = ".testCollectionIteratorFailFast";
          return new String[] {
              pre + "EntrySet" + post,
              pre + "KeySet" + post,
              pre + "Values" + post
          };
      }   
  }
  
  
  
  1.3       +11 -7     jakarta-commons/collections/src/java/org/apache/commons/collections/map/package.html
  
  Index: package.html
  ===================================================================
  RCS file: /home/cvs/jakarta-commons/collections/src/java/org/apache/commons/collections/map/package.html,v
  retrieving revision 1.2
  retrieving revision 1.3
  diff -u -r1.2 -r1.3
  --- package.html	18 Nov 2003 23:23:05 -0000	1.2
  +++ package.html	3 Dec 2003 15:16:49 -0000	1.3
  @@ -1,14 +1,19 @@
   <BODY>
   <p>
  -This package contains implementations of the Set and SortedSet interfaces.
  +This package contains implementations of the Map, OrderedMap and SortedMap interfaces.
  +A Map provides a lookup from a key to a value.
   <p>
  -The implementations are in the form of direct implementations and decorators.
  -A decorator wraps another implementation of the interface to add some
  -specific additional functionality.
  +The following implementations are provided:
  +<ul>
  +<li>HashedMap - general purpose HashMap replacement supporting MapIterator
  +<li>IdentityMap - Map that uses == for comparison instead of equals()
  +<li>Flat3Map - designed for good performance at size 3 or less
  +<li>StaticBucketMap - internally synchronized and designed for thread-contentious environments
  +</ul>
   <p>
  -The following implementations are provided in the package:
  +The following decorators are provided:
   <ul>
  -<li>Synchronized - synchronizes method access for multi-threaded environments
  +<!--li>Synchronized - synchronizes method access for multi-threaded environments-->
   <li>Unmodifiable - ensures the collection cannot be altered
   <li>Predicated - ensures that only elements that are valid according to a predicate can be added
   <li>Typed - ensures that only elements that are of a specific type can be added
  @@ -16,7 +21,6 @@
   <li>FixedSize - ensures that the size of the map cannot change
   <li>Lazy - creates objects in the map on demand
   <li>ListOrdered - ensures that insertion order is retained
  -<li>Flat3 - designed for good performance at size 3 or less
   </ul>
   </pre>
   </BODY>
  
  
  
  1.1                  jakarta-commons/collections/src/java/org/apache/commons/collections/map/StaticBucketMap.java
  
  Index: StaticBucketMap.java
  ===================================================================
  /*
   * $Header: /home/cvs/jakarta-commons/collections/src/java/org/apache/commons/collections/map/StaticBucketMap.java,v 1.1 2003/12/03 15:16:49 scolebourne Exp $
   * ====================================================================
   *
   * The Apache Software License, Version 1.1
   *
   * Copyright (c) 2002-2003 The Apache Software Foundation.  All rights
   * reserved.
   *
   * Redistribution and use in source and binary forms, with or without
   * modification, are permitted provided that the following conditions
   * are met:
   *
   * 1. Redistributions of source code must retain the above copyright
   *    notice, this list of conditions and the following disclaimer.
   *
   * 2. Redistributions in binary form must reproduce the above copyright
   *    notice, this list of conditions and the following disclaimer in
   *    the documentation and/or other materials provided with the
   *    distribution.
   *
   * 3. The end-user documentation included with the redistribution, if
   *    any, must include the following acknowledgement:
   *       "This product includes software developed by the
   *        Apache Software Foundation (http://www.apache.org/)."
   *    Alternately, this acknowledgement may appear in the software itself,
   *    if and wherever such third-party acknowledgements normally appear.
   *
   * 4. The names "The Jakarta Project", "Commons", and "Apache Software
   *    Foundation" must not be used to endorse or promote products derived
   *    from this software without prior written permission. For written
   *    permission, please contact apache@apache.org.
   *
   * 5. Products derived from this software may not be called "Apache"
   *    nor may "Apache" appear in their names without prior written
   *    permission of the Apache Software Foundation.
   *
   * THIS SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
   * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
   * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
   * DISCLAIMED.  IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR
   * ITS CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
   * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
   * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
   * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
   * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
   * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
   * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
   * SUCH DAMAGE.
   * ====================================================================
   *
   * This software consists of voluntary contributions made by many
   * individuals on behalf of the Apache Software Foundation.  For more
   * information on the Apache Software Foundation, please see
   * <http://www.apache.org/>.
   *
   */
  package org.apache.commons.collections.map;
  
  import java.util.AbstractCollection;
  import java.util.AbstractSet;
  import java.util.ArrayList;
  import java.util.Collection;
  import java.util.Iterator;
  import java.util.Map;
  import java.util.NoSuchElementException;
  import java.util.Set;
  
  /**
   * A StaticBucketMap is an efficient, thread-safe implementation of
   * <code>java.util.Map</code> that performs well in in a highly
   * thread-contentious environment.  The map supports very efficient
   * {@link #get(Object) get}, {@link #put(Object,Object) put}, 
   * {@link #remove(Object) remove} and {@link #containsKey(Object) containsKey}
   * operations, assuming (approximate) uniform hashing and
   * that the number of entries does not exceed the number of buckets.  If the
   * number of entries exceeds the number of buckets or if the hashcodes of the
   * objects are not uniformly distributed, these operations have a worst case
   * scenario that is proportional to the number of elements in the map
   * (<i>O(n)</i>).<p>
   *
   * Each bucket in the hash table has its own monitor, so two threads can 
   * safely operate on the map at the same time, often without incurring any 
   * monitor contention.  This means that you don't have to wrap instances
   * of this class with {@link java.util.Collections#synchronizedMap(Map)};
   * instances are already thread-safe.  Unfortunately, however, this means 
   * that this map implementation behaves in ways you may find disconcerting.  
   * Bulk operations, such as {@link #putAll(Map) putAll} or the
   * {@link Collection#retainAll(Collection) retainAll} operation in collection 
   * views, are <i>not</i> atomic.  If two threads are simultaneously 
   * executing 
   *
   * <pre>
   *   staticBucketMapInstance.putAll(map);
   * </pre>
   *
   * and
   *
   * <pre>
   *   staticBucketMapInstance.entrySet().removeAll(map.entrySet());
   * </pre>
   *
   * then the results are generally random.  Those two statement could cancel
   * each other out, leaving <code>staticBucketMapInstance</code> essentially 
   * unchanged, or they could leave some random subset of <code>map</code> in 
   * <code>staticBucketMapInstance</code>.<p>
   *
   * Also, much like an encyclopedia, the results of {@link #size()} and 
   * {@link #isEmpty()} are out-of-date as soon as they are produced.<p>
   *
   * The iterators returned by the collection views of this class are <i>not</i>
   * fail-fast.  They will <i>never</i> raise a 
   * {@link java.util.ConcurrentModificationException}.  Keys and values 
   * added to the map after the iterator is created do not necessarily appear
   * during iteration.  Similarly, the iterator does not necessarily fail to 
   * return keys and values that were removed after the iterator was created.<p>
   *
   * Finally, unlike {@link java.util.HashMap}-style implementations, this
   * class <i>never</i> rehashes the map.  The number of buckets is fixed 
   * at construction time and never altered.  Performance may degrade if 
   * you do not allocate enough buckets upfront.<p>
   *
   * The {@link #atomic(Runnable)} method is provided to allow atomic iterations
   * and bulk operations; however, overuse of {@link #atomic(Runnable) atomic}
   * will basically result in a map that's slower than an ordinary synchronized
   * {@link java.util.HashMap}.
   *
   * Use this class if you do not require reliable bulk operations and 
   * iterations, or if you can make your own guarantees about how bulk 
   * operations will affect the map.<p>
   *
   * @since Commons Collections 3.0
   * @version $Revision: 1.1 $ $Date: 2003/12/03 15:16:49 $
   * 
   * @author Berin Loritsch
   * @author Gerhard Froehlich
   * @author Michael A. Smith
   * @author Paul Jack
   * @author Leo Sutic
   * @author Janek Bogucki
   */
  public final class StaticBucketMap implements Map {
  
      private static final int DEFAULT_BUCKETS = 255;
      private Node[] m_buckets;
      private Lock[] m_locks;
  
      /**
       * Initializes the map with the default number of buckets (255).
       */
      public StaticBucketMap() {
          this(DEFAULT_BUCKETS);
      }
  
      /**
       * Initializes the map with a specified number of buckets.  The number
       * of buckets is never below 17, and is always an odd number (StaticBucketMap
       * ensures this). The number of buckets is inversely proportional to the
       * chances for thread contention.  The fewer buckets, the more chances for
       * thread contention.  The more buckets the fewer chances for thread
       * contention.
       *
       * @param numBuckets  the number of buckets for this map
       */
      public StaticBucketMap(int numBuckets) {
          int size = Math.max(17, numBuckets);
  
          // Ensure that bucketSize is never a power of 2 (to ensure maximal distribution)
          if (size % 2 == 0) {
              size--;
          }
  
          m_buckets = new Node[size];
          m_locks = new Lock[size];
  
          for (int i = 0; i < size; i++) {
              m_locks[i] = new Lock();
          }
      }
  
      //-----------------------------------------------------------------------
      /**
       * Determine the exact hash entry for the key.  The hash algorithm
       * is rather simplistic, but it does the job:
       *
       * <pre>
       *   He = |Hk mod n|
       * </pre>
       *
       * <p>
       *   He is the entry's hashCode, Hk is the key's hashCode, and n is
       *   the number of buckets.
       * </p>
       */
      private final int getHash(Object key) {
          if (key == null) {
              return 0;
          }
          int hash = key.hashCode();
          hash += ~(hash << 15);
          hash ^= (hash >>> 10);
          hash += (hash << 3);
          hash ^= (hash >>> 6);
          hash += ~(hash << 11);
          hash ^= (hash >>> 16);
          hash %= m_buckets.length;
          return (hash < 0) ? hash * -1 : hash;
      }
  
      /**
       * Gets the current size of the map.
       * The value is computed fresh each time the method is called.
       * 
       * @return the current size
       */
      public int size() {
          int cnt = 0;
  
          for (int i = 0; i < m_buckets.length; i++) {
              cnt += m_locks[i].size;
          }
          return cnt;
      }
  
      /**
       * Checks if the size is currently zero.
       * 
       * @return true if empty
       */
      public boolean isEmpty() {
          return (size() == 0);
      }
  
      /**
       * Gets the value associated with the key.
       * 
       * @param key  the key to retrieve
       * @return the associated value
       */
      public Object get(final Object key) {
          int hash = getHash(key);
  
          synchronized (m_locks[hash]) {
              Node n = m_buckets[hash];
  
              while (n != null) {
                  if (n.key == key || (n.key != null && n.key.equals(key))) {
                      return n.value;
                  }
  
                  n = n.next;
              }
          }
          return null;
      }
  
      /**
       * Checks if the map contains the specified key.
       * 
       * @param key  the key to check
       * @return true if found
       */
      public boolean containsKey(final Object key) {
          int hash = getHash(key);
  
          synchronized (m_locks[hash]) {
              Node n = m_buckets[hash];
  
              while (n != null) {
                  if (n.key == null || (n.key != null && n.key.equals(key))) {
                      return true;
                  }
  
                  n = n.next;
              }
          }
          return false;
      }
  
      /**
       * Checks if the map contains the specified value.
       * 
       * @param value  the value to check
       * @return true if found
       */
      public boolean containsValue(final Object value) {
          for (int i = 0; i < m_buckets.length; i++) {
              synchronized (m_locks[i]) {
                  Node n = m_buckets[i];
  
                  while (n != null) {
                      if (n.value == value || (n.value != null && n.value.equals(value))) {
                          return true;
                      }
  
                      n = n.next;
                  }
              }
          }
          return false;
      }
  
      //-----------------------------------------------------------------------
      /**
       * Puts a new key value mapping into the map.
       * 
       * @param key  the key to use
       * @param value  the value to use
       * @return the previous mapping for the key
       */
      public Object put(final Object key, final Object value) {
          int hash = getHash(key);
  
          synchronized (m_locks[hash]) {
              Node n = m_buckets[hash];
  
              if (n == null) {
                  n = new Node();
                  n.key = key;
                  n.value = value;
                  m_buckets[hash] = n;
                  m_locks[hash].size++;
                  return null;
              }
  
              // Set n to the last node in the linked list.  Check each key along the way
              //  If the key is found, then change the value of that node and return
              //  the old value.
              for (Node next = n; next != null; next = next.next) {
                  n = next;
  
                  if (n.key == key || (n.key != null && n.key.equals(key))) {
                      Object returnVal = n.value;
                      n.value = value;
                      return returnVal;
                  }
              }
  
              // The key was not found in the current list of nodes, add it to the end
              //  in a new node.
              Node newNode = new Node();
              newNode.key = key;
              newNode.value = value;
              n.next = newNode;
              m_locks[hash].size++;
          }
          return null;
      }
  
      /**
       * Removes the specified key from the map.
       * 
       * @param key  the key to remove
       * @return the previous value at this key
       */
      public Object remove(Object key) {
          int hash = getHash(key);
  
          synchronized (m_locks[hash]) {
              Node n = m_buckets[hash];
              Node prev = null;
  
              while (n != null) {
                  if (n.key == key || (n.key != null && n.key.equals(key))) {
                      // Remove this node from the linked list of nodes.
                      if (null == prev) {
                          // This node was the head, set the next node to be the new head.
                          m_buckets[hash] = n.next;
                      } else {
                          // Set the next node of the previous node to be the node after this one.
                          prev.next = n.next;
                      }
                      m_locks[hash].size--;
                      return n.value;
                  }
  
                  prev = n;
                  n = n.next;
              }
          }
          return null;
      }
      
      //-----------------------------------------------------------------------
      /**
       * Gets the key set.
       * 
       * @return the key set
       */
      public Set keySet() {
          return new KeySet();
      }
  
      /**
       * Gets the values.
       * 
       * @return the values
       */
      public Collection values() {
          return new Values();
      }
  
      /**
       * Gets the entry set.
       * 
       * @return the entry set
       */
      public Set entrySet() {
          return new EntrySet();
      }
  
      //-----------------------------------------------------------------------
      /**
       * Puts all the entries from the specified map into this map.
       * This operation is <b>not atomic</b> and may have undesired effects.
       */
      public void putAll(Map other) {
          Iterator i = other.keySet().iterator();
  
          while (i.hasNext()) {
              Object key = i.next();
              put(key, other.get(key));
          }
      }
  
      /**
       * Clears the map of all entries.
       */
      public void clear() {
          for (int i = 0; i < m_buckets.length; i++) {
              Lock lock = m_locks[i];
              synchronized (lock) {
                  m_buckets[i] = null;
                  lock.size = 0;
              }
          }
      }
  
      /**
       * Compares this map to another, as per the Map specification.
       * 
       * @param obj  the object to compare to
       * @return true if equal
       */
      public boolean equals(Object obj) {
          if (obj == this) {
              return true;
          }
          if (obj instanceof Map == false) {
              return false;
          }
          Map other = (Map) obj;
          return entrySet().equals(other.entrySet());
      }
  
      /**
       * Gets the hashcode, as per the Map specification.
       * 
       * @return the hashcode
       */
      public int hashCode() {
          int hashCode = 0;
  
          for (int i = 0; i < m_buckets.length; i++) {
              synchronized (m_locks[i]) {
                  Node n = m_buckets[i];
  
                  while (n != null) {
                      hashCode += n.hashCode();
                      n = n.next;
                  }
              }
          }
          return hashCode;
      }
  
      //-----------------------------------------------------------------------
      /**
       * The Map.Entry for the StaticBucketMap.
       */
      private static final class Node implements Map.Entry {
          protected Object key;
          protected Object value;
          protected Node next;
  
          public Object getKey() {
              return key;
          }
  
          public Object getValue() {
              return value;
          }
  
          public int hashCode() {
              return ((key == null ? 0 : key.hashCode()) ^
                      (value == null ? 0 : value.hashCode()));
          }
  
          public boolean equals(Object o) {
              if (o == this) {
                  return true;
              }
              if (o instanceof Map.Entry == false) {
                  return false;
              }
  
              Map.Entry e2 = (Map.Entry) o;
              return (
                  (key == null ? e2.getKey() == null : key.equals(e2.getKey())) &&
                  (value == null ? e2.getValue() == null : value.equals(e2.getValue())));
          }
  
          public Object setValue(Object val) {
              Object retVal = value;
              value = val;
              return retVal;
          }
      }
  
  
      /**
       * The lock object, which also includes a count of the nodes in this lock.
       */
      private final static class Lock {
          public int size;
      }
  
  
      //-----------------------------------------------------------------------
      private class EntryIterator implements Iterator {
  
          private ArrayList current = new ArrayList();
          private int bucket;
          private Map.Entry last;
  
  
          public boolean hasNext() {
              if (current.size() > 0) return true;
              while (bucket < m_buckets.length) {
                  synchronized (m_locks[bucket]) {
                      Node n = m_buckets[bucket];
                      while (n != null) {
                          current.add(n);
                          n = n.next;
                      }
                      bucket++;
                      if (current.size() > 0) return true;
                  }
              }
              return false;
          }
  
          protected Map.Entry nextEntry() {
              if (!hasNext()) throw new NoSuchElementException();
              last = (Map.Entry)current.remove(current.size() - 1);
              return last;
          }
  
          public Object next() {
              return nextEntry();
          }
  
          public void remove() {
              if (last == null) throw new IllegalStateException();
              StaticBucketMap.this.remove(last.getKey());
              last = null;
          }
  
      }
  
      private class ValueIterator extends EntryIterator {
  
          public Object next() {
              return nextEntry().getValue();
          }
  
      }
  
      private class KeyIterator extends EntryIterator {
  
          public Object next() {
              return nextEntry().getKey();
          }
  
      }
  
      private class EntrySet extends AbstractSet {
  
          public int size() {
              return StaticBucketMap.this.size();
          }
  
          public void clear() {
              StaticBucketMap.this.clear();
          }
  
          public Iterator iterator() {
              return new EntryIterator();
          }
  
          public boolean contains(Object o) {
              Map.Entry entry = (Map.Entry)o;
              int hash = getHash(entry.getKey());
              synchronized (m_locks[hash]) {
                  for (Node n = m_buckets[hash]; n != null; n = n.next) {
                      if (n.equals(entry)) return true;
                  }
              }
              return false;
          }
  
          public boolean remove(Object o) {
              Map.Entry entry = (Map.Entry)o;
              int hash = getHash(entry.getKey());
              synchronized (m_locks[hash]) {
                  for (Node n = m_buckets[hash]; n != null; n = n.next) {
                      if (n.equals(entry)) {
                          StaticBucketMap.this.remove(n.getKey());
                          return true;
                      }
                  }
              }
              return false;
          }
  
      }
  
  
      private class KeySet extends AbstractSet {
  
          public int size() {
              return StaticBucketMap.this.size();
          }
  
          public void clear() {
              StaticBucketMap.this.clear();
          }
  
          public Iterator iterator() {
              return new KeyIterator();
          }
  
          public boolean contains(Object o) {
              return StaticBucketMap.this.containsKey(o);
          }
  
          public boolean remove(Object o) {
              int hash = getHash(o);
              synchronized (m_locks[hash]) {
                  for (Node n = m_buckets[hash]; n != null; n = n.next) {
                      Object k = n.getKey();
                      if ((k == o) || ((k != null) && k.equals(o))) {
                          StaticBucketMap.this.remove(k);
                          return true;
                      }
                  }
              }
              return false;
  
          }
  
      }
  
  
      private class Values extends AbstractCollection {
  
          public int size() {
              return StaticBucketMap.this.size();
          }
  
          public void clear() {
              StaticBucketMap.this.clear();
          }
  
          public Iterator iterator() {
              return new ValueIterator();
          }
  
      }
  
  
      /**
       *  Prevents any operations from occurring on this map while the
       *  given {@link Runnable} executes.  This method can be used, for
       *  instance, to execute a bulk operation atomically: 
       *
       *  <pre>
       *    staticBucketMapInstance.atomic(new Runnable() {
       *        public void run() {
       *            staticBucketMapInstance.putAll(map);
       *        }
       *    });
       *  </pre>
       *
       *  It can also be used if you need a reliable iterator:
       *
       *  <pre>
       *    staticBucketMapInstance.atomic(new Runnable() {
       *        public void run() {
       *            Iterator iterator = staticBucketMapInstance.iterator();
       *            while (iterator.hasNext()) {
       *                foo(iterator.next();
       *            }
       *        }
       *    });
       *  </pre>
       *
       *  <b>Implementation note:</b> This method requires a lot of time
       *  and a ton of stack space.  Essentially a recursive algorithm is used
       *  to enter each bucket's monitor.  If you have twenty thousand buckets
       *  in your map, then the recursive method will be invoked twenty thousand
       *  times.  You have been warned.
       *
       *  @param r  the code to execute atomically
       */
      public void atomic(Runnable r) {
          if (r == null) throw new NullPointerException();
          atomic(r, 0);
      }
  
      private void atomic(Runnable r, int bucket) {
          if (bucket >= m_buckets.length) {
              r.run();
              return;
          }
          synchronized (m_locks[bucket]) {
              atomic(r, bucket + 1);
          }
      }
  
  
  }
  
  
  

---------------------------------------------------------------------
To unsubscribe, e-mail: commons-dev-unsubscribe@jakarta.apache.org
For additional commands, e-mail: commons-dev-help@jakarta.apache.org