Mega Code Archive

 
Categories / Java / Collections Data Structure
 

Array map

/*  * Copyright 2008-2009 the T2 Project ant 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.t2framework.commons.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.Iterator; import java.util.Map; import java.util.NoSuchElementException; import java.util.Set; /**  * Array map.  *   * @author shot  *   * @param <K>  * @param <V>  */ public class ArrayMap<K, V> extends AbstractMap<K, V> implements Map<K, V>,     Cloneable, Externalizable {   static final long serialVersionUID = 1L;   protected static final int INITIAL_CAPACITY = 17;   protected static final float LOAD_FACTOR = 0.75f;   protected transient int threshold;   protected transient Entry<K, V>[] mapTable;   protected transient Entry<K, V>[] listTable;   protected transient int size = 0;   protected transient Set<?> entrySet = null;   public static <K, V> ArrayMap<K, V> newArrayMap() {     return new ArrayMap<K, V>();   }   public ArrayMap() {     this(INITIAL_CAPACITY);   }   @SuppressWarnings("unchecked")   public ArrayMap(int initialCapacity) {     if (initialCapacity <= 0) {       initialCapacity = INITIAL_CAPACITY;     }     mapTable = new Entry[initialCapacity];     listTable = new Entry[initialCapacity];     threshold = (int) (initialCapacity * LOAD_FACTOR);   }   public ArrayMap(Map<K, V> map) {     this((int) (map.size() / LOAD_FACTOR) + 1);     putAll(map);   }   public final int size() {     return size;   }   public final boolean isEmpty() {     return size == 0;   }   public final boolean containsValue(Object value) {     return indexOf(value) >= 0;   }   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;   }   public boolean containsKey(final 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;   }   public V get(final 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;   }   public final V get(final int index) {     return getEntry(index).value_;   }   public final K getKey(final int index) {     return getEntry(index).key_;   }   public final Entry<K, V> getEntry(final int index) {     if (index >= size) {       throw new IndexOutOfBoundsException("Index:" + index + ", Size:"           + size);     }     return listTable[index];   }   public V put(final K key, final 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;   }   public final void set(final int index, final V value) {     getEntry(index).setValue(value);   }   public V remove(final Object key) {     Entry<K, V> e = removeMap(key);     if (e != null) {       V value = e.value_;       removeList(indexOf(e));       e.clear();       return value;     }     return null;   }   public final Object remove(int index) {     Entry<K, V> e = removeList(index);     Object value = e.value_;     removeMap(e.key_);     e.value_ = null;     return value;   }   @SuppressWarnings("unchecked")   @Override   public void putAll(Map<? extends K, ? extends V> map) {     for (Iterator<?> i = map.entrySet().iterator(); i.hasNext();) {       Map.Entry<K, V> e = (Map.Entry<K, V>) i.next();       put(e.getKey(), e.getValue());     }   }   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;   }   public final Object[] toArray() {     Object[] array = new Object[size];     for (int i = 0; i < array.length; i++) {       array[i] = get(i);     }     return 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;   }   @SuppressWarnings("unchecked")   public final boolean equals(Object o) {     if (!getClass().isInstance(o)) {       return false;     }     ArrayMap<K, V> e = (ArrayMap<K, V>) 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;   }   @SuppressWarnings("unchecked")   public final Set entrySet() {     if (entrySet == null) {       entrySet = new AbstractSet() {         public Iterator iterator() {           return new ArrayMapIterator();         }         public boolean contains(Object o) {           if (!(o instanceof Entry)) {             return false;           }           Entry entry = (Entry) o;           int index = (entry.hashCode_ & 0x7FFFFFFF)               % mapTable.length;           for (Entry e = mapTable[index]; e != null; e = e.next_) {             if (e.equals(entry)) {               return true;             }           }           return false;         }         public boolean remove(Object o) {           if (!(o instanceof Entry)) {             return false;           }           Entry entry = (Entry) o;           return ArrayMap.this.remove(entry.key_) != null;         }         public int size() {           return size;         }         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 = new Entry[num];     listTable = new Entry[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);     }   }   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;   }   @SuppressWarnings("unchecked")   private final void ensureCapacity() {     if (size >= threshold) {       Entry<K, V>[] oldTable = listTable;       int newCapacity = oldTable.length * 2 + 1;       Entry<K, V>[] newMapTable = new Entry[newCapacity];       Entry<K, V>[] newListTable = new Entry[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;   }   private class ArrayMapIterator implements Iterator<Entry<K, V>> {     private int current_ = 0;     private int last_ = -1;     public boolean hasNext() {       return current_ != size;     }     public 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 = -6625980241350717177L;     transient int hashCode_;     transient K key_;     transient V value_;     transient Entry<K, V> next_;     public Entry(final int hashCode, final K key, final V value,         final Entry<K, V> next) {       hashCode_ = hashCode;       key_ = key;       value_ = value;       next_ = next;     }     public K getKey() {       return key_;     }     public V getValue() {       return value_;     }     public V setValue(final V value) {       V oldValue = value_;       value_ = value;       return oldValue;     }     public void clear() {       key_ = null;       value_ = null;       next_ = null;     }     @SuppressWarnings("unchecked")     public boolean equals(final Object o) {       if (this == o) {         return true;       }       Entry<K, V> e = (Entry<K, V>) o;       return (key_ != null ? key_.equals(e.key_) : e.key_ == null)           && (value_ != null ? value_.equals(e.value_)               : e.value_ == null);     }     public int hashCode() {       return hashCode_;     }     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();     }   } } ------------------- /*  * Copyright 2008-2009 the T2 Project ant 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.t2framework.commons.util; import org.t2framework.commons.util.ArrayMap; import junit.framework.TestCase; public class ArrayMapTest extends TestCase {   public void test1() throws Exception {     ArrayMap<String, String> map = ArrayMap.newArrayMap();     map.put("aaa", "AAA");     map.put("bbb", "BBB");     assertEquals("AAA", map.get(0));     assertEquals("BBB", map.get("bbb"));   }   public void test2() throws Exception {     ArrayMap<String, String> map = ArrayMap.newArrayMap();     map.put("aaa", "AAA");     map.put("bbb", "BBB");     ArrayMap<String, String> map2 = ArrayMap.newArrayMap();     map2.put("ccc", "CCC");     map2.putAll(map);     assertEquals("CCC", map2.get(0));     assertEquals("BBB", map2.get("bbb"));   }   public void test3() throws Exception {     ArrayMap<String, String> map = ArrayMap.newArrayMap();     {       map.put("a", "A");       map.put("b", "B");       map.put("c", "C");     }     String key = map.getKey(0);     assertEquals("a", key);     assertTrue(map.containsKey("b"));     assertTrue(map.containsValue("A"));     assertEquals("C", map.get("c"));     assertTrue(map.indexOf("B") == 1);     Object[] array = map.toArray();     assertTrue(array.length == 3);     assertEquals("B", array[1]);   } }