package org.simantics.databoard.util; import java.util.AbstractSet; import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.IdentityHashMap; import java.util.Iterator; import java.util.Set; public class IdentityHashSet extends AbstractSet implements Set, Cloneable, java.io.Serializable { static final long serialVersionUID = -5024744406713321676L; private transient IdentityHashMap map; // Dummy value to associate with an Object in the backing Map private static final Object PRESENT = new Object(); /** * Constructs a new, empty set; the backing HashMap instance has * default initial capacity (16) and load factor (0.75). */ public IdentityHashSet() { map = new IdentityHashMap(); } /** * Constructs a new set containing the elements in the specified * collection. The HashMap is created with default load factor * (0.75) and an initial capacity sufficient to contain the elements in * the specified collection. * * @param c the collection whose elements are to be placed into this set * @throws NullPointerException if the specified collection is null */ public IdentityHashSet(Collection c) { map = new IdentityHashMap(Math.max((int) (c.size()/.75f) + 1, 16)); addAll(c); } /** * Constructs a new, empty set; the backing HashMap instance has * the specified initial capacity and default load factor (0.75). * * @param initialCapacity the initial capacity of the hash table * @throws IllegalArgumentException if the initial capacity is less * than zero */ public IdentityHashSet(int initialCapacity) { map = new IdentityHashMap(initialCapacity); } /** * Returns an iterator over the elements in this set. The elements * are returned in no particular order. * * @return an Iterator over the elements in this set * @see ConcurrentModificationException */ public Iterator iterator() { return map.keySet().iterator(); } /** * Returns the number of elements in this set (its cardinality). * * @return the number of elements in this set (its cardinality) */ public int size() { return map.size(); } /** * Returns true if this set contains no elements. * * @return true if this set contains no elements */ public boolean isEmpty() { return map.isEmpty(); } /** * Returns true if this set contains the specified element. * More formally, returns true if and only if this set * contains an element e such that * (o==null ? e==null : o.equals(e)). * * @param o element whose presence in this set is to be tested * @return true if this set contains the specified element */ public boolean contains(Object o) { return map.containsKey(o); } /** * Adds the specified element to this set if it is not already present. * More formally, adds the specified element e to this set if * this set contains no element e2 such that * (e==null ? e2==null : e.equals(e2)). * If this set already contains the element, the call leaves the set * unchanged and returns false. * * @param e element to be added to this set * @return true if this set did not already contain the specified * element */ public boolean add(E e) { return map.put(e, PRESENT)==null; } /** * Removes the specified element from this set if it is present. * More formally, removes an element e such that * (o==null ? e==null : o.equals(e)), * if this set contains such an element. Returns true if * this set contained the element (or equivalently, if this set * changed as a result of the call). (This set will not contain the * element once the call returns.) * * @param o object to be removed from this set, if present * @return true if the set contained the specified element */ public boolean remove(Object o) { return map.remove(o)==PRESENT; } /** * Removes all of the elements from this set. * The set will be empty after this call returns. */ public void clear() { map.clear(); } /** * Returns a shallow copy of this HashSet instance: the elements * themselves are not cloned. * * @return a shallow copy of this set */ @SuppressWarnings("unchecked") public Object clone() { try { IdentityHashSet newSet = (IdentityHashSet) super.clone(); newSet.map = (IdentityHashMap) map.clone(); return newSet; } catch (CloneNotSupportedException e) { throw new InternalError(); } } /** * Save the state of this HashSet instance to a stream (that is, * serialize it). * * @serialData The capacity of the backing HashMap instance * (int), and its load factor (float) are emitted, followed by * the size of the set (the number of elements it contains) * (int), followed by all of its elements (each an Object) in * no particular order. */ private void writeObject(java.io.ObjectOutputStream s) throws java.io.IOException { // Write out any hidden serialization magic s.defaultWriteObject(); // Write out size s.writeInt(map.size()); // Write out all elements in the proper order. for (Iterator i=map.keySet().iterator(); i.hasNext(); ) s.writeObject(i.next()); } /** * Reconstitute the HashSet instance from a stream (that is, * deserialize it). */ @SuppressWarnings("unchecked") private void readObject(java.io.ObjectInputStream s) throws java.io.IOException, ClassNotFoundException { // Read in any hidden serialization magic s.defaultReadObject(); // Read in size int size = s.readInt(); // Read in HashMap capacity and load factor and create backing HashMap map = new IdentityHashMap(size); // Read in all elements in the proper order. for (int i=0; i