Hashtable.avt

Переключить прокрутку окна
Загрузить этот исходный код

/*
    Исходный код среды исполнения ПВТ-ОО.

    Этот исходный код является частью проекта ПВТ-ОО.

    Copyright © 2021 Малик Разработчик

    Это свободная программа: вы можете перераспространять её и/или
    изменять её на условиях Меньшей Стандартной общественной лицензии GNU в том виде,
    в каком она была опубликована Фондом свободного программного обеспечения;
    либо версии 3 лицензии, либо (по вашему выбору) любой более поздней версии.

    Эта программа распространяется в надежде, что она может быть полезна,
    но БЕЗО ВСЯКИХ ГАРАНТИЙ; даже без неявной гарантии ТОВАРНОГО ВИДА
    или ПРИГОДНОСТИ ДЛЯ ОПРЕДЕЛЁННЫХ ЦЕЛЕЙ. Подробнее см. в Меньшей Стандартной
    общественной лицензии GNU.

    Вы должны были получить копию Меньшей Стандартной общественной лицензии GNU
    вместе с этой программой. Если это не так, см.
    <http://www.gnu.org/licenses/>.
*/

package avt.util;

import avt.lang.array.*;
import platform.independent.streamformat.*;

public class Hashtable(Object, MutableDataHolder, DataHolder, Cloneable, Measureable)
{
    private static int getIndex(long8 hash, int length) {
        int8 temp = (int8) (hash ^ (hash >> 32));
        temp = (temp & Int8.MAX_VALUE) ^ (temp >>> 31);
        int result = 0x55555555;
        for(int i = 8; i-- > 0; ) result ^= temp[i];
        return result % length;
    }


    private int fldLength;
    private HashtableEntry[] fldTable;
    private final Object fldMonitor;

    public () {
        fldTable = new HashtableEntry[15];
        fldMonitor = new Object();
    }

    public (int initialCapacity) {
        if(initialCapacity < 1) initialCapacity = 1;
        fldTable = new HashtableEntry[initialCapacity];
        fldMonitor = new Object();
    }

    public String toString() {
        StringBuilder result = new StringBuilder() + "{ ";
        synchronized(fldMonitor)
        {
            with(new HashtableMapEnumeration(fldTable))
            {
                for(int index = this.fldLength; index-- > 0; )
                {
                    result + nextElement().toString() + '=' + value().toString();
                    if(index > 0) result + ", ";
                }
            }
        }
        return (result + " }").toString();
    }

    public void clear() {
        synchronized(fldMonitor)
        {
            fldLength = 0;
            Object[] table = fldTable;
            Array.fill(table, 0, table.length, null);
        }
    }

    public boolean isEmpty() { return fldLength <= 0; }

    public Hashtable clone() {
        Hashtable result;
        synchronized(fldMonitor)
        {
            HashtableEntry[] curTable = fldTable;
            int capacity = curTable.length;
            HashtableEntry[] newTable = (result = new Hashtable(capacity)).fldTable;
            for(int index = capacity; index-- > 0; )
            {
                for(HashtableEntry entry = curTable[index]; entry != null; entry = entry.next)
                {
                    newTable[index] = new HashtableEntry(entry.hash, entry.key, entry.value, newTable[index]);
                }
            }
            result.fldLength = fldLength;
        }
        return result;
    }

    public boolean containsKey(boolean key) { return containsKey(key ? Boolean.TRUE : Boolean.FALSE); }

    public boolean containsKey(char key) { return containsKey(new Char(key)); }

    public boolean containsKey(byte key) { return containsKey(new Byte(key)); }

    public boolean containsKey(byte2 key) { return containsKey(new Byte2(key)); }

    public boolean containsKey(byte4 key) { return containsKey(new Byte4(key)); }

    public boolean containsKey(byte8 key) { return containsKey(new Byte8(key)); }

