1 // Copyright 2000-2005 the Contributors, as shown in the revision logs.
2 // Licensed under the Apache Public Source License 2.0 ("the License").
3 // You may not use this file except in compliance with the License.
7 import java.io.Serializable;
9 public interface Basket extends Serializable {
10 public boolean containsValue(Object object);
13 public void remove(Object object);
15 public interface List extends Basket {
16 public void add(Object object);
17 public void add(int index, Object object);
18 public Object set(int index, Object object);
19 public Object get(int index);
20 public Object remove(int index);
21 public int indexOf(Object object);
22 public void reverse();
23 public void sort(CompareFunc c);
26 public interface RandomAccess extends List { }
28 public interface Queue extends Basket {
30 //public void enqueue(Object o);
31 //public Object dequeue();
34 public interface Stack extends Basket {
37 public void push(Object object);
40 public interface Map extends Basket {
41 public boolean containsKey(Object key);
42 public Object get(Object key);
43 public Object put(Object key, Object value);
46 public interface CompareFunc {
47 public int compare(Object a, Object b);
51 // Implementations ////////////////////////////////////////////////////////
53 public class Array implements RandomAccess, Stack, Queue {
54 private static final long serialVersionUID = 1233428092L;
59 public Array() { this(10); }
60 public Array(int initialCapacity) { o = new Object[initialCapacity]; }
61 public Array(Object entry) { this(1); add(entry); }
63 public void add(Object obj) { add(size, obj); }
64 public void add(int i, Object obj) {
66 if (size - 1 > i) System.arraycopy(o, i, o, size, size - i - 1);
69 public Object set(int i, Object obj) {
70 if (i >= size) throw new IndexOutOfBoundsException(
71 "index "+i+" is beyond list boundary "+size);
72 Object old = o[i]; o[i] = obj; return old;
74 public Object get(int i) {
75 if (i >= size) throw new IndexOutOfBoundsException(
76 "index "+i+" is beyond list boundary "+size);
79 public Object remove(int i) {
80 if (i >= size || i < 0) throw new IndexOutOfBoundsException(
81 "index "+i+" is beyond list boundary "+size);
82 Object old = o[i]; o[i] = null;
83 if (size - 1 > i) System.arraycopy(o, i + 1, o, i, size - i - 1);
86 public void remove(Object obj) { remove(indexOf(obj)); }
88 public int indexOf(Object obj) {
89 for (int i=0; i < size; i++)
90 if ((obj == null && o[i] == null) || obj.equals(o[i])) return i;
94 public boolean containsValue(Object obj) {
95 for (int i=0; i < size; i++)
96 if ((obj == null && o[i] == null) || obj.equals(o[i])) return true;
99 public void clear() { for (int i=0; i < size; i++) o[i] = null; size = 0; }
100 public int size() { return size; }
101 public void size(int s) {
102 if (o.length >= s) return;
103 Object[] newo = new Object[s];
104 System.arraycopy(o, 0, newo, 0, size);
108 public void reverse() {
109 Object tmp; int max = (int)Math.floor((double)size / 2);
110 for (int i=0; i < size; i++) { tmp = o[i]; o[i] = o[size - i]; o[size - i] = tmp; }
113 public void sort(CompareFunc c) { sort(this, null, c, 0, size); }
115 public static void sort(Array a, Array b, CompareFunc c, int start, int end) {
116 Object tmpa, tmpb = null;
117 if(start >= end) return;
119 for(int i=start+1;i<=end;i++) {
121 if (b != null) tmpb = b.o[i];
123 for(j=i-1;j>=start;j--) {
124 if(c.compare(a.o[j],tmpa) <= 0) break;
126 if (b != null) b.o[j+1] = b.o[j];
129 if (b != null) b.o[j+1] = tmpb;
133 Object pivot = a.o[end];
137 while(c.compare(a.o[++lo],pivot) < 0) { }
138 while((hi > lo) && c.compare(a.o[--hi],pivot) > 0) { }
140 if (b != null) swap(b, lo,hi);
144 if (b != null) swap(b, lo,end);
145 sort(a, b, c, start, lo-1);
146 sort(a, b, c, lo+1, end);
149 private static final void swap(Array vec, int a, int b) {
151 Object tmp = vec.o[a];
157 public Object peek() {
158 if (size < 1) throw new IndexOutOfBoundsException("array is empty");
161 public Object pop() { return remove(size - 1); }
162 public void push(Object o) { add(o); }
165 //public class Tree implements RandomAccess { } FIXME
167 //public class IndexedTree extends Tree { } FIXME
169 /** Implementation of a hash table using Radke's quadratic residue
170 * linear probing. Uses a single array to store all entries.
172 * <p>See C. Radke, Communications of the ACM, 1970, 103-105</p>
174 * @author adam@ibex.org, crawshaw@ibex.org
176 public abstract class Hash implements Basket {
177 private static final long serialVersionUID = 3948384093L;
179 /** Used internally to record used slots. */
180 protected final Object placeholder = new java.io.Serializable() {
181 private static final long serialVersionUID = 1331L;
184 /** When <tt>loadFactor * usedslots > num_slots</tt>, call
185 * <tt>rehash()</tt>. */
186 protected final float loadFactor;
188 /** Used to determine the number of array slots required by each
190 protected final int indexmultiple;
192 /** Number of entries with non-null key (includes placeholder slots). */
193 protected int usedslots = 0;
195 /** Number of currently available entry slots. */
196 protected int numslots = 0;
198 /** Number of currently active entries. */
199 protected int size = 0;
201 /** Array of mappings.
203 * <p>Each map requires multiple slots in the array, and subclasses
204 * can vary the number of required slots without reimplementing all
205 * the functions of this class by changing the value of
206 * <tt>indexmultiple</tt>.</p>
208 * Default implementation uses <tt>indexmultiple == 1</tt>, and
209 * stores only the keys in <tt>entries</tt>.
211 protected Object[] entries = null;
213 public Hash(int indexmultiple, int initialCapacity, float loadFactor) {
214 // using a pseudoprime in the form 4x+3 ensures full coverage
215 initialCapacity = initialCapacity / 4;
216 initialCapacity = 4 * initialCapacity + 3;
217 this.numslots = initialCapacity;
218 this.loadFactor = loadFactor;
219 this.indexmultiple = indexmultiple;
221 entries = new Object[initialCapacity * indexmultiple];
224 public int size() { return size; }
225 public void clear() {
226 for (int i = usedslots * indexmultiple; i >= 0; i--) entries[i] = null;
227 size = usedslots = 0;
230 public boolean containsKey(Object k) { return indexOf(k) >= 0; }
232 /** <b>Warning:</b> This function is equivalent here to
233 * <tt>containsKey()</tt>. For a value map, use Basket.HashMap. */
234 public boolean containsValue(Object k) { return containsKey(k); }
236 public void remove(Object k) { remove(indexOf(k)); }
238 public void remove(int dest) {
239 if (dest < 0) return;
242 // instead of removing, insert a placeholder
243 entries[dest] = placeholder;
244 for (int inc=1; inc < indexmultiple; inc++) entries[dest + inc] = null;
248 /** Insert new entry or replace existing at given index. */
249 public int put(int dest, Object k) {
250 if (usedslots + 1 > numslots * loadFactor) rehash();
251 if (dest >= 0) { // update existing entry
253 } else { // new entry
254 dest = -1 * dest - 1;
256 for (int inc = 1; inc < indexmultiple; inc++)
257 entries[dest + inc] = null;
263 /** Called immediately after the addition of a new entry. */
264 protected abstract void entryAdded(int dest);
266 /** Called immediately after the update of a current entry. */
267 protected abstract void entryUpdated(int dest);
269 /** Called immediately before the removal of an entry. */
270 protected abstract void entryRemoved(int dest);
272 /** Compares two keys for equality. <tt>userKey</tt> is the key
273 * passed to the map, <tt>storedKey</tt> is from the map.
275 * <p>Default implementation provides standard Java equality
276 * testing, <tt>k1 == null ? k2 == null : k1.equals(k2)</tt>.</p>
278 protected boolean equals(Object userKey, Object storedKey) {
279 return userKey == null ? storedKey == null : userKey.equals(storedKey);
282 /** Returns the array position in <tt>entries</tt>, adjusted for
283 * <tt>indexmultiple</tt>, where <tt>k</tt> is/should be stored
284 * using Radke's quadratic residue linear probing.
286 * <p><tt>entries[0]</tt> is a hard coded exception for the null
289 * <p>If the key is not found, this function returns
290 * <tt>(-1 * indexPosition) - 1</tt>, where <tt>indexPosition</tt>
291 * is the array position where the mapping should be stored.</p>
293 * <p>Uses <tt>placeholder</tt> as a placeholder object, and
294 * compares keys using <tt>equals(Object, Object)</tt>.</p>
296 protected int indexOf(Object k) {
297 // special case null key
298 if (k == null) return equals(placeholder, entries[0]) ? -1 : 0;
300 int hash = k == null ? 0 : k.hashCode();
301 final int orig = Math.abs(hash) % numslots;
302 int dest = orig * indexmultiple;
306 while (entries[dest] != null) {
307 if (equals(k, entries[dest])) return dest;
308 dest = Math.abs((orig + (plus ? 1 : -1) * tries * tries) % numslots) * indexmultiple;
312 return -1 * dest - 1;
315 /** Doubles the available entry space, first by packing the data
316 * set (removes <tt>placeholder</tt> references) and if necessary
317 * by increasing the size of the <tt>entries</tt> array.
319 protected void rehash() {
320 int oldnumslots = numslots;
321 Object[] oldentries = entries;
323 if (size * 2 > usedslots) numslots *= 2;
324 entries = new Object[numslots * indexmultiple];
326 for (int i=0; i < oldnumslots; i++) {
327 int pos = i * indexmultiple;
328 if (pos > 0 && oldentries[pos] == null) continue;
329 if (oldentries[pos] == placeholder) continue;
331 // dont adjust any of the support entries
332 int dest = indexOf(oldentries[pos]);
333 dest = -1 * dest - 1; size++; usedslots++; // always new entry
334 for (int inc=0; inc < indexmultiple; inc++)
335 entries[dest + inc] = oldentries[pos + inc];
340 public class HashMap extends Hash implements Map {
341 private static final long serialVersionUID = 2348905755L;
343 public HashMap() { this(16, 0.75F); }
344 public HashMap(int cap, float load) { super(2, cap, load); }
346 public Object get(Object key) { int i = indexOf(key); return i >= 0 ? entries[i + 1] : null; }
347 public Object put(Object key, Object value) {
349 int dest = indexOf(key);
350 if (dest >= 0) old = entries[(-1 * dest - 1) + 1];
351 dest = put(dest, key);
352 entries[dest + 1] = value;
356 protected void entryAdded(int dest) {}
357 protected void entryUpdated(int dest) {}
358 protected void entryRemoved(int dest) {}
360 public boolean containsKey(Object k) { return super.containsValue(k); }
362 public boolean containsValue(Object v) {
363 for (int i=0; i < numslots; i++)
364 if ((i == 0 || entries[i * indexmultiple] != null) && // exception for null key
365 !equals(placeholder, entries[i * indexmultiple]) &&
366 v == null ? entries[i + 1] == null : v.equals(entries[i + 1]))