/******************************************************************************* * 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.impl.util.collections; import java.util.Collection; import java.util.Iterator; import java.util.NoSuchElementException; import java.util.Set; import org.rascalmpl.value.IValue; /** * A specialized version of the ShareableSet, specifically meant for storing values. * * @author Arnold Lankamp */ public final class ShareableValuesHashSet implements Set<IValue>, Iterable<IValue>{ private final static int INITIAL_LOG_SIZE = 4; private int modSize; private int hashMask; private Entry<IValue>[] data; private int threshold; private int load; private int currentHashCode; @SuppressWarnings("unchecked") public ShareableValuesHashSet(){ super(); modSize = INITIAL_LOG_SIZE; int tableSize = 1 << modSize; hashMask = tableSize - 1; data = (Entry<IValue>[]) new Entry[tableSize]; threshold = tableSize; load = 0; currentHashCode = 0; } public ShareableValuesHashSet(ShareableValuesHashSet shareableValuesHashSet){ super(); modSize = shareableValuesHashSet.modSize; int tableSize = 1 << modSize; hashMask = tableSize - 1; data = shareableValuesHashSet.data.clone(); threshold = tableSize; load = shareableValuesHashSet.load; currentHashCode = shareableValuesHashSet.currentHashCode; } @SuppressWarnings("unchecked") public void clear(){ modSize = INITIAL_LOG_SIZE; int tableSize = 1 << modSize; hashMask = tableSize - 1; data = (Entry<IValue>[]) new Entry[tableSize]; threshold = tableSize; load = 0; currentHashCode = 0; } @SuppressWarnings("unchecked") private void rehash(){ modSize++; int tableSize = 1 << modSize; hashMask = tableSize - 1; Entry<IValue>[] newData = (Entry<IValue>[]) new Entry[tableSize]; threshold = tableSize; Entry<IValue>[] oldData = data; for(int i = oldData.length - 1; i >= 0; i--){ Entry<IValue> entry = oldData[i]; if(entry != null){ // Determine the last unchanged entry chain. Entry<IValue> lastUnchangedEntryChain = entry; int newLastUnchangedEntryChainIndex = entry.hash & hashMask; Entry<IValue> 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.value, newData[position]); entry = entry.next; } } } data = newData; } private void ensureCapacity(){ if(load > threshold){ rehash(); } } public boolean add(IValue value){ ensureCapacity(); int hash = value.hashCode(); int position = hash & hashMask; Entry<IValue> currentStartEntry = data[position]; // Check if the value is already in here. if(currentStartEntry != null){ Entry<IValue> entry = currentStartEntry; do{ if(hash == entry.hash && entry.value.isEqual(value)){ return false; // Return false if it's already present. } entry = entry.next; }while(entry != null); } data[position] = new Entry<>(hash, value, currentStartEntry); // Insert the new entry. load++; currentHashCode ^= hash; // Update the current hashcode of this map. return true; } public boolean contains(Object object){ IValue value = (IValue) object; int hash = value.hashCode(); int position = hash & hashMask; Entry<IValue> entry = data[position]; while(entry != null){ if(hash == entry.hash && value.isEqual(entry.value)) return true; entry = entry.next; } return false; } public boolean remove(Object object){ IValue value = (IValue) object; int hash = value.hashCode(); int position = hash & hashMask; Entry<IValue> currentStartEntry = data[position]; if(currentStartEntry != null){ Entry<IValue> entry = currentStartEntry; do{ if(hash == entry.hash && entry.value.isEqual(value)){ Entry<IValue> e = data[position]; data[position] = entry.next; // Reconstruct the other entries (if necessary). while(e != entry){ data[position] = new Entry<>(e.hash, e.value, data[position]); e = e.next; } load--; currentHashCode ^= hash; // Update the current hashcode of this set. return true; } entry = entry.next; }while(entry != null); } return false; } public int size(){ return load; } public boolean isEmpty(){ return (load == 0); } public Iterator<IValue> iterator(){ return new SetIterator(data); } public boolean addAll(Collection<? extends IValue> collection){ boolean changed = false; Iterator<? extends IValue> collectionIterator = collection.iterator(); while(collectionIterator.hasNext()){ changed |= add(collectionIterator.next()); } return changed; } public boolean containsAll(Collection<?> collection){ Iterator<?> collectionIterator = collection.iterator(); while(collectionIterator.hasNext()){ if(!contains(collectionIterator.next())) return false; } return true; } public boolean retainAll(Collection<?> collection){ boolean changed = false; Iterator<IValue> valuesIterator = iterator(); while(valuesIterator.hasNext()){ IValue value = valuesIterator.next(); if(!collection.contains(value)){ remove(value); changed = true; } } return changed; } public boolean removeAll(Collection<?> collection){ boolean changed = false; Iterator<?> collectionIterator = collection.iterator(); while(collectionIterator.hasNext()){ Object value = collectionIterator.next(); changed |= remove(value); } return changed; } public Object[] toArray(){ Object[] values = new Object[load]; Iterator<IValue> valuesIterator = iterator(); int i = 0; while(valuesIterator.hasNext()){ values[i++] = valuesIterator.next(); } return values; } @SuppressWarnings("unchecked") public <T> T[] toArray(T[] array){ if(array.length < load) return (T[]) toArray(); Iterator<IValue> valuesIterator = iterator(); int i = 0; while(valuesIterator.hasNext()){ array[i++] = (T) valuesIterator.next(); } for(; i < load; i++){ array[i] = null; } return array; } public String toString(){ StringBuilder buffer = new StringBuilder(); buffer.append('{'); for(int i = 0; i < data.length; i++){ buffer.append('['); Entry<IValue> e = data[i]; if(e != null){ buffer.append(e); e = e.next; while(e != null){ buffer.append(','); buffer.append(e); e = e.next; } } buffer.append(']'); } buffer.append('}'); return buffer.toString(); } public int hashCode(){ return currentHashCode; } public boolean isEqual(ShareableValuesHashSet other){ if(other == null) return false; if(other.currentHashCode != currentHashCode) return false; if(other.size() != size()) return false; if(isEmpty()) return true; // No need to check if the sets are empty. Iterator<IValue> otherIterator = other.iterator(); while(otherIterator.hasNext()){ if(!contains(otherIterator.next())) return false; } return true; } private boolean containsTruelyEqual(IValue value){ int hash = value.hashCode(); int position = hash & hashMask; Entry<IValue> entry = data[position]; while(entry != null){ if(hash == entry.hash && value.equals(entry.value)) return true; entry = entry.next; } return false; } public boolean equals(Object o){ if(o == null) return false; if(o.getClass() == getClass()){ ShareableValuesHashSet other = (ShareableValuesHashSet) o; if(other.currentHashCode != currentHashCode) return false; if(other.size() != size()) return false; if(isEmpty()) return true; // No need to check if the sets are empty. Iterator<IValue> otherIterator = other.iterator(); while(otherIterator.hasNext()){ if(!containsTruelyEqual(otherIterator.next())) return false; } return true; } return false; } private static class Entry<V>{ public final int hash; public final V value; public final Entry<V> next; public Entry(int hash, V value, Entry<V> next){ super(); this.hash = hash; this.value = value; this.next = next; } public String toString(){ StringBuilder buffer = new StringBuilder(); buffer.append('<'); buffer.append(value); buffer.append('>'); return buffer.toString(); } } private static class SetIterator implements Iterator<IValue>{ private final Entry<IValue>[] data; private Entry<IValue> current; private int index; public SetIterator(Entry<IValue>[] entries){ super(); data = entries; index = data.length - 1; current = new Entry<>(0, null, data[index]); locateNext(); } private void locateNext(){ Entry<IValue> next = current.next; if(next != null){ current = next; return; } for(int i = index - 1; i >= 0 ; i--){ Entry<IValue> entry = data[i]; if(entry != null){ current = entry; index = i; return; } } current = null; index = 0; } public boolean hasNext(){ return (current != null); } public IValue next(){ if(!hasNext()) throw new NoSuchElementException("There are no more elements in this iteration"); IValue value = current.value; locateNext(); return value; } public void remove(){ throw new UnsupportedOperationException("This iterator doesn't support removal."); } } }