    public boolean containsKey(short key) { return containsKey(new Short(key)); }

    public boolean containsKey(short2 key) { return containsKey(new Short2(key)); }

    public boolean containsKey(short4 key) { return containsKey(new Short4(key)); }

    public boolean containsKey(short8 key) { return containsKey(new Short8(key)); }

    public boolean containsKey(int key) { return containsKey(new Int(key)); }

    public boolean containsKey(int2 key) { return containsKey(new Int2(key)); }

    public boolean containsKey(int4 key) { return containsKey(new Int4(key)); }

    public boolean containsKey(int8 key) { return containsKey(new Int8(key)); }

    public boolean containsKey(long key) { return containsKey(new Long(key)); }

    public boolean containsKey(long2 key) { return containsKey(new Long2(key)); }

    public boolean containsKey(long4 key) { return containsKey(new Long4(key)); }

    public boolean containsKey(long8 key) { return containsKey(new Long8(key)); }

    public boolean containsKey(Object key) {
        if(key == null) return false;
        long8 hash = key.hashCodeAsLong8();
        boolean result = false;
        synchronized(fldMonitor)
        {
            for(HashtableEntry[] table = fldTable, HashtableEntry entry = table[getIndex(hash, table.length)]; entry != null; entry = entry.next)
            {
                if(hash == entry.hash && key.equals(entry.key))
                {
                    result = true;
                    break;
                }
            }
        }
        return result;
    }

    public boolean containsValue(boolean value) { return containsValue(value ? Boolean.TRUE : Boolean.FALSE); }

    public boolean containsValue(char value) { return containsValue(new Char(value)); }

    public boolean containsValue(byte value) { return containsValue(new Byte(value)); }

    public boolean containsValue(byte2 value) { return containsValue(new Byte2(value)); }

    public boolean containsValue(byte4 value) { return containsValue(new Byte4(value)); }

    public boolean containsValue(byte8 value) { return containsValue(new Byte8(value)); }

    public boolean containsValue(short value) { return containsValue(new Short(value)); }

    public boolean containsValue(short2 value) { return containsValue(new Short2(value)); }

    public boolean containsValue(short4 value) { return containsValue(new Short4(value)); }

    public boolean containsValue(short8 value) { return containsValue(new Short8(value)); }

    public boolean containsValue(int value) { return containsValue(new Int(value)); }

    public boolean containsValue(int2 value) { return containsValue(new Int2(value)); }

    public boolean containsValue(int4 value) { return containsValue(new Int4(value)); }

    public boolean containsValue(int8 value) { return containsValue(new Int8(value)); }

    public boolean containsValue(long value) { return containsValue(new Long(value)); }

    public boolean containsValue(long2 value) { return containsValue(new Long2(value)); }

    public boolean containsValue(long4 value) { return containsValue(new Long4(value)); }

    public boolean containsValue(long8 value) { return containsValue(new Long8(value)); }

    public boolean containsValue(Object value) {
        if(value == null) return false;
        boolean result = false;
        synchronized(fldMonitor)
        {
            label0: for(HashtableEntry[] table = fldTable, int index = table.length; index-- > 0; )
            {
                for(HashtableEntry entry = table[index]; entry != null; entry = entry.next)
                {
                    if(value.equals(entry.value))
                    {
                        result = true;
                        break label0;
                    }
                }
            }
        }
        return result;
    }

    public MapEnumeration enumerateKeys() { return new HashtableMapEnumeration(fldTable); }

    public Enumeration enumerateValues() { return new HashtableEnumeration(false, fldTable); }

    public int length { read = fldLength }

    public final int capacity { read = getCapacity }

    public void operator []=(boolean key, Object value) { operator []=(key ? Boolean.TRUE : Boolean.FALSE, value); }

    public void operator []=(char key, Object value) { operator []=(new Char(key), value); }

    public void operator []=(byte key, Object value) { operator []=(new Byte(key), value); }

