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();
}
}
}
Related examples in the same category
1. | Set, HashSet and TreeSet | | |
2. | Things you can do with Sets | | |
3. | Set operations: union, intersection, difference, symmetric difference, is subset, is superset | | |
4. | Set implementation that use == instead of equals() | | |
5. | Set that compares object by identity rather than equality | | |
6. | Set union and intersection | | |
7. | Set with values iterated in insertion order. | | |
8. | Putting your own type in a Set | | |
9. | Use set | | |
10. | Another Set demo | | |
11. | Set subtraction | | |
12. | Working with HashSet and TreeSet | | |
13. | TreeSet Demo | | |
14. | Show the union and intersection of two sets | | |
15. | Demonstrate the Set interface | | |
16. | Array Set extends AbstractSet | | |
17. | Sync Test | | |
18. | Set Copy | | |
19. | Set and TreeSet | | |
20. | Tail | | |
21. | What you can do with a TreeSet | | |
22. | Remove all elements from a set | | |
23. | Copy all the elements from set2 to set1 (set1 += set2), set1 becomes the union of set1 and set2 | | |
24. | Remove all the elements in set1 from set2 (set1 -= set2), set1 becomes the asymmetric difference of set1 and set2 | | |
25. | Get the intersection of set1 and set2, set1 becomes the intersection of set1 and set2 | | |
26. | Extend AbstractSet to Create Simple Set | | |
27. | Int Set | | |
28. | One Item Set | | |
29. | Small sets whose elements are known to be unique by construction | | |
30. | List Set implements Set | | |
31. | Converts a char array to a Set | | |
32. | Converts a string to a Set | | |
33. | Implements the Set interface, backed by a ConcurrentHashMap instance | | |
34. | An IdentitySet that uses reference-equality instead of object-equality | | |
35. | An implementation of the java.util.Stack based on an ArrayList instead of a Vector, so it is not synchronized to protect against multi-threaded access. | | |
36. | A thin wrapper around a List transforming it into a modifiable Set. | | |
37. | A thread-safe Set that manages canonical objects | | |
38. | This program uses a set to print all unique words in System.in | | |
39. | Indexed Set | | |
40. | An ObjectToSet provides a java.util.Map from arbitrary objects to objects of class java.util.Set. | | |
41. | Sorted Multi Set | | |
42. | Fixed Size Sorted Set | | |
43. | Set operations | | |
44. | A NumberedSet is a generic container of Objects where each element is identified by an integer id. | | |
45. | Set which counts the number of times a values are added to it. | | |
46. | Set which counts the number of times a values are added to it and assigns them a unique positive index. | | |
47. | Indexed Set | | |
48. | Implements a Bloom filter. Which, as you may not know, is a space-efficient structure for storing a set. | | |
49. | Implementation of disjoint-set data structure | | |
50. | Call it an unordered list or a multiset, this collection is defined by oxymorons | | |