/* Copyright (C) 2003 Vladimir Roubtsov. All rights reserved. 
 *  
 * This program and the accompanying materials are made available under 
 * the terms of the Common Public License v1.0 which accompanies this distribution, 
 * and is available at http://www.eclipse.org/legal/cpl-v10.html 
 *  
 * $Id: IntSet.java,v 1.1.1.1 2004/05/09 16:57:53 vlad_r Exp $ 
 */ 
 
// ---------------------------------------------------------------------------- 
/** 
 * 
 * MT-safety: an instance of this class is <I>not</I> safe for access from 
 * multiple concurrent threads [even if access is done by a single thread at a 
 * time]. The caller is expected to synchronize externally on an instance [the 
 * implementation does not do internal synchronization for the sake of efficiency]. 
 * java.util.ConcurrentModificationException is not supported either. 
 * 
 * @author Vlad Roubtsov, (C) 2001 
 */ 
public 
final class IntSet 
{ 
    // public: ................................................................ 
 
    /** 
     * Equivalent to <CODE>IntSet(11, 0.75F)</CODE>. 
     */ 
    public IntSet () 
    { 
        this (11, 0.75F); 
    } 
     
    /** 
     * Equivalent to <CODE>IntSet(capacity, 0.75F)</CODE>. 
     */ 
    public IntSet (final int initialCapacity) 
    { 
        this (initialCapacity, 0.75F); 
    } 
     
    /** 
     * Constructs an IntSet with specified initial capacity and load factor. 
     * 
     * @param initialCapacity initial number of hash buckets in the table [may not be negative, 0 is equivalent to 1]. 
     * @param loadFactor the load factor to use to determine rehashing points [must be in (0.0, 1.0] range]. 
     */ 
    public IntSet (int initialCapacity, final float loadFactor) 
    { 
        if (initialCapacity < 0) throw new IllegalArgumentException ("negative input: initialCapacity [" + initialCapacity + "]"); 
        if ((loadFactor <= 0.0) || (loadFactor >= 1.0 + 1.0E-6)) 
            throw new IllegalArgumentException ("loadFactor not in (0.0, 1.0] range: " + loadFactor); 
         
        if (initialCapacity == 0) initialCapacity = 1; 
         
        m_loadFactor = loadFactor > 1.0 ? 1.0F : loadFactor;         
        m_sizeThreshold = (int) (initialCapacity * loadFactor); 
        m_buckets = new Entry [initialCapacity]; 
    } 
     
     
    /** 
     * Overrides Object.toString() for debug purposes. 
     */ 
    public String toString () 
    { 
        final StringBuffer s = new StringBuffer (); 
        debugDump (s); 
         
        return s.toString (); 
    } 
     
    /** 
     * Returns the number of key-value mappings in this map. 
     */ 
    public int size () 
    { 
        return m_size; 
    } 
     
    public boolean contains (final int key) 
    { 
        // index into the corresponding hash bucket: 
        final Entry [] buckets = m_buckets; 
        final int bucketIndex = (key & 0x7FFFFFFF) % buckets.length; 
         
        // traverse the singly-linked list of entries in the bucket: 
        for (Entry entry = buckets [bucketIndex]; entry != null; entry = entry.m_next) 
        { 
            if (key == entry.m_key) 
                return true; 
        } 
         
        return false; 
    } 
     
    public int [] values () 
    { 
        if (m_size == 0) 
            return new int[0]; 
        else 
        { 
            final int [] result = new int [m_size]; 
            int scan = 0; 
             
            for (int b = 0; b < m_buckets.length; ++ b) 
            { 
                for (Entry entry = m_buckets [b]; entry != null; entry = entry.m_next) 
                { 
                    result [scan ++] = entry.m_key; 
                } 
            } 
             
            return result; 
        } 
    } 
     
    public void values (final int [] target, final int offset) 
    { 
        if (m_size != 0) 
        { 
            int scan = offset; 
             
            for (int b = 0; b < m_buckets.length; ++ b) 
            { 
                for (Entry entry = m_buckets [b]; entry != null; entry = entry.m_next) 
                { 
                    target [scan ++] = entry.m_key; 
                } 
            } 
        } 
    } 
     