    public void operator []=(byte2 key, Object value) { operator []=(new Byte2(key), value); }

    public void operator []=(byte4 key, Object value) { operator []=(new Byte4(key), value); }

    public void operator []=(byte8 key, Object value) { operator []=(new Byte8(key), value); }

    public void operator []=(short key, Object value) { operator []=(new Short(key), value); }

    public void operator []=(short2 key, Object value) { operator []=(new Short2(key), value); }

    public void operator []=(short4 key, Object value) { operator []=(new Short4(key), value); }

    public void operator []=(short8 key, Object value) { operator []=(new Short8(key), value); }

    public void operator []=(int key, Object value) { operator []=(new Int(key), value); }

    public void operator []=(int2 key, Object value) { operator []=(new Int2(key), value); }

    public void operator []=(int4 key, Object value) { operator []=(new Int4(key), value); }

    public void operator []=(int8 key, Object value) { operator []=(new Int8(key), value); }

    public void operator []=(long key, Object value) { operator []=(new Long(key), value); }

    public void operator []=(long2 key, Object value) { operator []=(new Long2(key), value); }

    public void operator []=(long4 key, Object value) { operator []=(new Long4(key), value); }

    public void operator []=(long8 key, Object value) { operator []=(new Long8(key), value); }

    public void operator []=(Object key, Object value) {
        if(key == null)
        {
            throw new NullPointerException("аргумент key равен нулевой ссылке");
        }
        if(value == null)
        {
            remove(key);
            return;
        }
        put(key, value);
    }

    public Object operator [](boolean key) { return operator [](key ? Boolean.TRUE : Boolean.FALSE); }

    public Object operator [](char key) { return operator [](new Char(key)); }

    public Object operator [](byte key) { return operator [](new Byte(key)); }

    public Object operator [](byte2 key) { return operator [](new Byte2(key)); }

    public Object operator [](byte4 key) { return operator [](new Byte4(key)); }

    public Object operator [](byte8 key) { return operator [](new Byte8(key)); }

    public Object operator [](short key) { return operator [](new Short(key)); }

    public Object operator [](short2 key) { return operator [](new Short2(key)); }

    public Object operator [](short4 key) { return operator [](new Short4(key)); }

    public Object operator [](short8 key) { return operator [](new Short8(key)); }

    public Object operator [](int key) { return operator [](new Int(key)); }

    public Object operator [](int2 key) { return operator [](new Int2(key)); }

    public Object operator [](int4 key) { return operator [](new Int4(key)); }

    public Object operator [](int8 key) { return operator [](new Int8(key)); }

    public Object operator [](long key) { return operator [](new Long(key)); }

    public Object operator [](long2 key) { return operator [](new Long2(key)); }

    public Object operator [](long4 key) { return operator [](new Long4(key)); }

    public Object operator [](long8 key) { return operator [](new Long8(key)); }

    public Object operator [](Object key) {
        if(key == null)
        {
            throw new NullPointerException("аргумент key равен нулевой ссылке");
        }
        long8 hash = key.hashCodeAsLong8();
        Object result = null;
        synchronized(fldMonitor)
        {
            for(HashtableEntry[] table = fldTable, HashtableEntry entry = table[getIndex(hash, table.length)]; entry != null; entry = entry.next)
            {
                if(hash == entry.hash && key.equals(entry.key))
                {
                    result = entry.value;
                    break;
                }
            }
        }
        return result;
    }

    protected void rehash() {
        HashtableEntry[] oldTable = fldTable;
        int oldCapacity = oldTable.length;
        int newCapacity = (oldCapacity << 1) + 1;
        if(newCapacity < 0) newCapacity = Int.MAX_VALUE;
        HashtableEntry[] newTable = fldTable = new HashtableEntry[newCapacity];
        for(int oldIndex = oldCapacity; oldIndex-- > 0; )
        {
            for(HashtableEntry oldEntry = oldTable[oldIndex]; oldEntry != null; )
            {
                int newIndex = getIndex(oldEntry.hash, newCapacity);
                HashtableEntry newEntry = oldEntry;
                oldEntry = oldEntry.next;
                newEntry.next = newTable[newIndex];
                newTable[newIndex] = newEntry;
            }
        }
    }

