Reworked IntMap to be a proper Java collection in the style that the

java.util collections established. This means that IntMap becomes
HashIntMap, IntMap is now an interface that extends the standard Map
interface and a new Collections class is provided that allows one to
create a synchronized wrapper around an IntMap.


git-svn-id: https://samskivert.googlecode.com/svn/trunk@318 6335cc39-0255-0410-8fd6-9bcaacd3b74c
This commit is contained in:
mdb
2001-09-15 17:22:11 +00:00
parent 98b338f19e
commit dc12791cc8
5 changed files with 590 additions and 123 deletions
@@ -1,5 +1,5 @@
//
// $Id: UserRepository.java,v 1.15 2001/08/12 01:34:31 mdb Exp $
// $Id: UserRepository.java,v 1.16 2001/09/15 17:22:11 mdb Exp $
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001 Michael Bayne
@@ -31,7 +31,7 @@ import com.samskivert.Log;
import com.samskivert.jdbc.JDBCUtil;
import com.samskivert.jdbc.MySQLRepository;
import com.samskivert.jdbc.jora.*;
import com.samskivert.util.IntMap;
import com.samskivert.util.HashIntMap;
/**
* Interfaces with the RDBMS in which the user information is stored. The
@@ -334,7 +334,7 @@ public class UserRepository extends MySQLRepository
ids.append(userids[i]);
}
final IntMap map = new IntMap();
final HashIntMap map = new HashIntMap();
// do the query
execute(new Operation () {
@@ -0,0 +1,256 @@
//
// $Id: Collections.java,v 1.1 2001/09/15 17:22:11 mdb Exp $
package com.samskivert.util;
import java.util.*;
/**
* Like the <code>java.util</code> class of the same name, the
* <code>Collections</code> class provides utility functions related to
* the collections provided by the <code>com.samskivert.util</code>
* package.
*/
public class Collections
{
/**
* Returns a synchronized (thread-safe) int map backed by the
* specified int map. In order to guarantee serial access, it is
* critical that <strong>all</strong> access to the backing int map is
* accomplished through the returned int map.
*
* <p> It is imperative that the user manually synchronize on the
* returned int map when iterating over any of its collection views:
*
* <pre>
* IntMap m = Collections.synchronizedIntMap(new HashIntMap());
* ...
* Set s = m.keySet(); // Needn't be in synchronized block
* ...
* synchronized(m) { // Synchronizing on m, not s!
* Iterator i = s.iterator(); // Must be in synchronized block
* while (i.hasNext())
* foo(i.next());
* }
* </pre>
*
* Failure to follow this advice may result in non-deterministic
* behavior.
*
* <p> The returned map will be serializable if the specified map is
* serializable.
*
* @param m the int map to be "wrapped" in a synchronized int map.
*
* @return a synchronized view of the specified int map.
*/
public static IntMap synchronizedIntMap(IntMap m) {
return new SynchronizedIntMap(m);
}
/**
* Horked from the Java util class and extended for <code>IntMap</code>.
*/
protected static class SynchronizedIntMap implements IntMap
{
private IntMap m; // Backing Map
private Object mutex; // Object on which to synchronize
SynchronizedIntMap(IntMap m) {
if (m == null) {
throw new NullPointerException();
}
this.m = m;
mutex = this;
}
SynchronizedIntMap(IntMap m, Object mutex) {
this.m = m;
this.mutex = mutex;
}
public int size() {
synchronized(mutex) {return m.size();}
}
public boolean isEmpty(){
synchronized(mutex) {return m.isEmpty();}
}
public boolean containsKey(int key) {
synchronized(mutex) {return m.containsKey(key);}
}
public boolean containsKey(Object key) {
synchronized(mutex) {return m.containsKey(key);}
}
public boolean containsValue(Object value){
synchronized(mutex) {return m.containsValue(value);}
}
public Object get(int key) {
synchronized(mutex) {return m.get(key);}
}
public Object get(Object key) {
synchronized(mutex) {return m.get(key);}
}
public Object put(int key, Object value) {
synchronized(mutex) {return m.put(key, value);}
}
public Object put(Object key, Object value) {
synchronized(mutex) {return m.put(key, value);}
}
public Object remove(int key) {
synchronized(mutex) {return m.remove(key);}
}
public Object remove(Object key) {
synchronized(mutex) {return m.remove(key);}
}
public void putAll(Map map) {
synchronized(mutex) {m.putAll(map);}
}
public void clear() {
synchronized(mutex) {m.clear();}
}
private transient Set keySet = null;
private transient Set entrySet = null;
private transient Collection values = null;
public Set keySet() {
synchronized(mutex) {
if (keySet==null)
keySet = new SynchronizedSet(m.keySet(), mutex);
return keySet;
}
}
public Set entrySet() {
synchronized(mutex) {
if (entrySet==null)
entrySet = new SynchronizedSet(m.entrySet(), mutex);
return entrySet;
}
}
public Collection values() {
synchronized(mutex) {
if (values==null)
values = new SynchronizedCollection(m.values(), mutex);
return values;
}
}
public boolean equals(Object o) {
synchronized(mutex) {return m.equals(o);}
}
public int hashCode() {
synchronized(mutex) {return m.hashCode();}
}
public String toString() {
synchronized(mutex) {return m.toString();}
}
}
/**
* I wish I could use this from the <code>java.util.Collections</code>
* class, but those crazy kids at Sun are always using private and
* default access and pointlessly preventing people from properly
* reusing their code. Yay!
*/
protected static class SynchronizedSet
extends SynchronizedCollection implements Set {
SynchronizedSet(Set s) {
super(s);
}
SynchronizedSet(Set s, Object mutex) {
super(s, mutex);
}
public boolean equals(Object o) {
synchronized(mutex) {return c.equals(o);}
}
public int hashCode() {
synchronized(mutex) {return c.hashCode();}
}
}
/**
* I wish I could use this from the <code>java.util.Collections</code>
* class, but those crazy kids at Sun are always using private and
* default access and pointlessly preventing people from properly
* reusing their code. Yay!
*/
protected static class SynchronizedCollection
implements Collection {
Collection c; // Backing Collection
Object mutex; // Object on which to synchronize
SynchronizedCollection(Collection c) {
if (c==null)
throw new NullPointerException();
this.c = c;
mutex = this;
}
SynchronizedCollection(Collection c, Object mutex) {
this.c = c;
this.mutex = mutex;
}
public int size() {
synchronized(mutex) {return c.size();}
}
public boolean isEmpty() {
synchronized(mutex) {return c.isEmpty();}
}
public boolean contains(Object o) {
synchronized(mutex) {return c.contains(o);}
}
public Object[] toArray() {
synchronized(mutex) {return c.toArray();}
}
public Object[] toArray(Object[] a) {
synchronized(mutex) {return c.toArray(a);}
}
public Iterator iterator() {
return c.iterator(); // Must be manually synched by user!
}
public boolean add(Object o) {
synchronized(mutex) {return c.add(o);}
}
public boolean remove(Object o) {
synchronized(mutex) {return c.remove(o);}
}
public boolean containsAll(Collection coll) {
synchronized(mutex) {return c.containsAll(coll);}
}
public boolean addAll(Collection coll) {
synchronized(mutex) {return c.addAll(coll);}
}
public boolean removeAll(Collection coll) {
synchronized(mutex) {return c.removeAll(coll);}
}
public boolean retainAll(Collection coll) {
synchronized(mutex) {return c.retainAll(coll);}
}
public void clear() {
synchronized(mutex) {c.clear();}
}
public String toString() {
synchronized(mutex) {return c.toString();}
}
}
}
@@ -1,5 +1,5 @@
//
// $Id: HashIntMap.java,v 1.3 2001/08/11 22:43:29 mdb Exp $
// $Id: HashIntMap.java,v 1.4 2001/09/15 17:22:11 mdb Exp $
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001 Michael Bayne
@@ -20,63 +20,81 @@
package com.samskivert.util;
import java.util.Enumeration;
import java.util.NoSuchElementException;
import java.util.*;
/**
* An int map is like a hashmap, but with integers as keys. We avoid the
* annoyance of having to create integer objects every time we want to
* lookup or insert values.
* An int map is like a regular map, but with integers as keys. We avoid
* the annoyance of having to create integer objects every time we want to
* lookup or insert values. The hash int map is an int map that uses a
* hashtable mechanism to store its key/value mappings.
*/
public class IntMap
public class HashIntMap
extends AbstractMap implements IntMap
{
/**
* The default number of buckets to use for the hash table.
*/
public final static int DEFAULT_BUCKETS = 64;
public IntMap (int buckets)
/**
* Constructs an empty hash int map with the specified number of hash
* buckets.
*/
public HashIntMap (int buckets)
{
_buckets = new Record[buckets];
}
public IntMap ()
/**
* Constructs an empty hash int map with the default number of hash
* buckets.
*/
public HashIntMap ()
{
this(DEFAULT_BUCKETS);
}
public synchronized int size ()
// documentation inherited
public int size ()
{
return _size;
}
public synchronized void put (int key, Object value)
// documentation inherited
public boolean containsKey (Object key)
{
int index = Math.abs(key)%_buckets.length;
Record rec = _buckets[index];
// either we start a new chain
if (rec == null) {
_buckets[index] = new Record(key, value);
_size++; // we're bigger
return;
}
// or we replace an element in an existing chain
Record prev = rec;
for (; rec != null; rec = rec.next) {
if (rec.key == key) {
rec.value = value; // we're not bigger
return;
}
prev = rec;
}
// or we append it to this chain
prev.next = new Record(key, value);
_size++; // we're bigger
return containsKey(((Integer)key).intValue());
}
public synchronized Object get (int key)
// documentation inherited
public boolean containsKey (int key)
{
int index = Math.abs(key)%_buckets.length;
return get(key) != null;
}
// documentation inherited
public boolean containsValue (Object o)
{
for (int i = 0; i < _buckets.length; i++) {
for (Record r = _buckets[i]; r != null; r = r.next) {
if (r.value.equals(o)) {
return true;
}
}
}
return false;
}
// documentation inherited
public Object get (Object key)
{
return get(((Integer)key).intValue());
}
// documentation inherited
public Object get (int key)
{
int index = Math.abs(key) % _buckets.length;
for (Record rec = _buckets[index]; rec != null; rec = rec.next) {
if (rec.key == key) {
return rec.value;
@@ -85,16 +103,59 @@ public class IntMap
return null;
}
public boolean contains (int key)
// documentation inherited
public Object put (Object key, Object value)
{
return get(key) != null;
return put(((Integer)key).intValue(), value);
}
public synchronized Object remove (int key)
// documentation inherited
public Object put (int key, Object value)
{
// disallow null values
if (value == null) {
throw new IllegalArgumentException();
}
int index = Math.abs(key)%_buckets.length;
Record rec = _buckets[index];
// either we start a new chain
if (rec == null) {
_buckets[index] = new Record(key, value);
_size++; // we're bigger
return null;
}
// or we replace an element in an existing chain
Record prev = rec;
for (; rec != null; rec = rec.next) {
if (rec.key == key) {
Object ovalue = rec.value;
rec.value = value; // we're not bigger
return ovalue;
}
prev = rec;
}
// or we append it to this chain
prev.next = new Record(key, value);
_size++; // we're bigger
return null;
}
// documentation inherited
public Object remove (Object key)
{
return remove(((Integer)key).intValue());
}
// documentation inherited
public Object remove (int key)
{
int index = Math.abs(key) % _buckets.length;
Record rec = _buckets[index];
// if there's no chain, there's no object
if (rec == null) {
return null;
@@ -120,7 +181,8 @@ public class IntMap
return null;
}
public synchronized void clear ()
// documentation inherited
public void clear ()
{
// abandon all of our hash chains (the joy of garbage collection)
for (int i = 0; i < _buckets.length; i++) {
@@ -130,17 +192,99 @@ public class IntMap
_size = 0;
}
public Enumeration keys ()
// documentation inherited
public Set entrySet ()
{
return new Enumerator(_buckets, true);
return new AbstractSet() {
public int size ()
{
return _size;
}
public Iterator iterator ()
{
return new EntryIterator();
}
};
}
public Enumeration elements ()
protected class EntryIterator implements Iterator
{
return new Enumerator(_buckets, false);
public boolean hasNext ()
{
// if we're pointing to an entry, we've got more entries
if (_record != null) {
return true;
}
// search backward through the buckets looking for the next
// non-empty hash chain
while (_index-- > 0) {
if ((_record = _buckets[_index]) != null) {
return true;
}
}
// found no non-empty hash chains, we're done
return false;
}
public Object next ()
{
// if we're not pointing to an entry, search for the next
// non-empty hash chain
if (_record == null) {
while ((_index-- > 0) &&
((_record = _buckets[_index]) == null));
}
// keep track of the last thing we returned
_last = _record;
// if we found a record, return it's value and move our record
// reference to it's successor
if (_record != null) {
_record = _last.next;
return _last;
}
throw new NoSuchElementException();
}
public void remove ()
{
if (_last == null) {
throw new IllegalStateException();
}
// remove the record the hard way
HashIntMap.this.remove(_last.key);
_last = null;
}
protected int _index = _buckets.length;
protected Record _record, _last;
}
protected static class Record
/**
* Returns an iteration over the keys of this hash int map. The keys
* are returned as <code>Integer</code> objects.
*/
public Iterator keys ()
{
return keySet().iterator();
}
/**
* Returns an iteration over the elements (values) of this hash int
* map.
*/
public Iterator elements ()
{
return values().iterator();
}
protected static class Record implements Entry
{
public Record next;
public int key;
@@ -151,86 +295,74 @@ public class IntMap
this.key = key;
this.value = value;
}
public Object getKey ()
{
return new Integer(key);
}
public Object getValue ()
{
return value;
}
public Object setValue (Object value)
{
Object ovalue = this.value;
this.value = value;
return ovalue;
}
public boolean equals (Object o)
{
if (o instanceof Record) {
Record or = (Record)o;
return (key == or.key) && value.equals(or.value);
} else {
return false;
}
}
public int hashCode ()
{
return key ^ ((value == null) ? 0 : value.hashCode());
}
}
class Enumerator implements Enumeration
{
public Enumerator (Record[] buckets, boolean returnKeys)
{
_buckets = buckets;
_index = buckets.length;
_returnKeys = returnKeys;
}
public static void main (String[] args)
{
HashIntMap table = new HashIntMap();
public boolean hasMoreElements ()
{
// if we're pointing to an entry, we've got more entries
if (_record != null) {
return true;
}
System.out.print("Inserting: ");
for (int i = 10; i < 100; i++) {
Integer value = new Integer(i);
table.put(i, value);
System.out.print("(" + i + "," + value + ")");
}
System.out.println("");
// search backward through the buckets looking for the next
// non-empty hash chain
while (_index-- > 0) {
if ((_record = _buckets[_index]) != null) {
return true;
}
}
System.out.print("Looking up: ");
for (int i = 10; i < 100; i++) {
System.out.print("(" + i + "," + table.get(i) + ")");
}
System.out.println("");
// found no non-empty hash chains, we're done
return false;
}
System.out.println("Keys: " +
StringUtil.toString(table.keys()));
System.out.println("Elems: " +
StringUtil.toString(table.elements()));
public Object nextElement ()
{
// if we're not pointing to an entry, search for the next
// non-empty hash chain
if (_record == null) {
while ((_index-- > 0) &&
((_record = _buckets[_index]) == null));
}
System.out.print("Removing: ");
for (int i = 10; i < 98; i++) {
System.out.print("(" + i + "," + table.remove(i) + ")");
}
System.out.println("");
// if we found a record, return it's value and move our record
// reference to it's successor
if (_record != null) {
Record r = _record;
_record = r.next;
return _returnKeys ? new Integer(r.key) : r.value;
}
throw new NoSuchElementException("IntMapEnumerator");
}
private int _index;
private Record _record;
private Record[] _buckets;
private boolean _returnKeys;
}
// public static void main (String[] args)
// {
// IntMap table = new IntMap();
// System.out.print("Inserting: ");
// for (int i = 10; i < 100; i++) {
// Integer value = new Integer(i);
// table.put(i, value);
// System.out.print("(" + i + "," + value + ")");
// }
// System.out.println("");
// System.out.print("Looking up: ");
// for (int i = 10; i < 100; i++) {
// System.out.print("(" + i + "," + table.get(i) + ")");
// }
// System.out.println("");
// System.out.print("Removing: ");
// for (int i = 10; i < 100; i++) {
// System.out.print("(" + i + "," + table.remove(i) + ")");
// }
// System.out.println("");
// }
System.out.println("Keys: " +
StringUtil.toString(table.keys()));
System.out.println("Elems: " +
StringUtil.toString(table.elements()));
}
private Record[] _buckets;
private int _size;
@@ -0,0 +1,78 @@
//
// $Id: IntMap.java,v 1.1 2001/09/15 17:22:11 mdb Exp $
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001 Michael Bayne
//
// This library is free software; you can redistribute it and/or modify it
// under the terms of the GNU Lesser General Public License as published
// by the Free Software Foundation; either version 2.1 of the License, or
// (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
package com.samskivert.util;
import java.util.Map;
/**
* An int map is a map that uses integers as keys and provides accessors
* that eliminate the need to create and manipulate superfluous
* <code>Integer</code> objects. It extends the <code>Map</code> interface
* and therefore provides all of the standard accessors (for which
* <code>Integer</code> objects should be supplied as keys).
*/
public interface IntMap extends Map
{
/**
* Returns <tt>true</tt> if this map contains a mapping for the
* specified key.
*
* @param key key whose presence in this map is to be tested.
*
* @return <tt>true</tt> if this map contains a mapping for the
* specified key.
*/
public boolean containsKey (int key);
/**
* Returns the value to which this map maps the specified key. Returns
* <tt>null</tt> if the map contains no mapping for this key.
*
* @param key key whose associated value is to be returned.
*
* @return the value to which this map maps the specified key, or
* <tt>null</tt> if the map contains no mapping for this key.
*/
public Object get (int key);
/**
* Associates the specified value with the specified key in this map.
* If the map previously contained a mapping for this key, the old
* value is replaced.
*
* @param key key with which the specified value is to be associated.
* @param value value to be associated with the specified key.
*
* @return previous value associated with specified key, or
* <tt>null</tt> if there was no mapping for key.
*/
public Object put (int key, Object value);
/**
* Removes the mapping for this key from this map if present.
*
* @param key key whose mapping is to be removed from the map.
*
* @return previous value associated with specified key, or
* <tt>null</tt> if there was no mapping for key.
*/
public Object remove (int key);
}
@@ -1,5 +1,5 @@
//
// $Id: IntervalManager.java,v 1.3 2001/08/11 22:43:29 mdb Exp $
// $Id: IntervalManager.java,v 1.4 2001/09/15 17:22:11 mdb Exp $
//
// samskivert library - useful routines for java programs
// Copyright (C) 2001 Michael Bayne
@@ -21,6 +21,7 @@
package com.samskivert.util;
import java.util.*;
import java.util.Collections;
import com.samskivert.Log;
/**
@@ -245,7 +246,7 @@ public class IntervalManager extends Thread
// to rewrite this such that we can add and remove intervaleds much
// faster.
protected static ArrayList _schedule = new ArrayList();
protected static IntMap _hash = new IntMap();
protected static HashIntMap _hash = new HashIntMap();
protected static long _nextwake = Long.MAX_VALUE;
protected static int _helpers = 0; // # of created helpers