package de.spieleck.util;
/* Please see the license information in the header below. */
/*
NGramJ - n-gram based text classification
Copyright (C) 2001 Frank S. Nestel (frank at spieleck.de)
This program 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 program 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 General Public License for more details.
You should have received a copy of the GNU Lesser General Public License
along with this program (lesser.txt); if not, write to the Free Software
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*/
import java.lang.StringBuffer;
import java.util.Enumeration;
import java.util.Iterator;
/**
* The IntMap provides a simple hashmap from keys to integers.
* The API is simplified of the HashMap/Hashtable collection API.
* Additionally there are call to do simple arithmetic with entries
* in the map.
*
* <p>The convenience of IntMap is avoiding all wrapping of integers.
* </p>
* <B>Note:</B> This class is completely unsynchronized for speed!
*/
public class IntMap
{
/**
* Encoding of a null entry. Since NULL is equal to Integer.MIN_VALUE,
* it's impossible to distinguish between the two.
*/
public final static int NULL = Integer.MIN_VALUE;
protected Object [] keys;
protected int nullValue;
protected int []values;
protected int size;
protected int mask;
protected int limit;
/**
* Create a new IntMap. Default size is 16.
*/
public IntMap()
{
this(16);
}
public IntMap(int size)
{
this(size, NULL);
}
public IntMap(int size, int nullValue)
{
if ( size < 8 )
size = 8;
int z = 0;
while ( size > 1 )
{
z++;
size /= 2;
}
size = 2 << z;
keys = new Object[size];
values = new int[size];
mask = keys.length - 1;
limit = 3 * keys.length / 4;
size = 0;
this.nullValue = nullValue;
}
/**
*
*/
public int getNullValue()
{
return nullValue;
}
/**
* This only works with binary indizes
*/
protected int firstIndex(Object key)
{
return key.hashCode() & mask;
}
protected int nextIndex(int index)
{
return (index + 5) & mask;
}
/**
* Clear the hashmap.
* XXX Or should we let the GC the work an use keys = new Object[..]???
*/
public void clear()
{
for (int i = 0; i < values.length; i++)
keys[i] = null;
size = 0;
}
/**
* Returns the current number of entries in the map.
*/
public int size()
{
return size;
}
/**
* Get an Element
*/
public int getElement(Object key)
{
return get(key);
}
/**
* Get an Element
*/
public int get(Object key)
{
if ( key == null )
return nullValue;
for (int i = firstIndex(key); keys[i] != null; i = nextIndex(i))
{
if ( keys[i] == key || key.equals(keys[i]) )
return values[i];
}
return nullValue;
}
/**
* Optimized operations.
* avoid get change put cycles.
*/
public int inc(Object key)
{
return add(key, 1);
}
/**
* Optimized operations.
* avoid get change put cycles.
*/
public int dec(Object key)
{
return add(key, -1);
}
/**
* Optimized operations.
* avoid get change put cycles.
*/
public int add(Object key, int off)
{
if ( key == null )
return nullValue;
int i;
for (i = firstIndex(key); keys[i] != null; i = nextIndex(i))
{
if ( keys[i] == key || key.equals(keys[i]) )
{
values[i] += off;
return values[i];
}
}
return nullValue;
}
/**
* Expands the table
*/
protected void grow()
{
int newSize = 2 * keys.length;
Object[] oldKeys = keys;
int[] oldValues = values;
//
keys = new Object[newSize];
values = new int[newSize];
mask = newSize - 1;
size = 0; // We recound the size below!
for (int i = 0; i < oldKeys.length; i++)
if (oldKeys[i] != null)
internalPut(oldKeys[i], oldValues[i]);
limit = 3 * newSize / 4;
}
/**
* Puts a new value in the property table with the appropriate flags
*/
public int putElement(Object key, int value)
{
return put(key, value);
}
public int put(Object key, int value)
{
if (key == null)
return nullValue;
if ( size >= limit )
grow();
return internalPut(key, value);
}
protected int internalPut(Object key, int value)
{
for(int i = firstIndex(key); true; i = nextIndex(i))
{
Object testKey = keys[i];
if ( testKey == null )
{
keys[i] = key;
values[i] = value;
size++;
return nullValue;
}
else if ( key == testKey || testKey.equals(key))
{
int old = values[i];
values[i] = value;
return old;
}
}
}
/**
* Deletes the entry.
*/
public int remove(Object key)
{
if (key == null || size == 0)
return nullValue;
int i;
for (i = firstIndex(key); keys[i] != null; i = nextIndex(i))
{
Object testKey = keys[i];
if (key == testKey || key.equals(testKey) )
{
int value = values[i];
do
{
keys[i] = null;
int j = i;
int r;
do
{
i = nextIndex(i);
if (keys[i] == null)
break;
r = firstIndex(keys[i]);
}
while ( (i <= r && r < j)
|| (r < j && j < i)
|| (j < i && i <= r)
);
keys[j] = keys[i];
values[j] = values[i];
}
while (keys[i] != null);
--size;
return value;
}
}
return nullValue;
}
public Enumeration keys()
{
return new IntMapEnumeration();
}
public Iterator iterator()
{
return new IntMapIterator();
}
public String toString()
{
StringBuffer sbuf = new StringBuffer();
sbuf.append("IntMap[");
boolean isFirst = true;
for (int i = 0; i < keys.length; i++)
{
if (keys[i] != null)
{
if (! isFirst)
sbuf.append(", ");
isFirst = false;
sbuf.append(keys[i]);
sbuf.append(":");
sbuf.append(values[i]);
}
}
sbuf.append("]");
return sbuf.toString();
}
private class IntMapEnumeration
implements Enumeration
{
protected int index = 0;
public boolean hasMoreElements()
{
for (; index < keys.length; index++)
if (keys[index] != null )
return true;
return false;
}
public Object nextElement()
{
for (; index < keys.length; index++)
if (keys[index] != null )
return keys[index++];
return null;
}
}
private class IntMapIterator
implements Iterator
{
protected int index = 0;
public boolean hasNext()
{
for (; index < keys.length; index++)
if (keys[index] != null )
return true;
return false;
}
public Object next()
{
for (; index < keys.length; index++)
if (keys[index] != null )
return keys[index++];
return null;
}
public void remove()
{
throw new java.lang.UnsupportedOperationException(
"IntMapIterator.remove()");
}
}
}