    private void put(Object key, Object value) {
        long8 hash = key.hashCodeAsLong8();
        boolean needError = false;
        synchronized(fldMonitor)
        {
            label0:
            {
                HashtableEntry[] table = fldTable;
                int capacity = table.length;
                int index = getIndex(hash, capacity);
                for(HashtableEntry entry = table[index]; entry != null; entry = entry.next)
                {
                    if(hash == entry.hash && key.equals(entry.key))
                    {
                        entry.value = value;
                        break label0;
                    }
                }
                int length = fldLength;
                if(length >= Int.MAX_VALUE)
                {
                    needError = true;
                    break label0;
                }
                if(length++ >= capacity)
                {
                    rehash();
                    index = getIndex(hash, (table = fldTable).length);
                }
                table[index] = new HashtableEntry(hash, key, value, table[index]);
                fldLength = length;
            }
        }
        if(needError)
        {
            throw new BufferTooLargeError("объём буфера очень велик");
        }
    }

    private void remove(Object key) {
        long8 hash = key.hashCodeAsLong8();
        synchronized(fldMonitor)
        {
            for(HashtableEntry[] table = fldTable, int index = getIndex(hash, table.length), HashtableEntry temp = null, HashtableEntry entry = table[index]; entry != null; temp = entry, entry = entry.next)
            {
                if(hash != entry.hash || !key.equals(entry.key)) continue;
                if(temp != null)
                {
                    temp.next = entry.next;
                } else
                {
                    table[index] = entry.next;
                }
                fldLength--;
                break;
            }
        }
    }

    private int getCapacity() { return fldTable.length; }
}

final class HashtableEntry(Object)
{
    HashtableEntry next;
    Object value;
    final long8 hash;
    final Object key;

    public (long8 hash, Object key, Object value, HashtableEntry next) {
        this.next = next;
        this.value = value;
        this.hash = hash;
        this.key = key;
    }
}

class HashtableEnumeration(Enumeration)
{
    int fldIndex;
    HashtableEntry fldNext;
    HashtableEntry fldCurrent;
    final boolean fldKeys;
    final HashtableEntry[] fldTable;

    public (boolean keys, HashtableEntry[] table) {
        fldIndex = table.length;
        fldKeys = keys;
        fldTable = table;
    }

    public boolean hasMoreElements() {
        if(fldNext != null) return true;
        for(HashtableEntry[] table = fldTable, int index = fldIndex; index-- > 0; )
        {
            if((fldNext = table[index]) != null)
            {
                fldIndex = index;
                return true;
            }
        }
        fldIndex = -1;
        return false;
    }

    public Object nextElement() {
        HashtableEntry entry = fldNext;
        if(entry == null)
        {
            int index = fldIndex;
            HashtableEntry[] table = fldTable;
            while(index-- > 0 && (entry = table[index]) == null);
            fldNext = entry;
            fldIndex = index < 0 ? -1 : index;
        }
        if(entry == null)
        {
            fldCurrent = null;
            throw new NoSuchElementException("в перечислении больше не осталось элементов");
        }
        fldNext = (fldCurrent = entry).next;
        return fldKeys ? entry.key : entry.value;
    }
}

class HashtableMapEnumeration(HashtableEnumeration, MapEnumeration)
{
    public (HashtableEntry[] table): super(true, table) {  }

    public Object value() {
        HashtableEntry entry = fldCurrent;
        if(entry == null)
        {
            throw new NoSuchElementException("в перечислении больше не осталось элементов");
        }
        return entry.value;
    }
}