source: branches/0.22.x/abcl/src/org/armedbear/lisp/HashTable.java

Last change on this file was 12431, checked in by Mark Evenson, 15 years ago

Replace FastStringBuffer? with java.lang.StringBuilder?.

Phil Hudson suggested in Feburary 2009 that "[FastStringBuffer?] should
be removed with all references to it replaced with
java.lang.StringBuilder? once enough confidence in this change has been
gained." After almost a year of using FastStringBuffer? as a delagate
for StringBuilder?, that confidence has indeed been gained.

One subtlety for use of StringBuilder?: there is no

StringBuilder?(char)

constructor, so use

StringBuilder?(String.valueOf(c))

to construct a new StringBuilder? containing a single char. Otherwise
that char will get promoted to an int, and you will invoke

StringBuilder?(int capacity)

which will "swallow" the first character that you thought you were adding.

  • Property svn:eol-style set to native
  • Property svn:keywords set to Id
File size: 7.8 KB
Line 
1/*
2 * HashTable.java
3 *
4 * Copyright (C) 2002-2007 Peter Graves
5 * $Id: HashTable.java 12431 2010-02-08 08:05:15Z mevenson $
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11 *
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA  02110-1301, USA.
20 *
21 * As a special exception, the copyright holders of this library give you
22 * permission to link this library with independent modules to produce an
23 * executable, regardless of the license terms of these independent
24 * modules, and to copy and distribute the resulting executable under
25 * terms of your choice, provided that you also meet, for each linked
26 * independent module, the terms and conditions of the license of that
27 * module.  An independent module is a module which is not derived from
28 * or based on this library.  If you modify this library, you may extend
29 * this exception to your version of the library, but you are not
30 * obligated to do so.  If you do not wish to do so, delete this
31 * exception statement from your version.
32 */
33
34package org.armedbear.lisp;
35
36import static org.armedbear.lisp.Lisp.*;
37
38public abstract class HashTable extends LispObject
39{
40  private static final int DEFAULT_SIZE = 16;
41
42  protected static final float loadFactor = 0.75f;
43
44  protected final LispObject rehashSize;
45  protected final LispObject rehashThreshold;
46
47  // The rounded product of the capacity and the load factor. When the number
48  // of elements exceeds the threshold, the implementation calls rehash().
49  protected int threshold;
50
51  // Array containing the actual key-value mappings.
52  protected HashEntry[] buckets;
53
54  // The number of key-value pairs.
55  protected int count;
56
57  protected HashTable()
58  {
59    rehashSize = new SingleFloat(1.5f); // FIXME
60    rehashThreshold = new SingleFloat(0.75f); // FIXME
61    buckets = new HashEntry[DEFAULT_SIZE];
62    threshold = (int) (DEFAULT_SIZE * loadFactor);
63  }
64
65  protected HashTable(int size, LispObject rehashSize,
66                      LispObject rehashThreshold)
67  {
68    this.rehashSize = rehashSize;
69    this.rehashThreshold = rehashThreshold;
70    buckets = new HashEntry[size];
71    threshold = (int) (size * loadFactor);
72  }
73
74  protected static int calculateInitialCapacity(int size)
75  {
76    int capacity = 1;
77    while (capacity < size)
78      capacity <<= 1;
79    return capacity;
80  }
81
82  public final LispObject getRehashSize()
83  {
84    return rehashSize;
85  }
86
87  public final LispObject getRehashThreshold()
88  {
89    return rehashThreshold;
90  }
91
92  public int getSize()
93  {
94    return buckets.length;
95  }
96
97  public int getCount()
98  {
99    return count;
100  }
101
102  public abstract Symbol getTest();
103
104  @Override
105  public LispObject typeOf()
106  {
107    return Symbol.HASH_TABLE;
108  }
109
110  @Override
111  public LispObject classOf()
112  {
113    return BuiltInClass.HASH_TABLE;
114  }
115
116  @Override
117  public LispObject typep(LispObject type)
118  {
119    if (type == Symbol.HASH_TABLE)
120      return T;
121    if (type == BuiltInClass.HASH_TABLE)
122      return T;
123    return super.typep(type);
124  }
125
126  @Override
127  public boolean equalp(LispObject obj)
128  {
129    if (this == obj)
130      return true;
131    if (obj instanceof HashTable)
132      {
133        HashTable ht = (HashTable) obj;
134        if (count != ht.count)
135          return false;
136        if (getTest() != ht.getTest())
137          return false;
138        LispObject entries = ENTRIES();
139        while (entries != NIL)
140          {
141            LispObject entry = entries.car();
142            LispObject key = entry.car();
143            LispObject value = entry.cdr();
144            if (!value.equalp(ht.get(key)))
145              return false;
146            entries = entries.cdr();
147          }
148        return true;
149      }
150    return false;
151  }
152
153  @Override
154  public LispObject getParts()
155  {
156    LispObject parts = NIL;
157    for (int i = 0; i < buckets.length; i++)
158      {
159        HashEntry e = buckets[i];
160        while (e != null)
161          {
162            parts = parts.push(new Cons("KEY [bucket " + i + "]", e.key));
163            parts = parts.push(new Cons("VALUE", e.value));
164            e = e.next;
165          }
166      }
167    return parts.nreverse();
168  }
169
170  public synchronized void clear()
171  {
172    for (int i = buckets.length; i-- > 0;)
173      buckets[i] = null;
174    count = 0;
175  }
176
177  // gethash key hash-table &optional default => value, present-p
178  public synchronized LispObject gethash(LispObject key)
179
180  {
181    LispObject value = get(key);
182    final LispObject presentp;
183    if (value == null)
184      value = presentp = NIL;
185    else
186      presentp = T;
187    return LispThread.currentThread().setValues(value, presentp);
188  }
189
190  // gethash key hash-table &optional default => value, present-p
191  public synchronized LispObject gethash(LispObject key,
192                                         LispObject defaultValue)
193
194  {
195    LispObject value = get(key);
196    final LispObject presentp;
197    if (value == null)
198      {
199        value = defaultValue;
200        presentp = NIL;
201      }
202    else
203      presentp = T;
204    return LispThread.currentThread().setValues(value, presentp);
205  }
206
207  public synchronized LispObject gethash1(LispObject key)
208
209  {
210    final LispObject value = get(key);
211    return value != null ? value : NIL;
212  }
213
214  public synchronized LispObject puthash(LispObject key, LispObject newValue)
215
216  {
217    put(key, newValue);
218    return newValue;
219  }
220
221  // remhash key hash-table => generalized-boolean
222  public synchronized LispObject remhash(LispObject key)
223
224  {
225    // A value in a Lisp hash table can never be null, so...
226    return remove(key) != null ? T : NIL;
227  }
228
229  @Override
230  public String writeToString()
231  {
232    if (Symbol.PRINT_READABLY.symbolValue(LispThread.currentThread()) != NIL)
233      {
234        error(new PrintNotReadable(list(Keyword.OBJECT, this)));
235        return null; // Not reached.
236      }
237    StringBuilder sb = new StringBuilder(getTest().writeToString());
238    sb.append(' ');
239    sb.append(Symbol.HASH_TABLE.writeToString());
240    sb.append(' ');
241    sb.append(count);
242    if (count == 1)
243      sb.append(" entry");
244    else
245      sb.append(" entries");
246    sb.append(", ");
247    sb.append(buckets.length);
248    sb.append(" buckets");
249    return unreadableString(sb.toString());
250  }
251
252  public abstract LispObject get(LispObject key);
253
254  public abstract void put(LispObject key, LispObject value)
255   ;
256
257  public abstract LispObject remove(LispObject key);
258
259  protected abstract void rehash();
260
261  // Returns a list of (key . value) pairs.
262  public LispObject ENTRIES()
263  {
264    LispObject list = NIL;
265    for (int i = buckets.length; i-- > 0;)
266      {
267        HashEntry e = buckets[i];
268        while (e != null)
269          {
270            list = new Cons(new Cons(e.key, e.value), list);
271            e = e.next;
272          }
273      }
274    return list;
275  }
276
277  public LispObject MAPHASH(LispObject function)
278  {
279    for (int i = buckets.length; i-- > 0;)
280      {
281        HashEntry e = buckets[i];
282        while (e != null)
283          {
284            function.execute(e.key, e.value);
285            e = e.next;
286          }
287      }
288    return NIL;
289  }
290
291  protected static class HashEntry
292  {
293    LispObject key;
294    LispObject value;
295    HashEntry next;
296
297    HashEntry(LispObject key, LispObject value)
298    {
299      this.key = key;
300      this.value = value;
301    }
302  }
303
304  // For EQUALP hash tables.
305  @Override
306  public int psxhash()
307  {
308    long result = 2062775257; // Chosen at random.
309    result = mix(result, count);
310    result = mix(result, getTest().sxhash());
311    return (int) (result & 0x7fffffff);
312  }
313}
Note: See TracBrowser for help on using the repository browser.