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

455
LINES

< > BotCompany Repo | #1012710 // WeakHasherMap (from plume-lib - doesn't work anymore, uses HashMap internals)

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

Libraryless. Click here for Pure Java version (3792L/22K).

1  
/*
2  
 * @(#)WeakHashMap.java 1.5 98/09/30
3  
 *
4  
 * Copyright 1998 by Sun Microsystems, Inc.,
5  
 * 901 San Antonio Road, Palo Alto, California, 94303, U.S.A.
6  
 * All rights reserved.
7  
 *
8  
 * This software is the confidential and proprietary information
9  
 * of Sun Microsystems, Inc. ("Confidential Information").  You
10  
 * shall not disclose such Confidential Information and shall use
11  
 * it only in accordance with the terms of the license agreement
12  
 * you entered into with Sun.
13  
 */
14  
 
15  
// From https://github.com/mernst/plume-lib/blob/df0bfafc3c16848d88f4ea0ef3c8bf3367ae085e/java/src/plume/WeakHasherMap.java
16  
17  
static final class WeakHasherMap<K,V> extends AbstractMap<K,V> implements Map<K,V> {
18  
19  
    private Hasher hasher = null;
20  
    /*@Pure*/
21  
    private boolean keyEquals(Object k1, Object k2) {
22  
  return (hasher==null ? k1.equals(k2)
23  
           : hasher.equals(k1, k2));
24  
    }
25  
    /*@Pure*/
26  
    private int keyHashCode(Object k1) {
27  
  return (hasher==null ? k1.hashCode()
28  
           : hasher.hashCode(k1));
29  
    }
30  
31  
    // The WeakKey class can't be static because it depends on the hasher.
32  
    // That in turn means that its methods can't be static.
33  
    // However, I need to be able to call the methods such as create() that
34  
    // were static in the original version of this code.
35  
    // This finesses that.
36  
37  
    private /*@Nullable*/ WeakKey WeakKeyCreate(K k) {
38  
  if (k == null) return null;
39  
  else return new WeakKey(k);
40  
    }
41  
    private /*@Nullable*/ WeakKey WeakKeyCreate(K k, ReferenceQueue<? super K> q) {
42  
  if (k == null) return null;
43  
  else return new WeakKey(k, q);
44  
    }
45  
46  
    // Cannot be a static class: uses keyHashCode() and keyEquals()
47  
    private final class WeakKey extends WeakReference<K> {
48  
  private int hash; /* Hashcode of key, stored here since the key
49  
           may be tossed by the GC */
50  
51  
  private WeakKey(K k) {
52  
      super(k);
53  
      hash = keyHashCode(k);
54  
  }
55  
56  
  private /*@Nullable*/ WeakKey create(K k) {
57  
      if (k == null) return null;
58  
      else return new WeakKey(k);
59  
  }
60  
61  
  private WeakKey(K k, ReferenceQueue<? super K> q) {
62  
      super(k, q);
63  
      hash = keyHashCode(k);
64  
  }
65  
66  
  private /*@Nullable*/ WeakKey create(K k, ReferenceQueue<? super K> q) {
67  
      if (k == null) return null;
68  
      else return new WeakKey(k, q);
69  
  }
70  
71  
        /* A WeakKey is equal to another WeakKey iff they both refer to objects
72  
     that are, in turn, equal according to their own equals methods */
73  
  /*@Pure*/
74  
  @Override
75  
  public boolean equals(/*@Nullable*/ Object o) {
76  
            if (o == null) return false; // never happens
77  
      if (this == o) return true;
78  
            // This test is illegal because WeakKey is a generic type,
79  
            // so use the getClass hack below instead.
80  
      // if (!(o instanceof WeakKey)) return false;
81  
            if (!(o.getClass().equals(WeakKey.class))) return false;
82  
      Object t = this.get();
83  
            @SuppressWarnings("unchecked")
84  
      Object u = ((WeakKey)o).get();
85  
      if ((t == null) || (u == null)) return false;
86  
      if (t == u) return true;
87  
      return keyEquals(t, u);
88  
  }
89  
90  
  /*@Pure*/
91  
  @Override
92  
  public int hashCode() {
93  
      return hash;
94  
  }
95  
96  
    }
97  
98  
99  
    /* Hash table mapping WeakKeys to values */
100  
    private HashMap<WeakKey,V> hash;
101  
102  
    /* Reference queue for cleared WeakKeys */
103  
    private ReferenceQueue<? super K> queue = new ReferenceQueue<K>();
104  
105  
106  
    /* Remove all invalidated entries from the map, that is, remove all entries
107  
       whose keys have been discarded.  This method should be invoked once by
108  
       each public mutator in this class.  We don't invoke this method in
109  
       public accessors because that can lead to surprising
110  
       ConcurrentModificationExceptions. */
111  
    @SuppressWarnings("unchecked")
112  
    private void processQueue() {
113  
  WeakKey wk;
114  
  while ((wk = (WeakKey)queue.poll()) != null) { // unchecked cast
115  
      hash.remove(wk);
116  
  }
117  
    }
118  
119  

120  
    /* -- Constructors -- */
121  
122  
    /**
123  
     * Constructs a new, empty <code>WeakHashMap</code> with the given
124  
     * initial capacity and the given load factor.
125  
     *
126  
     * @param  initialCapacity  the initial capacity of the
127  
     *                          <code>WeakHashMap</code>
128  
     *
129  
     * @param  loadFactor       the load factor of the <code>WeakHashMap</code>
130  
     *
131  
     * @throws IllegalArgumentException  If the initial capacity is less than
132  
     *                                   zero, or if the load factor is
133  
     *                                   nonpositive
134  
     */
135  
    public WeakHasherMap(int initialCapacity, float loadFactor) {
136  
  hash = new HashMap<WeakKey,V>(initialCapacity, loadFactor);
137  
    }
138  
139  
    /**
140  
     * Constructs a new, empty <code>WeakHashMap</code> with the given
141  
     * initial capacity and the default load factor, which is
142  
     * <code>0.75</code>.
143  
     *
144  
     * @param  initialCapacity  the initial capacity of the
145  
     *                          <code>WeakHashMap</code>
146  
     *
147  
     * @throws IllegalArgumentException  If the initial capacity is less than
148  
     *                                   zero
149  
     */
150  
    public WeakHasherMap(int initialCapacity) {
151  
  hash = new HashMap<WeakKey,V>(initialCapacity);
152  
    }
153  
154  
    /**
155  
     * Constructs a new, empty <code>WeakHashMap</code> with the default
156  
     * capacity and the default load factor, which is <code>0.75</code>.
157  
     */
158  
    public WeakHasherMap() {
159  
  hash = new HashMap<WeakKey,V>();
160  
    }
161  
162  
    /**
163  
     * Constructs a new, empty <code>WeakHashMap</code> with the default
164  
     * capacity and the default load factor, which is <code>0.75</code>.
165  
     * The <code>WeakHashMap</code> uses the specified hasher for hashing
166  
     * keys and comparing them for equality.
167  
     * @param h the Hasher to use when hashing values for this map
168  
     */
169  
    public WeakHasherMap(Hasher h) {
170  
  hash = new HashMap<WeakKey,V>();
171  
  hasher = h;
172  
    }
173  
174  

175  
    /* -- Simple queries -- */
176  
177  
    /**
178  
     * Returns the number of key-value mappings in this map.
179  
     * <strong>Note:</strong> <em>In contrast to most implementations of the
180  
     * <code>Map</code> interface, the time required by this operation is
181  
     * linear in the size of the map.</em>
182  
     */
183  
    /*@Pure*/
184  
    @Override
185  
    public int size() {
186  
  return entrySet().size();
187  
    }
188  
189  
    /**
190  
     * Returns <code>true</code> if this map contains no key-value mappings.
191  
     */
192  
    /*@Pure*/
193  
    @Override
194  
    public boolean isEmpty() {
195  
  return entrySet().isEmpty();
196  
    }
197  
198  
    /**
199  
     * Returns <code>true</code> if this map contains a mapping for the
200  
     * specified key.
201  
     *
202  
     * @param   key   the key whose presence in this map is to be tested
203  
     */
204  
    /*@Pure*/
205  
    @Override
206  
    public boolean containsKey(Object key) {
207  
        @SuppressWarnings("unchecked")
208  
        K kkey = (K) key;
209  
  return hash.containsKey(WeakKeyCreate(kkey));
210  
    }
211  
212  

213  
    /* -- Lookup and modification operations -- */
214  
215  
    /**
216  
     * Returns the value to which this map maps the specified <code>key</code>.
217  
     * If this map does not contain a value for this key, then return
218  
     * <code>null</code>.
219  
     *
220  
     * @param  key  the key whose associated value, if any, is to be returned
221  
     */
222  
    /*@Pure*/
223  
    @Override
224  
    public /*@Nullable*/ V get(Object key) {  // type of argument is Object, not K
225  
        @SuppressWarnings("unchecked")
226  
        K kkey = (K) key;
227  
  return hash.get(WeakKeyCreate(kkey));
228  
    }
229  
230  
    /**
231  
     * Updates this map so that the given <code>key</code> maps to the given
232  
     * <code>value</code>.  If the map previously contained a mapping for
233  
     * <code>key</code> then that mapping is replaced and the previous value is
234  
     * returned.
235  
     *
236  
     * @param  key    the key that is to be mapped to the given
237  
     *                <code>value</code>
238  
     * @param  value  the value to which the given <code>key</code> is to be
239  
     *                mapped
240  
     *
241  
     * @return  the previous value to which this key was mapped, or
242  
     *          <code>null</code> if if there was no mapping for the key
243  
     */
244  
    @Override
245  
    public V put(K key, V value) {
246  
  processQueue();
247  
  return hash.put(WeakKeyCreate(key, queue), value);
248  
    }
249  
250  
    /**
251  
     * Removes the mapping for the given <code>key</code> from this map, if
252  
     * present.
253  
     *
254  
     * @param  key  the key whose mapping is to be removed
255  
     *
256  
     * @return  the value to which this key was mapped, or <code>null</code> if
257  
     *          there was no mapping for the key
258  
     */
259  
    @Override
260  
    public V remove(Object key) { // type of argument is Object, not K
261  
  processQueue();
262  
        @SuppressWarnings("unchecked")
263  
        K kkey = (K) key;
264  
  return hash.remove(WeakKeyCreate(kkey));
265  
    }
266  
267  
    /**
268  
     * Removes all mappings from this map.
269  
     */
270  
    @Override
271  
    public void clear() {
272  
  processQueue();
273  
  hash.clear();
274  
    }
275  
276  

277  
    /* -- Views -- */
278  
279  
280  
    /* Internal class for entries */
281  
    // This can't be static, again because of dependence on hasher.
282  
    @SuppressWarnings("TypeParameterShadowing")
283  
    private final class Entry<K,V> implements Map.Entry<K,V> {
284  
  private Map.Entry<WeakKey,V> ent;
285  
  private K key;  /* Strong reference to key, so that the GC
286  
           will leave it alone as long as this Entry
287  
           exists */
288  
289  
  Entry(Map.Entry<WeakKey,V> ent, K key) {
290  
      this.ent = ent;
291  
      this.key = key;
292  
  }
293  
294  
  /*@Pure*/
295  
  @Override
296  
  public K getKey() {
297  
      return key;
298  
  }
299  
300  
  /*@Pure*/
301  
  @Override
302  
  public V getValue() {
303  
      return ent.getValue();
304  
  }
305  
306  
  @Override
307  
  public V setValue(V value) {
308  
      return ent.setValue(value);
309  
  }
310  
311  
        /*@Pure*/
312  
        private boolean keyvalEquals(K o1, K o2) {
313  
      return (o1 == null) ? (o2 == null) : keyEquals(o1, o2);
314  
  }
315  
316  
        /*@Pure*/
317  
        private boolean valEquals(V o1, V o2) {
318  
      return (o1 == null) ? (o2 == null) : o1.equals(o2);
319  
  }
320  
321  
        /*@Pure*/
322  
        @SuppressWarnings("NonOverridingEquals")
323  
        public boolean equals(Map.Entry<K,V> e /* Object o*/) {
324  
            // if (! (o instanceof Map.Entry)) return false;
325  
            // Map.Entry<K,V> e = (Map.Entry<K,V>)o;
326  
      return (keyvalEquals(key, e.getKey())
327  
        && valEquals(getValue(), e.getValue()));
328  
  }
329  
330  
  /*@Pure*/
331  
  @Override
332  
  public int hashCode() {
333  
      V v;
334  
      return (((key == null) ? 0 : keyHashCode(key))
335  
        ^ (((v = getValue()) == null) ? 0 : v.hashCode()));
336  
  }
337  
338  
    }
339  
340  
341  
    /* Internal class for entry sets */
342  
    private final class EntrySet extends AbstractSet<Map.Entry<K,V>> {
343  
  Set<Map.Entry<WeakKey,V>> hashEntrySet = hash.entrySet();
344  
345  
  @Override
346  
  public Iterator<Map.Entry<K,V>> iterator() {
347  
348  
      return new Iterator<Map.Entry<K,V>>() {
349  
    Iterator<Map.Entry<WeakKey,V>> hashIterator = hashEntrySet.iterator();
350  
    Map.Entry<K,V> next = null;
351  
352  
    @Override
353  
    public boolean hasNext() {
354  
        while (hashIterator.hasNext()) {
355  
      Map.Entry<WeakKey,V> ent = hashIterator.next();
356  
      WeakKey wk = ent.getKey();
357  
      K k = null;
358  
      if ((wk != null) && ((k = wk.get()) == null)) {
359  
          /* Weak key has been cleared by GC */
360  
          continue;
361  
      }
362  
      next = new Entry<K,V>(ent, k);
363  
      return true;
364  
        }
365  
        return false;
366  
    }
367  
368  
    @Override
369  
    public Map.Entry<K,V> next() {
370  
        if ((next == null) && !hasNext())
371  
      throw new NoSuchElementException();
372  
        Map.Entry<K,V> e = next;
373  
        next = null;
374  
        return e;
375  
    }
376  
377  
    @Override
378  
    public void remove() {
379  
        hashIterator.remove();
380  
    }
381  
382  
      };
383  
  }
384  
385  
  /*@Pure*/
386  
  @Override
387  
  public boolean isEmpty() {
388  
      return !(iterator().hasNext());
389  
  }
390  
391  
  /*@Pure*/
392  
  @Override
393  
  public int size() {
394  
      int j = 0;
395  
      for (Iterator<Map.Entry<K,V>> i = iterator(); i.hasNext(); i.next()) j++;
396  
      return j;
397  
  }
398  
399  
  @Override
400  
  public boolean remove(Object o) {
401  
      processQueue();
402  
      if (!(o instanceof Map.Entry<?,?>)) return false;
403  
            @SuppressWarnings("unchecked")
404  
      Map.Entry<K,V> e = (Map.Entry<K,V>)o; // unchecked cast
405  
      Object ev = e.getValue();
406  
      WeakKey wk = WeakKeyCreate(e.getKey());
407  
      Object hv = hash.get(wk);
408  
      if ((hv == null)
409  
    ? ((ev == null) && hash.containsKey(wk)) : hv.equals(ev)) {
410  
    hash.remove(wk);
411  
    return true;
412  
      }
413  
      return false;
414  
  }
415  
416  
  /*@Pure*/
417  
  @Override
418  
  public int hashCode() {
419  
      int h = 0;
420  
      for (Iterator<Map.Entry<WeakKey,V>> i = hashEntrySet.iterator(); i.hasNext(); ) {
421  
    Map.Entry<WeakKey,V> ent = i.next();
422  
    WeakKey wk = ent.getKey();
423  
    Object v;
424  
    if (wk == null) continue;
425  
    h += (wk.hashCode()
426  
          ^ (((v = ent.getValue()) == null) ? 0 : v.hashCode()));
427  
      }
428  
      return h;
429  
  }
430  
431  
    }
432  
433  
434  
    private /*@Nullable*/ Set<Map.Entry<K,V>> entrySet = null;
435  
436  
    /**
437  
     * Returns a <code>Set</code> view of the mappings in this map.
438  
     */
439  
    /*@SideEffectFree*/
440  
    @Override
441  
    public Set<Map.Entry<K,V>> entrySet() {
442  
  if (entrySet == null) entrySet = new EntrySet();
443  
  return entrySet;
444  
    }
445  
446  
    // find matching key
447  
    K findKey(O key) {
448  
      processQueue();
449  
      K kkey = (K) key;
450  
      // TODO: use replacement for HashMap to avoid reflection
451  
      WeakKey wkey = WeakKeyCreate(kkey);
452  
      WeakKey found = hashMap_findKey(hash, wkey);
453  
      ret found == null ? null : found.get();
454  
    }
455  
}

download  show line numbers  debug dex  old transpilations   

Travelled to 14 computer(s): aoiabmzegqzx, bhatertpkbcr, cbybwowwnfue, cfunsshuasjs, gwrvuhgaqvyk, ishqpsrjomds, lpdgvwnxivlt, mqqgnosmbjvj, onxytkatvevr, pyentgdyhuwx, pzhvpgtvlbxg, tslmcundralx, tvejysmllsmz, vouqrxazstgt

No comments. add comment

Snippet ID: #1012710
Snippet name: WeakHasherMap (from plume-lib - doesn't work anymore, uses HashMap internals)
Eternal ID of this version: #1012710/13
Text MD5: fc81e6a0c822a398011203b7f0eb23a0
Transpilation MD5: d2b33a78a6bdb330638fddf1cf874c43
Author: stefan
Category: javax
Type: JavaX fragment (include)
Public (visible to everyone): Yes
Archived (hidden from active list): No
Created/modified: 2021-07-27 00:33:05
Source code size: 13599 bytes / 455 lines
Pitched / IR pitched: No / No
Views / Downloads: 438 / 1082
Version history: 12 change(s)
Referenced in: [show references]