/* 
 * 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]); 
  } 
 
} 
 
    
     
     
     
     
     
  
  |