Mega Code Archive

 
Categories / Java / Collections Data Structure
 

A set acts like array

/*  * Copyright 2004-2010 the Seasar Foundation and the Others.  *  * Licensed under the Apache License, Version 2.0 (the "License");  * you may not use this file except in compliance with the License.  * You may obtain a copy of the License at  *  *     http://www.apache.org/licenses/LICENSE-2.0  *  * Unless required by applicable law or agreed to in writing, software  * distributed under the License is distributed on an "AS IS" BASIS,  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND,  * either express or implied. See the License for the specific language  * governing permissions and limitations under the License.  */ //package org.slim3.util; import java.io.Externalizable; import java.io.IOException; import java.io.ObjectInput; import java.io.ObjectOutput; import java.lang.reflect.Array; import java.util.AbstractMap; import java.util.AbstractSet; import java.util.Collection; import java.util.ConcurrentModificationException; import java.util.Iterator; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; /**  * A set acts like array.  *   * @author higa  * @param <E>  *            the element type  * @since 1.0.0  */ public class ArraySet<E> extends AbstractSet<E> implements Set<E>, Cloneable,     java.io.Serializable {   static final long serialVersionUID = -5024744406713321676L;   private transient ArrayMap<E, Object> map;   // Dummy value to associate with an Object in the backing Map   private static final Object PRESENT = new Object();   /**    * Constructor.    */   public ArraySet() {     map = new ArrayMap<E, Object>();   }   /**    * Constructor.    *     * @param c    *            the collection whose elements are to be placed into this set.    * @throws NullPointerException    *             if the specified collection is null.    */   public ArraySet(Collection<? extends E> c) throws NullPointerException {     if (c == null) {       throw new NullPointerException("The c parameter must not be null.");     }     map = new ArrayMap<E, Object>(Math.max((int) (c.size() / .75f) + 1, 16));     addAll(c);   }   /**    * Constructor.    *     * @param initialCapacity    *            the initial capacity of the hash table.    * @throws IllegalArgumentException    *             if the initial capacity is less than zero.    */   public ArraySet(int initialCapacity) {     map = new ArrayMap<E, Object>(initialCapacity);   }   /**    * Returns an element specified by the index.    *     * @param index    *            the index    * @return an element    */   public E get(int index) {     return map.getKey(index);   }   /**    * 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    */   @Override   public Iterator<E> 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).    */   @Override   public int size() {     return map.size();   }   /**    * Returns <tt>true</tt> if this set contains no elements.    *     * @return <tt>true</tt> if this set contains no elements.    */   @Override   public boolean isEmpty() {     return map.isEmpty();   }   /**    * Returns <tt>true</tt> if this set contains the specified element.    *     * @param o    *            element whose presence in this set is to be tested.    * @return <tt>true</tt> if this set contains the specified element.    */   @Override   public boolean contains(Object o) {     return map.containsKey(o);   }   /**    * Adds the specified element to this set if it is not already present.    *     * @param o    *            element to be added to this set.    * @return <tt>true</tt> if the set did not already contain the specified    *         element.    */   @Override   public boolean add(E o) {     return map.put(o, PRESENT) == null;   }   /**    * Removes the specified element from this set if it is present.    *     * @param o    *            object to be removed from this set, if present.    * @return <tt>true</tt> if the set contained the specified element.    */   @Override   public boolean remove(Object o) {     return map.remove(o) == PRESENT;   }   /**    * Removes all of the elements from this set.    */   @Override   public void clear() {     map.clear();   }   /**    * Returns a shallow copy of this <tt>HashSet</tt> instance: the elements    * themselves are not cloned.    *     * @return a shallow copy of this set.    */   @SuppressWarnings("unchecked")   @Override   public Object clone() {     try {       ArraySet<E> newSet = (ArraySet<E>) super.clone();       newSet.map = (ArrayMap<E, Object>) map.clone();       return newSet;     } catch (CloneNotSupportedException e) {       throw new InternalError();     }   }   /**    * Save the state of this <tt>HashSet</tt> instance to a stream (that is,    * serialize this set).    *     * @serialData The capacity of the backing <tt>HashMap</tt> 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 {     s.defaultWriteObject();     s.writeInt(map.size());     for (Iterator<E> i = map.keySet().iterator(); i.hasNext();) {       s.writeObject(i.next());     }   }   /**    * Reconstitute the <tt>HashSet</tt> 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();     int size = s.readInt();     map = new ArrayMap<E, Object>(size);     for (int i = 0; i < size; i++) {       E e = (E) s.readObject();       map.put(e, PRESENT);     }   } } class ArrayMap<K, V> extends AbstractMap<K, V> implements Map<K, V>, Cloneable,     Externalizable {   private static final long serialVersionUID = 1L;   /**    * The default initial capacity.    */   protected static final int INITIAL_CAPACITY = 17;   /**    * The default load factor.    */   protected static final float LOAD_FACTOR = 0.75f;   /**    * The threshold to extend the capacity.    */   protected transient int threshold;   /**    * The map table.    */   protected transient Entry<K, V>[] mapTable;   /**    * The list table.    */   protected transient Entry<K, V>[] listTable;   /**    * The number of entries.    */   protected transient int size = 0;   /**    * The set of entries.    */   protected transient Set<Map.Entry<K, V>> entrySet = null;   /**    * Constructor.    */   public ArrayMap() {     this(INITIAL_CAPACITY);   }   /**    * Constructor.    *     * @param initialCapacity    *            the initial capacity    */   public ArrayMap(int initialCapacity) {     if (initialCapacity <= 0) {       initialCapacity = INITIAL_CAPACITY;     }     mapTable = createArray(initialCapacity);     listTable = createArray(initialCapacity);     threshold = (int) (initialCapacity * LOAD_FACTOR);   }   /**    * Constructor.    *     * @param map    *            other map    */   public ArrayMap(Map<? extends K, ? extends V> map) {     this((int) (map.size() / LOAD_FACTOR) + 1);     putAll(map);   }   @Override   public final int size() {     return size;   }   @Override   public final boolean isEmpty() {     return size == 0;   }   @Override   public final boolean containsValue(Object value) {     return indexOf(value) >= 0;   }   /**    * Returns the index in the list table.    *     * @param value    *            the value    * @return the index    */   public final int indexOf(Object value) {     if (value != null) {       for (int i = 0; i < size; i++) {         if (value.equals(listTable[i].value)) {           return i;         }       }     } else {       for (int i = 0; i < size; i++) {         if (listTable[i].value == null) {           return i;         }       }     }     return -1;   }   @Override   public boolean containsKey(Object key) {     Entry<K, V>[] tbl = mapTable;     if (key != null) {       int hashCode = key.hashCode();       int index = (hashCode & 0x7FFFFFFF) % tbl.length;       for (Entry<K, V> e = tbl[index]; e != null; e = e.next) {         if (e.hashCode == hashCode && key.equals(e.key)) {           return true;         }       }     } else {       for (Entry<K, V> e = tbl[0]; e != null; e = e.next) {         if (e.key == null) {           return true;         }       }     }     return false;   }   @Override   public V get(Object key) {     Entry<K, V>[] tbl = mapTable;     if (key != null) {       int hashCode = key.hashCode();       int index = (hashCode & 0x7FFFFFFF) % tbl.length;       for (Entry<K, V> e = tbl[index]; e != null; e = e.next) {         if (e.hashCode == hashCode && key.equals(e.key)) {           return e.value;         }       }     } else {       for (Entry<K, V> e = tbl[0]; e != null; e = e.next) {         if (e.key == null) {           return e.value;         }       }     }     return null;   }   /**    * Returns the value for the index.    *     * @param index    *            the index    * @return the value    */   public final V get(int index) {     return getEntry(index).value;   }   /**    * Returns the key for the index.    *     * @param index    *            the index    * @return the key    */   public final K getKey(int index) {     return getEntry(index).key;   }   /**    * Returns the entry for the index.    *     * @param index    *            the index    * @return the entry    */   public final Entry<K, V> getEntry(int index) {     if (index >= size) {       throw new IndexOutOfBoundsException("Index:" + index + ", Size:"           + size);     }     return listTable[index];   }   @Override   public V put(K key, V value) {     int hashCode = 0;     int index = 0;     if (key != null) {       hashCode = key.hashCode();       index = (hashCode & 0x7FFFFFFF) % mapTable.length;       for (Entry<K, V> e = mapTable[index]; e != null; e = e.next) {         if ((e.hashCode == hashCode) && key.equals(e.key)) {           return swapValue(e, value);         }       }     } else {       for (Entry<K, V> e = mapTable[0]; e != null; e = e.next) {         if (e.key == null) {           return swapValue(e, value);         }       }     }     ensureCapacity();     index = (hashCode & 0x7FFFFFFF) % mapTable.length;     Entry<K, V> e = new Entry<K, V>(hashCode, key, value, mapTable[index]);     mapTable[index] = e;     listTable[size++] = e;     return null;   }   /**    * Sets the value for the index.    *     * @param index    *            the index    * @param value    *            the value    */   public final void set(int index, V value) {     getEntry(index).setValue(value);   }   @Override   public V remove(Object key) {     Entry<K, V> e = removeMap(key);     if (e != null) {       V value = e.value;       removeList(indexOf(e));       e.clear();       return value;     }     return null;   }   /**    * Removes the entry for the index.    *     * @param index    *            the index    * @return the removed value    */   public final V remove(int index) {     Entry<K, V> e = removeList(index);     V value = e.value;     removeMap(e.key);     e.value = null;     return value;   }   @Override   public void putAll(Map<? extends K, ? extends V> map) {     for (Iterator<? extends Map.Entry<? extends K, ? extends V>> i = map         .entrySet().iterator(); i.hasNext();) {       Map.Entry<? extends K, ? extends V> e = i.next();       put(e.getKey(), e.getValue());     }   }   @Override   public final void clear() {     for (int i = 0; i < mapTable.length; i++) {       mapTable[i] = null;     }     for (int i = 0; i < listTable.length; i++) {       listTable[i] = null;     }     size = 0;   }   /**    * Converts this object to an array.    *     * @return an array    */   public final Object[] toArray() {     Object[] array = new Object[size];     for (int i = 0; i < array.length; i++) {       array[i] = get(i);     }     return array;   }   /**    * Converts this object to an array.    *     * @param proto    *            the prototype    * @return an array    */   public final Object[] toArray(final Object proto[]) {     Object[] array = proto;     if (proto.length < size) {       array = (Object[]) Array.newInstance(proto.getClass()           .getComponentType(), size);     }     for (int i = 0; i < array.length; i++) {       array[i] = get(i);     }     if (array.length > size) {       array[size] = null;     }     return array;   }   @Override   @SuppressWarnings("unchecked")   public final boolean equals(Object o) {     if (!getClass().isInstance(o)) {       return false;     }     ArrayMap<K, V> e = (ArrayMap) o;     if (size != e.size) {       return false;     }     for (int i = 0; i < size; i++) {       if (!listTable[i].equals(e.listTable[i])) {         return false;       }     }     return true;   }   @Override   public final Set<Map.Entry<K, V>> entrySet() {     if (entrySet == null) {       entrySet = new AbstractSet<Map.Entry<K, V>>() {         @Override         public Iterator<Map.Entry<K, V>> iterator() {           return new ArrayMapIterator();         }         @Override         @SuppressWarnings("unchecked")         public boolean contains(Object o) {           if (!(o instanceof Entry)) {             return false;           }           Entry<K, V> entry = (Entry<K, V>) o;           int index = (entry.hashCode & 0x7FFFFFFF) % mapTable.length;           for (Entry<K, V> e = mapTable[index]; e != null; e = e.next) {             if (e.equals(entry)) {               return true;             }           }           return false;         }         @Override         @SuppressWarnings("unchecked")         public boolean remove(Object o) {           if (!(o instanceof Entry)) {             return false;           }           Entry<K, V> entry = (Entry) o;           return ArrayMap.this.remove(entry.key) != null;         }         @Override         public int size() {           return size;         }         @Override         public void clear() {           ArrayMap.this.clear();         }       };     }     return entrySet;   }   public final void writeExternal(final ObjectOutput out) throws IOException {     out.writeInt(listTable.length);     out.writeInt(size);     for (int i = 0; i < size; i++) {       out.writeObject(listTable[i].key);       out.writeObject(listTable[i].value);     }   }   @SuppressWarnings("unchecked")   public final void readExternal(final ObjectInput in) throws IOException,       ClassNotFoundException {     int num = in.readInt();     mapTable = createArray(num);     listTable = createArray(num);     threshold = (int) (num * LOAD_FACTOR);     int size = in.readInt();     for (int i = 0; i < size; i++) {       K key = (K) in.readObject();       V value = (V) in.readObject();       put(key, value);     }   }   @Override   public Object clone() {     ArrayMap<K, V> copy = new ArrayMap<K, V>();     copy.threshold = threshold;     copy.mapTable = mapTable;     copy.listTable = listTable;     copy.size = size;     return copy;   }   private final int indexOf(final Entry<K, V> entry) {     for (int i = 0; i < size; i++) {       if (listTable[i] == entry) {         return i;       }     }     return -1;   }   private final Entry<K, V> removeMap(Object key) {     int hashCode = 0;     int index = 0;     if (key != null) {       hashCode = key.hashCode();       index = (hashCode & 0x7FFFFFFF) % mapTable.length;       for (Entry<K, V> e = mapTable[index], prev = null; e != null; prev = e, e = e.next) {         if ((e.hashCode == hashCode) && key.equals(e.key)) {           if (prev != null) {             prev.next = e.next;           } else {             mapTable[index] = e.next;           }           return e;         }       }     } else {       for (Entry<K, V> e = mapTable[index], prev = null; e != null; prev = e, e = e.next) {         if ((e.hashCode == hashCode) && e.key == null) {           if (prev != null) {             prev.next = e.next;           } else {             mapTable[index] = e.next;           }           return e;         }       }     }     return null;   }   private final Entry<K, V> removeList(int index) {     Entry<K, V> e = listTable[index];     int numMoved = size - index - 1;     if (numMoved > 0) {       System.arraycopy(listTable, index + 1, listTable, index, numMoved);     }     listTable[--size] = null;     return e;   }   private final void ensureCapacity() {     if (size >= threshold) {       Entry<K, V>[] oldTable = listTable;       int newCapacity = oldTable.length * 2 + 1;       Entry<K, V>[] newMapTable = createArray(newCapacity);       Entry<K, V>[] newListTable = createArray(newCapacity);       threshold = (int) (newCapacity * LOAD_FACTOR);       System.arraycopy(oldTable, 0, newListTable, 0, size);       for (int i = 0; i < size; i++) {         Entry<K, V> old = oldTable[i];         int index = (old.hashCode & 0x7FFFFFFF) % newCapacity;         Entry<K, V> e = old;         old = old.next;         e.next = newMapTable[index];         newMapTable[index] = e;       }       mapTable = newMapTable;       listTable = newListTable;     }   }   private final V swapValue(final Entry<K, V> entry, final V value) {     V old = entry.value;     entry.value = value;     return old;   }   @SuppressWarnings("unchecked")   private Entry<K, V>[] createArray(final int length) {     return new Entry[length];   }   private class ArrayMapIterator implements Iterator<Map.Entry<K, V>> {     private int current = 0;     private int last = -1;     public boolean hasNext() {       return current != size;     }     public Map.Entry<K, V> next() {       try {         Entry<K, V> n = listTable[current];         last = current++;         return n;       } catch (IndexOutOfBoundsException e) {         throw new NoSuchElementException();       }     }     public void remove() {       if (last == -1) {         throw new IllegalStateException();       }       ArrayMap.this.remove(last);       if (last < current) {         current--;       }       last = -1;     }   }   private static class Entry<K, V> implements Map.Entry<K, V>, Externalizable {     private static final long serialVersionUID = 1L;     transient int hashCode;     transient K key;     transient V value;     transient Entry<K, V> next;     /**      * Constructor.      *       * @param hashCode      *            the hashCode      * @param key      *            the key      * @param value      *            the value      * @param next      *            the next      */     public Entry(final int hashCode, final K key, final V value,         final Entry<K, V> next) {       this.hashCode = hashCode;       this.key = key;       this.value = value;       this.next = next;     }     public K getKey() {       return key;     }     public V getValue() {       return value;     }     public V setValue(final V value) {       V oldValue = value;       this.value = value;       return oldValue;     }     /**      * Clears contents.      */     public void clear() {       key = null;       value = null;       next = null;     }     @Override     public boolean equals(final Object o) {       if (this == o) {         return true;       }       Entry<?, ?> e = (Entry<?, ?>) o;       return (key != null ? key.equals(e.key) : e.key == null)           && (value != null ? value.equals(e.value) : e.value == null);     }     @Override     public int hashCode() {       return hashCode;     }     @Override     public String toString() {       return key + "=" + value;     }     public void writeExternal(final ObjectOutput s) throws IOException {       s.writeInt(hashCode);       s.writeObject(key);       s.writeObject(value);       s.writeObject(next);     }     @SuppressWarnings("unchecked")     public void readExternal(final ObjectInput s) throws IOException,         ClassNotFoundException {       hashCode = s.readInt();       key = (K) s.readObject();       value = (V) s.readObject();       next = (Entry) s.readObject();     }   } }