a7524cd415755df3c82070e00929436a7837633f
[org.ibex.core.git] / src / org / xwt / util / Hash.java
1 // Copyright 2003 Adam Megacz, see the COPYING file for licensing [GPL]
2 package org.xwt.util;
3
4 import java.util.*;
5
6 /** Implementation of an unsynchronized hash table, with one or two
7  *  keys, using Radke's quadradic residue linear probing instead of
8  *  buckets to minimize object count (less allocations, faster GC).
9  *  See C. Radke, Communications of the ACM, 1970, 103-105
10  */
11 public class Hash {
12
13     /** this object is inserted as key in a slot when the
14      *  corresponding value is removed -- this ensures that the
15      *  probing sequence for any given key remains the same even if
16      *  other keys are removed.
17      */
18     private static Object placeholder = new Object();
19
20     /** the number of entries with at least one non-null key */
21     private int usedslots = 0;
22
23     /** the number of entries with non-null values */
24     protected int size = 0;
25
26     /** when num_slots < loadFactor * size, rehash into a bigger table */
27     private final int loadFactor;
28
29     /** primary keys */
30     private Object[] keys1 = null;
31
32     /** secondary keys; null if no secondary key has ever been added */
33     private Object[] keys2 = null;
34
35     /** the values for the table */
36     private Object[] vals = null;
37     
38     /** the number of entries with a non-null value */
39     public int size() { return size; }
40
41     /** empties the table */
42     public void clear() {
43         size = 0;
44         usedslots = 0;
45         for(int i=0; i<vals.length; i++) {
46             vals[i] = null;
47             keys1[i] = null;
48             if (keys2 != null) keys2[i] = null;
49         }
50     }
51
52     /** returns all the primary keys in the table */
53     public Enumeration keys() {
54         // FIXME!!!
55         return null;
56     }
57
58     public Hash() { this(25, 3); }
59     public Hash(int initialcapacity, int loadFactor) {
60         // using a pseudoprime in the form 4x+3 ensures full coverage
61         initialcapacity = initialcapacity / 4;
62         initialcapacity = 4 * initialcapacity + 3;
63         keys1 = new Object[initialcapacity];
64         vals = new Object[initialcapacity];
65         this.loadFactor = loadFactor;
66     }
67     
68     public void remove(Object k1) { remove(k1, null); }
69     public void remove(Object k1, Object k2) { put_(k1, k2, null); }
70
71     private void rehash() {
72         Object[] oldkeys1 = keys1;
73         Object[] oldkeys2 = keys2;
74         Object[] oldvals = vals;
75         keys1 = new Object[oldvals.length * 2];
76         keys2 = oldkeys2 == null ? null : new Object[oldvals.length * 2];
77         vals = new Object[oldvals.length * 2];
78         size = 0;
79         usedslots = 0;
80         for(int i=0; i<oldvals.length; i++)
81             if (((oldkeys1[i] != null && oldkeys1[i] != placeholder) || (oldkeys2 != null && oldkeys2[i] != null)) && oldvals[i] != null)
82                 put_(oldkeys1[i], oldkeys2 == null ? null : oldkeys2[i], oldvals[i]);
83     }
84
85     public Object get(Object k1) { return get(k1, null); }
86     public Object get(Object k1, Object k2) {
87         if (k2 != null && keys2 == null) return null;
88         int hash = (k1 == null ? 0 : k1.hashCode()) ^ (k2 == null ? 0 : k2.hashCode());
89         int dest = Math.abs(hash) % vals.length;
90         int odest = dest;
91         int tries = 1;
92         boolean plus = true;
93         while (keys1[dest] != null || (keys2 != null && keys2[dest] != null)) {
94             Object hk1 = keys1[dest];
95             Object hk2 = keys2 == null ? null : keys2[dest];
96             if ((k1 == hk1 || (k1 != null && hk1 != null && k1.equals(hk1))) &&
97                 (k2 == hk2 || (k2 != null && hk2 != null && k2.equals(hk2)))) {
98                 return vals[dest];
99             }
100             dest = Math.abs((odest + (plus ? 1 : -1 ) * tries * tries) % vals.length);
101             if (plus) tries++;
102             plus = !plus;
103         }
104         return null;
105     }
106
107     public void put(Object k1, Object v) { put(k1, null, v); }
108     public void put(Object k1, Object k2, Object v) { put_(k1, k2, v); }
109     private void put_(Object k1, Object k2, Object v) {
110         if (usedslots * loadFactor > vals.length) rehash();
111         int hash = (k1 == null ? 0 : k1.hashCode()) ^ (k2 == null ? 0 : k2.hashCode());
112         int dest = Math.abs(hash) % vals.length;
113         int odest = dest;
114         boolean plus = true;
115         int tries = 1;
116         while (true) {
117             Object hk1 = keys1[dest];
118             Object hk2 = keys2 == null ? null : keys2[dest];
119             if (hk1 == null && hk2 == null) {                                         // empty slot
120                 if (v == null) return;
121                 size++;
122                 usedslots++;
123                 break;
124             }
125
126             if ((k1 == hk1 || (k1 != null && hk1 != null && k1.equals(hk1))) &&       // replacing former entry
127                 (k2 == hk2 || (k2 != null && hk2 != null && k2.equals(hk2)))) {
128
129                 // we don't actually remove things from the table; rather, we insert a placeholder
130                 if (v == null) {
131                     k1 = placeholder;
132                     k2 = null;
133                     size--;
134                 }
135                 break;
136             }
137
138             dest = Math.abs((odest + (plus ? 1 : -1 ) * tries * tries) % vals.length);
139             if (plus) tries++;
140             plus = !plus;
141         }
142
143         keys1[dest] = k1;
144         if (k2 != null && keys2 == null) keys2 = new Object[keys1.length];
145         if (keys2 != null) keys2[dest] = k2;
146         vals[dest] = v;
147     }
148
149 }
150
151