/******************************************************************************* * Copyright (c) 2009 Centrum Wiskunde en Informatica (CWI) * All rights reserved. This program and the accompanying materials * are made available under the terms of the Eclipse Public License v1.0 * which accompanies this distribution, and is available at * http://www.eclipse.org/legal/epl-v10.html * * Contributors: * Arnold Lankamp - interfaces and implementation *******************************************************************************/ package org.rascalmpl.value.util; /** * This is a hashset which assigns a unique identifier to every inserted entry. Identifiers will be * generated in order, starting at 0. * * @author Arnold Lankamp * * @param <E> The value type. */ public final class IndexedSet<E>{ private final static int INITIAL_LOG_SIZE = 4; private int modSize; private int hashMask; private Entry<E>[] data; private int threshold; private int load; /** * Constructor. */ public IndexedSet(){ super(); modSize = INITIAL_LOG_SIZE; int tableSize = 1 << modSize; hashMask = tableSize - 1; data = (Entry<E>[]) new Entry[tableSize]; threshold = tableSize; load = 0; } private void rehash(){ modSize++; int tableSize = 1 << modSize; hashMask = tableSize - 1; Entry<E>[] newData = (Entry<E>[]) new Entry[tableSize]; threshold = tableSize; Entry<E>[] oldData = data; for(int i = oldData.length - 1; i >= 0; i--){ Entry<E> entry = oldData[i]; if(entry != null){ // Determine the last unchanged entry chain. Entry<E> lastUnchangedEntryChain = entry; int newLastUnchangedEntryChainIndex = entry.hash & hashMask; Entry<E> e = entry.next; while(e != null){ int newIndex = e.hash & hashMask; if(newIndex != newLastUnchangedEntryChainIndex){ lastUnchangedEntryChain = e; newLastUnchangedEntryChainIndex = newIndex; } e = e.next; } newData[newLastUnchangedEntryChainIndex] = lastUnchangedEntryChain; // Reconstruct the other entries (if necessary). while(entry != lastUnchangedEntryChain){ int hash = entry.hash; int position = hash & hashMask; newData[position] = new Entry<>(hash, entry.key, entry.value, newData[position]); entry = entry.next; } } } data = newData; } private void ensureCapacity(){ if(load > threshold){ rehash(); } } /** * Stores the given element into this set or returns its identifier in case it was already * present. * * @param element * The element to store. * @return The identifier associated with the given value; -1 if the element wasn't present yet. */ public int store(E element){ int hash = element.hashCode(); int position = hash & hashMask; Entry<E> entry = data[position]; while(entry != null){ if(hash == entry.hash && element.equals(entry.key)) return entry.value; entry = entry.next; } // Not present ensureCapacity(); position = hash & hashMask; data[position] = new Entry<>(hash, element, load++, data[position]); return -1; } /** * Attempts to find the identifier associted with the given element. * * @param element * The element to retrieve the identifier from. * @return The identifier associated with the given element; -1 if not present, */ public int get(E element){ int hash = element.hashCode(); int position = hash & hashMask; Entry<E> entry = data[position]; while(entry != null){ if(hash == entry.hash && element.equals(entry.key)) return entry.value; entry = entry.next; } return -1; } private static class Entry<E>{ public final int hash; public final E key; public final int value; public final Entry<E> next; public Entry(int hash, E key, int value, Entry<E> next){ super(); this.hash = hash; this.key = key; this.value = value; this.next = next; } } }