blob: 3b4a981c864676e7860866a17b4532805c66e8a2 [file] [log] [blame]
/**
* JDBM LICENSE v1.00
*
* Redistribution and use of this software and associated documentation
* ("Software"), with or without modification, are permitted provided
* that the following conditions are met:
*
* 1. Redistributions of source code must retain copyright
* statements and notices. Redistributions must also contain a
* copy of this document.
*
* 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 name "JDBM" must not be used to endorse or promote
* products derived from this Software without prior written
* permission of Cees de Groot. For written permission,
* please contact cg@cdegroot.com.
*
* 4. Products derived from this Software may not be called "JDBM"
* nor may "JDBM" appear in their names without prior written
* permission of Cees de Groot.
*
* 5. Due credit should be given to the JDBM Project
* (http://jdbm.sourceforge.net/).
*
* THIS SOFTWARE IS PROVIDED BY THE JDBM PROJECT AND CONTRIBUTORS
* ``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
* CEES DE GROOT OR ANY 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.
*
* Copyright 2000 (C) Cees de Groot. All Rights Reserved.
* Contributions are Copyright (C) 2000 by their associated contributors.
*
*/
package jdbm.htree;
import java.io.Externalizable;
import java.io.IOException;
import java.io.ObjectInput;
import java.io.ObjectOutput;
import java.util.ArrayList;
/**
* A bucket is a placeholder for multiple (key, value) pairs. Buckets
* are used to store collisions (same hash value) at all levels of an
* H*tree.
*
* There are two types of buckets: leaf and non-leaf.
*
* Non-leaf buckets are buckets which hold collisions which happen
* when the H*tree is not fully expanded. Keys in a non-leaf buckets
* can have different hash codes. Non-leaf buckets are limited to an
* arbitrary size. When this limit is reached, the H*tree should create
* a new Directory page and distribute keys of the non-leaf buckets into
* the newly created Directory.
*
* A leaf bucket is a bucket which contains keys which all have
* the same <code>hashCode()</code>. Leaf buckets stand at the
* bottom of an H*tree because the hashing algorithm cannot further
* discriminate between different keys based on their hash code.
*
* @author <a href="mailto:boisvert@intalio.com">Alex Boisvert</a>
* @version $Id: HashBucket.java,v 1.2 2005/06/25 23:12:32 doomdark Exp $
*/
final class HashBucket
extends HashNode
implements Externalizable
{
final static long serialVersionUID = 1L;
/**
* The maximum number of elements (key, value) a non-leaf bucket
* can contain.
*/
public static final int OVERFLOW_SIZE = 8;
/**
* Depth of this bucket.
*/
private int _depth;
/**
* Keys in this bucket. Keys are ordered to match their respective
* value in <code>_values</code>.
*/
private ArrayList _keys;
/**
* Values in this bucket. Values are ordered to match their respective
* key in <code>_keys</code>.
*/
private ArrayList _values;
/**
* Public constructor for serialization.
*/
public HashBucket() {
// empty
}
/**
* Construct a bucket with a given depth level. Depth level is the
* number of <code>HashDirectory</code> above this bucket.
*/
public HashBucket( int level )
{
if ( level > HashDirectory.MAX_DEPTH+1 ) {
throw new IllegalArgumentException(
"Cannot create bucket with depth > MAX_DEPTH+1. "
+ "Depth=" + level );
}
_depth = level;
_keys = new ArrayList( OVERFLOW_SIZE );
_values = new ArrayList( OVERFLOW_SIZE );
}
/**
* Returns the number of elements contained in this bucket.
*/
public int getElementCount()
{
return _keys.size();
}
/**
* Returns whether or not this bucket is a "leaf bucket".
*/
public boolean isLeaf()
{
return ( _depth > HashDirectory.MAX_DEPTH );
}
/**
* Returns true if bucket can accept at least one more element.
*/
public boolean hasRoom()
{
if ( isLeaf() ) {
return true; // leaf buckets are never full
} else {
// non-leaf bucket
return ( _keys.size() < OVERFLOW_SIZE );
}
}
/**
* Add an element (key, value) to this bucket. If an existing element
* has the same key, it is replaced silently.
*
* @return Object which was previously associated with the given key
* or <code>null</code> if no association existed.
*/
public Object addElement( Object key, Object value )
{
int existing = _keys.indexOf(key);
if ( existing != -1 ) {
// replace existing element
Object before = _values.get( existing );
_values.set( existing, value );
return before;
} else {
// add new (key, value) pair
_keys.add( key );
_values.add( value );
return null;
}
}
/**
* Remove an element, given a specific key.
*
* @param key Key of the element to remove
*
* @return Removed element value, or <code>null</code> if not found
*/
public Object removeElement( Object key )
{
int existing = _keys.indexOf(key);
if ( existing != -1 ) {
Object obj = _values.get( existing );
_keys.remove( existing );
_values.remove( existing );
return obj;
} else {
// not found
return null;
}
}
/**
* Returns the value associated with a given key. If the given key
* is not found in this bucket, returns <code>null</code>.
*/
public Object getValue( Object key )
{
int existing = _keys.indexOf(key);
if ( existing != -1 ) {
return _values.get( existing );
} else {
// key not found
return null;
}
}
/**
* Obtain keys contained in this buckets. Keys are ordered to match
* their values, which be be obtained by calling <code>getValues()</code>.
*
* As an optimization, the Vector returned is the instance member
* of this class. Please don't modify outside the scope of this class.
*/
ArrayList getKeys()
{
return this._keys;
}
/**
* Obtain values contained in this buckets. Values are ordered to match
* their keys, which be be obtained by calling <code>getKeys()</code>.
*
* As an optimization, the Vector returned is the instance member
* of this class. Please don't modify outside the scope of this class.
*/
ArrayList getValues()
{
return this._values;
}
/**
* Implement Externalizable interface.
*/
public void writeExternal( ObjectOutput out )
throws IOException
{
out.writeInt( _depth );
int entries = _keys.size();
out.writeInt( entries );
// write keys
for (int i=0; i<entries; i++) {
out.writeObject( _keys.get( i ) );
}
// write values
for (int i=0; i<entries; i++) {
out.writeObject( _values.get( i ) );
}
}
/**
* Implement Externalizable interface.
*/
public void readExternal(ObjectInput in)
throws IOException, ClassNotFoundException {
_depth = in.readInt();
int entries = in.readInt();
// prepare array lists
int size = Math.max( entries, OVERFLOW_SIZE );
_keys = new ArrayList( size );
_values = new ArrayList( size );
// read keys
for ( int i=0; i<entries; i++ ) {
_keys.add( in.readObject() );
}
// read values
for ( int i=0; i<entries; i++ ) {
_values.add( in.readObject() );
}
}
public String toString() {
StringBuffer buf = new StringBuffer();
buf.append("HashBucket {depth=");
buf.append(_depth);
buf.append(", keys=");
buf.append(_keys);
buf.append(", values=");
buf.append(_values);
buf.append("}");
return buf.toString();
}
}