    public boolean add (final int key) 
    { 
        Entry currentKeyEntry = null; 
         
        // detect if 'key' is already in the table [in which case, set 'currentKeyEntry' to point to its entry]: 
         
        // index into the corresponding hash bucket: 
        int bucketIndex = (key & 0x7FFFFFFF) % m_buckets.length; 
         
        // traverse the singly-linked list of entries in the bucket: 
        Entry [] buckets = m_buckets; 
        for (Entry entry = buckets [bucketIndex]; entry != null; entry = entry.m_next) 
        { 
            if (key == entry.m_key) 
            { 
                currentKeyEntry = entry; 
                break; 
            } 
        } 
         
        if (currentKeyEntry == null) 
        { 
            // add a new entry: 
             
            if (m_size >= m_sizeThreshold) rehash (); 
             
            buckets = m_buckets; 
            bucketIndex = (key & 0x7FFFFFFF) % buckets.length; 
            final Entry bucketListHead = buckets [bucketIndex]; 
            final Entry newEntry = new Entry (key, bucketListHead); 
            buckets [bucketIndex] = newEntry; 
             
            ++ m_size; 
             
            return true; 
        } 
        else 
            return false; 
    } 
     
    // protected: ............................................................. 
 
    // package: ............................................................... 
     
     
    void debugDump (final StringBuffer out) 
    { 
        if (out != null) 
        { 
            out.append (super.toString ()); out.append (EOL); 
            out.append ("size = " + m_size + ", bucket table size = " + m_buckets.length + ", load factor = " + m_loadFactor + EOL); 
            out.append ("size threshold = " + m_sizeThreshold + EOL); 
        } 
    } 
 
    // private: ............................................................... 
 
     
    /** 
     * The structure used for chaining colliding keys. 
     */ 
    private static final class Entry 
    { 
        Entry (final int key, final Entry next) 
        { 
            m_key = key;  
            m_next = next; 
        } 
         
        final int m_key; 
         
        Entry m_next; // singly-linked list link 
         
    } // end of nested class 
     
 
    /** 
     * Re-hashes the table into a new array of buckets. 
     */ 
    private void rehash () 
    { 
        // TODO: it is possible to run this method twice, first time using the 2*k+1 prime sequencer for newBucketCount 
        // and then with that value reduced to actually shrink capacity. As it is right now, the bucket table can 
        // only grow in size 
         
        final Entry [] buckets = m_buckets; 
         
        final int newBucketCount = (m_buckets.length << 1) + 1; 
        final Entry [] newBuckets = new Entry [newBucketCount]; 
 
        // rehash all entry chains in every bucket: 
        for (int b = 0; b < buckets.length; ++ b) 
        { 
            for (Entry entry = buckets [b]; entry != null; ) 
            { 
                final Entry next = entry.m_next; // remember next pointer because we are going to reuse this entry 
                final int entryKey = entry.m_key; 
             
                // index into the corresponding new hash bucket: 
                final int newBucketIndex = (entryKey & 0x7FFFFFFF) % newBucketCount; 
                 
                final Entry bucketListHead = newBuckets [newBucketIndex]; 
                entry.m_next = bucketListHead; 
                newBuckets [newBucketIndex] = entry;                                 
                 
                entry = next; 
            } 
        } 
         
 
        m_sizeThreshold = (int) (newBucketCount * m_loadFactor); 
        m_buckets = newBuckets; 
    } 
     
     
    private final float m_loadFactor; // determines the setting of m_sizeThreshold 
     
    private Entry [] m_buckets; // table of buckets 
    private int m_size; // number of keys in the table, not cleared as of last check 
    private int m_sizeThreshold; // size threshold for rehashing 
         
    private static final String EOL = System.getProperty ("line.separator", "\n"); 
     
} // end of class 
// ---------------------------------------------------------------------------- 
 
    
     
     
     
     
     
     
     
  
  |