All Downloads are FREE. Search and download functionalities are using the official Maven repository.

javolution.util.internal.map.FractalMapImpl Maven / Gradle / Ivy

Go to download

Only the Java Core part of Javolution library, with slight modifications for use in MSFTBX.

There is a newer version: 6.11.8
Show newest version
/*
 * Javolution - Java(TM) Solution for Real-Time and Embedded Systems
 * Copyright (C) 2012 - Javolution (http://javolution.org/)
 * All rights reserved.
 * 
 * Permission to use, copy, modify, and distribute this software is
 * freely granted, provided that this notice is preserved.
 */
package javolution.util.internal.map;

import javolution.util.function.Equality;

/**
 * A fractal-based map with rehash performed only on limited size maps.
 * It is based on a fractal structure with self-similar patterns at any scale
 * (maps holding submaps). At each depth only a part of the hashcode is used
 * starting by the last bits. 
 */
@SuppressWarnings("rawtypes")
final class FractalMapImpl {

    static final int EMPTINESS_LEVEL = 2; // Can be 1 (load factor 0.5), 2 (load factor 0.25) or any greater value.
    static final int INITIAL_BLOCK_CAPACITY = 2 << EMPTINESS_LEVEL;
    static final int SHIFT = 10; // Number of hashcode bits per depth. 
    //private static final int MAX_BLOCK_CAPACITY = 1 << SHIFT;
    private int count; // Number of entries different from null in this block.
    private MapEntryImpl[] entries = new MapEntryImpl[INITIAL_BLOCK_CAPACITY]; // Entries value can be a sub-map.
    private final int shift; // Zero if base map.
    final Equality keyComparator;
    
    public FractalMapImpl(Equality keyComparator) {
        this.shift = 0;
        this.keyComparator = keyComparator;
    }

    public FractalMapImpl(Equality keyComparator, int shift) {
        this.shift = shift;
        this.keyComparator = keyComparator;
    }

    /** Adds the specified entry if not already present; returns 
     *  either the specified entry or an existing entry for the specified key. **/
    @SuppressWarnings("unchecked")
    public MapEntryImpl addEntry(MapEntryImpl newEntry, Object key) {
    	int hash = keyComparator.hashOf(key);
        int i = indexOfKey(key, hash);
        MapEntryImpl entry = entries[i];
        if (entry != null) return entry; // Entry exists
        entries[i] = newEntry;
        newEntry.key = key;
        newEntry.hash = hash;
        // Check if we need to resize.
        if ((++count << EMPTINESS_LEVEL) > entries.length) {
            resize(entries.length << 1);
        }
        return newEntry;
    }

    public void clear() {
        entries = new MapEntryImpl[INITIAL_BLOCK_CAPACITY];
        count = 0;
    }

    /** Returns null if no entry with specified key */
    public MapEntryImpl getEntry(Object key) {
      	int hash = keyComparator.hashOf(key);
        return entries[indexOfKey(key, hash)];
    }

    /** Returns the entry removed or null if none. */
    public MapEntryImpl removeEntry(Object key, int hash) {
        int i = indexOfKey(key, hash);
        MapEntryImpl oldEntry = entries[i];
        if (oldEntry == null) return null; // Entry does not exist.
        entries[i] = null;
        // Since we have made a hole, adjacent keys might have to shift.
        for (;;) {
            // We use a step of 1 (improve caching through memory locality).
            i = (i + 1) & (entries.length - 1);
            MapEntryImpl entry = entries[i];
            if (entry == null) break; // Done.
            int correctIndex = indexOfKey(entry.key, entry.hash);
            if (correctIndex != i) { // Misplaced.
                entries[correctIndex] = entries[i];
                entries[i] = null;
            }
        }
        // Check if we need to resize.
        if (((--count << (EMPTINESS_LEVEL + 1)) <= entries.length)
                && (entries.length > INITIAL_BLOCK_CAPACITY)) {
            resize(entries.length >> 1);
        }
        return oldEntry;
    }

    /** Returns the index of the specified key in the map 
       (points to a null key if key not present). */
    private int indexOfKey(Object key, int hash) {
        int mask = entries.length - 1;
        int i = (hash >> shift) & mask;
        while (true) {
            MapEntryImpl entry = entries[i];
            if (entry == null) return i;
            if ((entry.hash == hash) && keyComparator.equal(key, entry.key)) return i;
            i = (i + 1) & mask;
        }
    }

    // The capacity is a power of two such as: 
    //    (count * 2**EMPTINESS_LEVEL) <=  capacity < (count * 2**(EMPTINESS_LEVEL+1))
    // TODO: Use submaps if max capacity reached.
    private void resize(int newCapacity) {
        MapEntryImpl[] newEntries = new MapEntryImpl[newCapacity];
        int newMask = newEntries.length - 1;
        for (int i = 0, n = entries.length; i < n; i++) {
            MapEntryImpl entry = entries[i];
            if (entry == null) continue;
            int newIndex = entry.hash & newMask;
            while (newEntries[newIndex] != null) { // Find empty slot.
                newIndex = (newIndex + 1) & newMask;
            }
            newEntries[newIndex] = entry;
        }
        entries = newEntries;
    }
}