Not logged in.  Login/Logout/Register | List snippets | | Create snippet | Upload image | Upload data

443
LINES

< > BotCompany Repo | #1031040 // CompactHashMap - using only one array, no modCount. synchronized

JavaX fragment (include) [tags: use-pretranspiled]

Libraryless. Click here for Pure Java version (8470L/47K).

1  
// size:
2  
// 64 bytes for 0 to 1 elements
3  
// 96 bytes for 2 to 4 elements
4  
5  
/*
6  
 * #!
7  
 * Ontopia Engine
8  
 * #-
9  
 * Copyright (C) 2001 - 2013 The Ontopia Project
10  
 * #-
11  
 * Licensed under the Apache License, Version 2.0 (the "License");
12  
 * you may not use this file except in compliance with the License.
13  
 * You may obtain a copy of the License at
14  
 * 
15  
 *      http://www.apache.org/licenses/LICENSE-2.0
16  
 * 
17  
 * Unless required by applicable law or agreed to in writing, software
18  
 * distributed under the License is distributed on an "AS IS" BASIS,
19  
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
20  
 * See the License for the specific language governing permissions and
21  
 * limitations under the License.
22  
 * !#
23  
 */
24  
25  
sclass CompactHashMap<K, V> extends CompactAbstractMap<K, V> {
26  
  final static int INITIAL_SIZE = 3;
27  
  final static double LOAD_FACTOR = 0.6;
28  
29  
  // This object is used to represent a null KEY (null value are kept as is)
30  
  final static Object nullObject = new Object();
31  
  
32  
  /**
33  
   * When a key is deleted this object is put into the hashtable in
34  
   * its place, so that other entries with the same key (collisions)
35  
   * further down the hashtable are not lost after we delete an object
36  
   * in the collision chain.
37  
   */
38  
  final static Object deletedObject = new Object();
39  
  int elements;
40  
  int freecells;
41  
  O[] table; // key, value, key, value, ...
42  
  //int modCount;
43  
44  
  *() {
45  
    this(INITIAL_SIZE);
46  
  }
47  
48  
  *(int size) {
49  
    table = new Object[(size==0 ? 1 : size)*2];
50  
    elements = 0;
51  
    freecells = tableSize();
52  
    //modCount = 0;
53  
  }
54  
  
55  
  // TODO: allocate smarter
56  
  *(Map<K, V> map) {
57  
    this(0);
58  
    if (map != null) putAll(map);
59  
  }
60  
61  
  // ===== MAP IMPLEMENTATION =============================================
62  
63  
  /**
64  
   * Returns the number of key/value mappings in this map.
65  
   */
66  
  public synchronized int size() {
67  
    return elements;
68  
  }
69  
  
70  
  /**
71  
   * Returns <tt>true</tt> if this map contains no mappings.
72  
   */
73  
  public synchronized boolean isEmpty() {
74  
    return elements == 0;
75  
  }
76  
77  
  /**
78  
   * Removes all key/value mappings in the map.
79  
   */
80  
  public synchronized void clear() {
81  
    elements = 0;
82  
    for (int ix = 0; ix < tableSize(); ix++) {
83  
      key(ix, null);
84  
      value(ix, null);
85  
    }
86  
    freecells = tableSize();
87  
    //modCount++;
88  
  }
89  
  
90  
  /**
91  
   * Returns <tt>true</tt> if this map contains the specified key.
92  
   */
93  
  public synchronized boolean containsKey(Object k) {
94  
    return key(findKeyIndex(k)) != null;
95  
  }
96  
  
97  
  /**
98  
   * Returns <tt>true</tt> if this map contains the specified value.
99  
   */
100  
  public synchronized boolean containsValue(Object v) {
101  
    if (v == null)
102  
      v = (V)nullObject;
103  
104  
    for (int ix = 0; ix < tableSize(); ix++)
105  
      if (value(ix) != null && value(ix).equals(v))
106  
        return true;
107  
108  
    return false;
109  
  }
110  
111  
  /**
112  
   * Returns a read-only set view of the map's keys.
113  
   */
114  
  public synchronized Set<Entry<K, V>> entrySet() {
115  
    ret new EntrySet;
116  
  }
117  
118  
  /**
119  
   * Removes the mapping with key k, if there is one, and returns its
120  
   * value, if there is one, and null if there is none.
121  
   */
122  
  public synchronized V remove(Object k) {
123  
    int index = findKeyIndex(k);
124  
125  
    // we found the right position, now do the removal
126  
    if (key(index) != null) {
127  
      // we found the object
128  
129  
      // same problem here as with put
130  
      V v = value(index);
131  
      key(index, deletedObject);
132  
      value(index, deletedObject);
133  
      //modCount++;
134  
      elements--;
135  
      return v;
136  
    } else
137  
      // we did not find the key
138  
      return null;
139  
  }
140  
141  
  /**
142  
   * Adds the specified mapping to this map, returning the old value for
143  
   * the mapping, if there was one.
144  
   */
145  
  public synchronized V put(K k, V v) {
146  
    if (k == null)
147  
      k = (K)nullObject;
148  
149  
    int hash = k.hashCode();
150  
    int index = (hash & 0x7FFFFFFF) % tableSize();
151  
    int offset = 1;
152  
    int deletedix = -1;
153  
    
154  
    // search for the key (continue while !null and !this key)
155  
    while(key(index) != null &&
156  
          !(key(index).hashCode() == hash &&
157  
            key(index).equals(k))) {
158  
159  
      // if there's a deleted mapping here we can put this mapping here,
160  
      // provided it's not in here somewhere else already
161  
      if (key(index) == deletedObject)
162  
        deletedix = index;
163  
      
164  
      index = ((index + offset) & 0x7FFFFFFF) % tableSize();
165  
      offset = offset*2 + 1;
166  
167  
      if (offset == -1)
168  
        offset = 2;
169  
    }
170  
    
171  
    if (key(index) == null) { // wasn't present already
172  
      if (deletedix != -1) // reusing a deleted cell
173  
        index = deletedix;
174  
      else
175  
        freecells--;
176  
177  
      //modCount++;
178  
      elements++;
179  
180  
      key(index, k);
181  
      value(index, v);
182  
      
183  
      // rehash with increased capacity
184  
      if (1 - (freecells / (double) tableSize()) > LOAD_FACTOR)
185  
        rehash(tableSize()*2 + 1);
186  
      return null;
187  
    } else { // was there already
188  
      //modCount++;
189  
      V oldv = value(index);
190  
      value(index, v);
191  
      return oldv;
192  
    }
193  
  }
194  
195  
  /**
196  
   * INTERNAL: Rehashes the hashmap to a bigger size.
197  
   */
198  
  void rehash(int newCapacity) {
199  
    int oldCapacity = tableSize();
200  
    O[] newTable = new Object[newCapacity*2];
201  
202  
    for (int ix = 0; ix < oldCapacity; ix++) {
203  
      Object k = key(ix);
204  
      if (k == null || k == deletedObject)
205  
        continue;
206  
      
207  
      int hash = k.hashCode();
208  
      int index = (hash & 0x7FFFFFFF) % newCapacity;
209  
      int offset = 1;
210  
211  
      // search for the key
212  
      while(newTable[index*2] != null) { // no need to test for duplicates
213  
        index = ((index + offset) & 0x7FFFFFFF) % newCapacity;
214  
        offset = offset*2 + 1;
215  
216  
        if (offset == -1)
217  
          offset = 2;
218  
      }
219  
220  
      newTable[index*2] = k;
221  
      newTable[index*2+1] = value(ix);
222  
    }
223  
224  
    table = newTable;
225  
    freecells = tableSize() - elements;
226  
  }
227  
228  
  /**
229  
   * Returns the value for the key k, if there is one, and null if
230  
   * there is none.
231  
   */
232  
  public synchronized V get(Object k) {
233  
    return value(findKeyIndex(k));
234  
  }
235  
236  
  /**
237  
   * Returns a virtual read-only collection containing all the values
238  
   * in the map.
239  
   */
240  
  public synchronized Collection<V> values() {
241  
    return new ValueCollection();
242  
  }
243  
244  
  /**
245  
   * Returns a virtual read-only set of all the keys in the map.
246  
   */
247  
  public synchronized Set<K> keySet() {
248  
    return new KeySet();
249  
  }
250  
251  
  // --- Internal utilities
252  
253  
  final int findKeyIndex(Object k) {
254  
    if (k == null)
255  
      k = nullObject;
256  
257  
    int hash = k.hashCode();
258  
    int index = (hash & 0x7FFFFFFF) % tableSize();
259  
    int offset = 1;
260  
261  
    // search for the key (continue while !null and !this key)
262  
    while(key(index) != null &&
263  
          !(key(index).hashCode() == hash &&
264  
            key(index).equals(k))) {
265  
      index = ((index + offset) & 0x7FFFFFFF) % tableSize();
266  
      offset = offset*2 + 1;
267  
268  
      if (offset == -1)
269  
        offset = 2;
270  
    }
271  
    return index;
272  
  }
273  
  
274  
  // --- Key set
275  
276  
  class KeySet extends AbstractSet<K> {
277  
    public int size() { synchronized(CompactHashMap.this) {
278  
      return elements;
279  
    }}
280  
281  
    public boolean contains(Object k) { synchronized(CompactHashMap.this) {
282  
      return containsKey(k);
283  
    }}
284  
285  
    public Iterator<K> iterator() { synchronized(CompactHashMap.this) {
286  
      return new KeyIterator();
287  
    }}
288  
  }
289  
290  
  class KeyIterator<K> implements Iterator<K> {
291  
    private int ix;
292  
    
293  
    private KeyIterator() {
294  
      synchronized(CompactHashMap.this) {
295  
        // walk up to first value, so that hasNext() and next() return
296  
        // correct results
297  
        for (; ix < tableSize(); ix++)
298  
          if (value(ix) != null && key(ix) != deletedObject)
299  
            break;
300  
      }
301  
    }
302  
303  
    public boolean hasNext() { synchronized(CompactHashMap.this) {
304  
      return ix < tableSize();
305  
    }}
306  
307  
    public void remove() {
308  
      throw new UnsupportedOperationException("Collection is read-only");
309  
    }
310  
311  
    public K next() { synchronized(CompactHashMap.this) {
312  
      if (ix >= tableSize())
313  
        throw new NoSuchElementException();
314  
      K key = (K) key(ix++);
315  
      
316  
      // walk up to next value
317  
      for (; ix < tableSize(); ix++)
318  
        if (key(ix) != null && key(ix) != deletedObject)
319  
          break;
320  
      
321  
      // ix now either points to next key, or outside array (if no next)
322  
      return key;
323  
    }}
324  
  }
325  
  
326  
  // --- Entry set
327  
  
328  
  class EntrySet extends AbstractSet<Map.Entry<K, V>> {
329  
    public int size() { synchronized(CompactHashMap.this) {
330  
      return elements;
331  
    }}
332  
333  
    public boolean contains(O o) { synchronized(CompactHashMap.this) {
334  
      if (o cast Map.Entry) {
335  
        O key = o.getKey();
336  
        if (!containsKey(o)) false;
337  
        ret eq(o.getValue(), get(key));
338  
      }
339  
      false;
340  
    }}
341  
342  
    public Iterator<Map.Entry<K, V>> iterator() {
343  
      return new EntryIterator();
344  
    }
345  
  }
346  
347  
  class EntryIterator implements Iterator<Map.Entry<K, V>> {
348  
    private int ix;
349  
    
350  
    private EntryIterator() {
351  
      synchronized(CompactHashMap.this) {
352  
        // walk up to first value, so that hasNext() and next() return
353  
        // correct results
354  
        for (; ix < tableSize(); ix++)
355  
          if (value(ix) != null && key(ix) != deletedObject)
356  
            break;
357  
      }
358  
    }
359  
360  
    public boolean hasNext() { synchronized(CompactHashMap.this) {
361  
      return ix < tableSize();
362  
    }}
363  
364  
    public void remove() {
365  
      throw new UnsupportedOperationException("Collection is read-only");
366  
    }
367  
368  
    public Map.Entry<K, V> next() { synchronized(CompactHashMap.this) {
369  
      if (ix >= tableSize())
370  
        throw new NoSuchElementException();
371  
      K key = key(ix);
372  
      V val = value(ix);
373  
      ++ix;
374  
      
375  
      // walk up to next value
376  
      for (; ix < tableSize(); ix++)
377  
        if (key(ix) != null && key(ix) != deletedObject)
378  
          break;
379  
      
380  
      // ix now either points to next key, or outside array (if no next)
381  
      return simpleMapEntry(key, val);
382  
    }}
383  
  }
384  
  
385  
  // --- Value collection
386  
387  
  class ValueCollection<V> extends AbstractCollection<V> {
388  
    public int size() { synchronized(CompactHashMap.this) {
389  
      return elements;
390  
    }}
391  
392  
    public Iterator<V> iterator() {
393  
      return new ValueIterator();
394  
    }
395  
396  
    public boolean contains(Object v) {
397  
      return containsValue(v);
398  
    }
399  
  }
400  
401  
  class ValueIterator<V> implements Iterator<V> {
402  
    private int ix;
403  
    
404  
    private ValueIterator() {
405  
      synchronized(CompactHashMap.this) {
406  
        // walk up to first value, so that hasNext() and next() return
407  
        // correct results
408  
        for (; ix < table.length/2; ix++)
409  
          if (value(ix) != null && value(ix) != deletedObject)
410  
            break;
411  
      }
412  
    }
413  
414  
    public boolean hasNext() { synchronized(CompactHashMap.this) {
415  
      return ix < tableSize();
416  
    }}
417  
418  
    public void remove() {
419  
      throw new UnsupportedOperationException("Collection is read-only");
420  
    }
421  
422  
    public V next() { synchronized(CompactHashMap.this) {
423  
      if (ix >= tableSize())
424  
        throw new NoSuchElementException();
425  
      V value = (V) value(ix++);
426  
      
427  
      // walk up to next value
428  
      for (; ix < tableSize(); ix++)
429  
        if (value(ix) != null && value(ix) != deletedObject)
430  
          break;
431  
      
432  
      // ix now either points to next value, or outside array (if no next)
433  
      return value;
434  
    }}
435  
  }
436  
  
437  
  K key(int i) { ret (K) table[i*2]; }
438  
  void key(int i, O key) { table[i*2] = key; }
439  
  V value(int i) { ret (V) table[i*2+1]; }
440  
  void value(int i, O value) { table[i*2+1] = value; }
441  
  
442  
  int tableSize() { ret table.length/2; }
443  
}

Author comment

Began life as a copy of #1031038

download  show line numbers  debug dex  old transpilations   

Travelled to 9 computer(s): bhatertpkbcr, ekrmjmnbrukm, elmgxqgtpvxh, gjtlkbvenryc, mowyntqkapby, mqqgnosmbjvj, pyentgdyhuwx, vouqrxazstgt, wnsclhtenguj

No comments. add comment

Snippet ID: #1031040
Snippet name: CompactHashMap - using only one array, no modCount. synchronized
Eternal ID of this version: #1031040/19
Text MD5: 6e79376880d0b5b9e1c2f888461c500a
Transpilation MD5: 55e19cdcec5a96070e6cfd14615eb19c
Author: stefan
Category: javax / collections
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2022-04-04 00:18:28
Source code size: 11853 bytes / 443 lines
Pitched / IR pitched: No / No
Views / Downloads: 250 / 2108
Version history: 18 change(s)
Referenced in: [show references]