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