PageRenderTime 44ms CodeModel.GetById 12ms app.highlight 28ms RepoModel.GetById 1ms app.codeStats 0ms

/hazelcast/src/main/java/com/hazelcast/util/SortedHashMap.java

https://bitbucket.org/gabral6_gmailcom/hazelcast
Java | 550 lines | 447 code | 77 blank | 26 comment | 113 complexity | 7cd8f28c2137b6a85ed4ddacf722b168 MD5 | raw file
  1/*
  2 * Copyright (c) 2008-2013, Hazelcast, Inc. All Rights Reserved.
  3 *
  4 * Licensed under the Apache License, Version 2.0 (the "License");
  5 * you may not use this file except in compliance with the License.
  6 * You may obtain a copy of the License at
  7 *
  8 * http://www.apache.org/licenses/LICENSE-2.0
  9 *
 10 * Unless required by applicable law or agreed to in writing, software
 11 * distributed under the License is distributed on an "AS IS" BASIS,
 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 13 * See the License for the specific language governing permissions and
 14 * limitations under the License.
 15 */
 16
 17package com.hazelcast.util;
 18
 19import java.util.*;
 20
 21public class SortedHashMap<K, V> extends AbstractMap<K, V> {
 22
 23    static final int MAXIMUM_CAPACITY = 1 << 30;
 24    static final int DEFAULT_INITIAL_CAPACITY = 16;
 25    static final float DEFAULT_LOAD_FACTOR = 0.75f;
 26
 27    int modCount = 0;
 28    Entry<K, V>[] table;
 29    private transient Entry<K, V> header = null;
 30    int size;
 31    int threshold;
 32    final float loadFactor;
 33    final OrderingType orderingType;
 34
 35    public enum OrderingType {
 36        NONE, LRU, LFU, HASH
 37    }
 38
 39    public SortedHashMap() {
 40        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, OrderingType.NONE);
 41    }
 42
 43    public SortedHashMap(int initialCapacity) {
 44        this(initialCapacity, DEFAULT_LOAD_FACTOR, OrderingType.NONE);
 45    }
 46
 47    public SortedHashMap(int initialCapacity, OrderingType orderingType) {
 48        this(initialCapacity, DEFAULT_LOAD_FACTOR, orderingType);
 49    }
 50
 51    public SortedHashMap(int initialCapacity, float loadFactor, OrderingType orderingType) {
 52        if (initialCapacity < 0)
 53            throw new IllegalArgumentException("Illegal initial capacity: " +
 54                    initialCapacity);
 55        if (initialCapacity > MAXIMUM_CAPACITY)
 56            initialCapacity = MAXIMUM_CAPACITY;
 57        if (loadFactor <= 0 || Float.isNaN(loadFactor))
 58            throw new IllegalArgumentException("Illegal load factor: " +
 59                    loadFactor);
 60        // Find a power of 2 >= initialCapacity
 61        int capacity = 1;
 62        while (capacity < initialCapacity)
 63            capacity <<= 1;
 64        this.orderingType = orderingType;
 65        this.loadFactor = loadFactor;
 66        threshold = (int) (capacity * loadFactor);
 67        table = new Entry[capacity];
 68        header = new Entry<K, V>(-1, null, null, null);
 69        header.before = header.after = header;
 70    }
 71
 72    public V put(K key, V value) {
 73        int hash = hash(key.hashCode());
 74        int i = indexFor(hash, table.length);
 75        for (Entry<K, V> e = table[i]; e != null; e = e.next) {
 76            Object k;
 77            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
 78                V oldValue = e.value;
 79                e.value = value;
 80                e.recordAccess(this);
 81                return oldValue;
 82            }
 83        }
 84        modCount++;
 85        addEntry(hash, key, value, i);
 86        return null;
 87    }
 88
 89    public static void touch(SortedHashMap linkedMap, Object key, OrderingType orderingType) {
 90        Entry e = linkedMap.getEntry(key);
 91        if (e != null) {
 92            e.touch(linkedMap, orderingType);
 93        }
 94    }
 95
 96    public static void moveToTop(SortedHashMap linkedMap, Object key) {
 97        Entry e = linkedMap.getEntry(key);
 98        if (e != null) {
 99            e.moveToTop(linkedMap);
100        }
101    }
102
103    public static OrderingType getOrderingTypeByName(String orderingType) {
104        return OrderingType.valueOf(orderingType.toUpperCase());
105    }
106
107    public boolean containsKey(Object key) {
108        return getEntry(key) != null;
109    }
110
111    public V get(Object key) {
112        Entry<K, V> e = getEntry(key);
113        if (e == null)
114            return null;
115        e.recordAccess(this);
116        return e.value;
117    }
118
119    Entry<K, V> getEntry(Object k) {
120        int hash = hash(k.hashCode());
121        int i = indexFor(hash, table.length);
122        Entry<K, V> e = table[i];
123        while (e != null && !(e.hash == hash && eq(k, e.key)))
124            e = e.next;
125        return e;
126    }
127
128    static boolean eq(Object x, Object y) {
129        return x == y || x.equals(y);
130    }
131
132    public void clear() {
133        modCount++;
134        Entry[] tab = table;
135        for (int i = 0; i < tab.length; i++)
136            tab[i] = null;
137        size = 0;
138        header.before = header.after = header;
139    }
140
141    public V remove(Object key) {
142        Entry<K, V> e = removeEntryForKey(key);
143        return (e == null ? null : e.value);
144    }
145
146    Entry<K, V> removeEntryForKey(Object k) {
147        int hash = hash(k.hashCode());
148        int i = indexFor(hash, table.length);
149        Entry<K, V> prev = table[i];
150        Entry<K, V> e = prev;
151        while (e != null) {
152            Entry<K, V> next = e.next;
153            if ((e.hash == hash) && (k == e.key || k.equals(e.key))) {
154                modCount++;
155                size--;
156                if (prev == e)
157                    table[i] = next;
158                else
159                    prev.next = next;
160                e.recordRemoval(this);
161                return e;
162            }
163            prev = e;
164            e = next;
165        }
166        return e;
167    }
168
169    Entry<K, V> removeMapping(Object o) {
170        if (!(o instanceof Map.Entry))
171            return null;
172        Map.Entry<K, V> entry = (Map.Entry<K, V>) o;
173        Object k = entry.getKey();
174        int hash = hash(k.hashCode());
175        int i = indexFor(hash, table.length);
176        Entry<K, V> prev = table[i];
177        Entry<K, V> e = prev;
178        while (e != null) {
179            Entry<K, V> next = e.next;
180            if (e.hash == hash && e.equals(entry)) {
181                modCount++;
182                size--;
183                if (prev == e)
184                    table[i] = next;
185                else
186                    prev.next = next;
187                e.recordRemoval(this);
188                return e;
189            }
190            prev = e;
191            e = next;
192        }
193        return e;
194    }
195
196    void addEntry(int hash, K key, V value, int bucketIndex) {
197        createEntry(hash, key, value, bucketIndex);
198        Entry<K, V> eldest = header.after;
199        if (removeEldestEntry(eldest)) {
200            removeEntryForKey(eldest.key);
201        } else {
202            if (size >= threshold)
203                resize(2 * table.length);
204        }
205    }
206
207    protected boolean removeEldestEntry(Map.Entry<K, V> eldest) {
208        return false;
209    }
210
211    void createEntry(int hash, K key, V value, int bucketIndex) {
212        Entry<K, V> old = table[bucketIndex];
213        Entry<K, V> e = new Entry<K, V>(hash, key, value, old);
214        table[bucketIndex] = e;
215        e.addBefore(header);
216        size++;
217    }
218
219    void resize(int newCapacity) {
220        Entry[] oldTable = table;
221        int oldCapacity = oldTable.length;
222        if (oldCapacity == MAXIMUM_CAPACITY) {
223            threshold = Integer.MAX_VALUE;
224            return;
225        }
226        Entry[] newTable = new Entry[newCapacity];
227        transfer(newTable);
228        table = newTable;
229        threshold = (int) (newCapacity * loadFactor);
230    }
231
232    void transfer(Entry[] newTable) {
233        int newCapacity = newTable.length;
234        for (Entry<K, V> e = header.after; e != header; e = e.after) {
235            int index = indexFor(e.hash, newCapacity);
236            e.next = newTable[index];
237            newTable[index] = e;
238        }
239    }
240
241    static int hash(int h) {
242        h ^= (h >>> 20) ^ (h >>> 12);
243        return h ^ (h >>> 7) ^ (h >>> 4);
244    }
245
246    static int indexFor(int h, int length) {
247        return h & (length - 1);
248    }
249
250    public boolean containsValue(Object value) {
251        // Overridden to take advantage of faster iterator
252        if (value == null) {
253            for (Entry<K, V> e = header.after; e != header; e = e.after)
254                if (e.value == null)
255                    return true;
256        } else {
257            for (Entry<K, V> e = header.after; e != header; e = e.after)
258                if (value.equals(e.value))
259                    return true;
260        }
261        return false;
262    }
263
264    static class Entry<K, V> implements Map.Entry<K, V> {
265        K key;
266        V value;
267        Entry<K, V> next;
268        Entry<K, V> after;
269        Entry<K, V> before;
270        long accessCount = 1;
271        long lastAccess = 0;
272        int hash = -1;
273
274        Entry(int hash, K key, V value, Entry<K, V> next) {
275            this.key = key;
276            this.value = value;
277            this.hash = hash;
278            this.next = next;
279            lastAccess = Clock.currentTimeMillis();
280        }
281
282        public K getKey() {
283            return key;
284        }
285
286        public V getValue() {
287            return value;
288        }
289
290        public V setValue(V value) {
291            this.value = value;
292            return value;
293        }
294
295        public boolean equals(Object o) {
296            if (!(o instanceof Map.Entry)) {
297                return false;
298            }
299            Map.Entry e = (Map.Entry) o;
300            Object k1 = getKey();
301            Object k2 = e.getKey();
302            if (k1 == k2 || (k1 != null && k1.equals(k2))) {
303                Object v1 = getValue();
304                Object v2 = e.getValue();
305                if (v1 == v2 || (v1 != null && v1.equals(v2))) {
306                    return true;
307                }
308            }
309            return false;
310        }
311
312        public int hashCode() {
313            return key.hashCode();
314        }
315
316        public String toString() {
317            return "Entry key=" + getKey() + ", value=" + getValue();
318        }
319
320        private void remove() {
321            before.after = after;
322            after.before = before;
323        }
324
325        private void addBefore(Entry<K, V> existingEntry) {
326            after = existingEntry;
327            before = existingEntry.before;
328            before.after = this;
329            after.before = this;
330        }
331
332        private void addAfter(Entry<K, V> existingEntry) {
333            addBefore(existingEntry.after);
334        }
335
336        /**
337         * This method is invoked by the superclass whenever the value
338         * of a pre-existing entry is read by Map.get or modified by Map.set.
339         * If the enclosing Map is access-ordered, it moves the entry
340         * to the end of the list; otherwise, it does nothing.
341         *
342         * @param lm
343         */
344        void recordAccess(SortedHashMap<K, V> lm) {
345            touch(lm, lm.orderingType);
346        }
347
348        void touch(SortedHashMap<K, V> lm, OrderingType orderingType) {
349            if (orderingType != OrderingType.NONE) {
350                lastAccess = Clock.currentTimeMillis();
351                accessCount++;
352                lm.modCount++;
353                if (orderingType == OrderingType.LFU) {
354                    moveLFU(lm);
355                } else if (orderingType == OrderingType.LRU) {
356                    moveLRU(lm);
357                } else if (orderingType == OrderingType.HASH) {
358                    moveHash(lm);
359                } else throw new RuntimeException("Unknown orderingType:" + lm.orderingType);
360            }
361        }
362
363        void moveLRU(SortedHashMap lm) {
364            remove();
365            addBefore(lm.header);
366        }
367
368        void moveLFU(SortedHashMap lm) {
369            Entry<K, V> nextOne = after;
370            boolean shouldMove = false;
371            while (nextOne != null && accessCount >= nextOne.accessCount && nextOne != lm.header) {
372                shouldMove = true;
373                nextOne = nextOne.after;
374            }
375            if (shouldMove) {
376                remove();
377                addBefore(nextOne);
378            }
379        }
380
381        void moveHash(SortedHashMap lm) {
382            Entry<K, V> nextOne = after;
383            boolean shouldMove = false;
384            while (nextOne != null && nextOne != lm.header && value.hashCode() >= nextOne.value.hashCode()) {
385                shouldMove = true;
386                nextOne = nextOne.after;
387            }
388            if (shouldMove) {
389                remove();
390                addBefore(nextOne);
391            }
392        }
393
394        void moveToTop(SortedHashMap lm) {
395            remove();
396            addAfter(lm.header);
397        }
398
399        void recordRemoval(SortedHashMap<K, V> lm) {
400            remove();
401        }
402    }
403
404    private abstract class LinkedHashIterator<T> implements Iterator<T> {
405        Entry<K, V> nextEntry = header.after;
406        Entry<K, V> lastReturned = null;
407
408        int expectedModCount = modCount;
409
410        public boolean hasNext() {
411            return nextEntry != header;
412        }
413
414        public void remove() {
415            if (lastReturned == null)
416                throw new IllegalStateException();
417            if (modCount != expectedModCount)
418                throw new ConcurrentModificationException();
419            SortedHashMap.this.remove(lastReturned.key);
420            lastReturned = null;
421            expectedModCount = modCount;
422        }
423
424        Entry<K, V> nextEntry() {
425            if (modCount != expectedModCount)
426                throw new ConcurrentModificationException();
427            if (nextEntry == header)
428                throw new NoSuchElementException();
429            Entry<K, V> e = lastReturned = nextEntry;
430            nextEntry = e.after;
431            return e;
432        }
433    }
434
435    private class KeyIterator extends LinkedHashIterator<K> {
436        public K next() {
437            return nextEntry().getKey();
438        }
439    }
440
441    private class ValueIterator extends LinkedHashIterator<V> {
442        public V next() {
443            return nextEntry().value;
444        }
445    }
446
447    private class EntryIterator extends LinkedHashIterator<Map.Entry<K, V>> {
448        public Map.Entry<K, V> next() {
449            return nextEntry();
450        }
451    }
452    // These Overrides alter the behavior of superclass view iterator() methods
453
454    Iterator<K> newKeyIterator() {
455        return new KeyIterator();
456    }
457
458    Iterator<V> newValueIterator() {
459        return new ValueIterator();
460    }
461
462    Iterator<Map.Entry<K, V>> newEntryIterator() {
463        return new EntryIterator();
464    }
465
466    private transient Set<Map.Entry<K, V>> entrySet = null;
467    transient volatile Set<K> keySet = null;
468    transient volatile Collection<V> values = null;
469
470    public Set<K> keySet() {
471        Set<K> ks = keySet;
472        return (ks != null ? ks : (keySet = new KeySet()));
473    }
474
475    private class KeySet extends AbstractSet<K> {
476        public Iterator<K> iterator() {
477            return newKeyIterator();
478        }
479
480        public int size() {
481            return size;
482        }
483
484        public boolean contains(Object o) {
485            return containsKey(o);
486        }
487
488        public boolean remove(Object o) {
489            return SortedHashMap.this.removeEntryForKey(o) != null;
490        }
491
492        public void clear() {
493            SortedHashMap.this.clear();
494        }
495    }
496
497    public Collection<V> values() {
498        Collection<V> vs = values;
499        return (vs != null ? vs : (values = new Values()));
500    }
501
502    private class Values extends AbstractCollection<V> {
503        public Iterator<V> iterator() {
504            return newValueIterator();
505        }
506
507        public int size() {
508            return size;
509        }
510
511        public boolean contains(Object o) {
512            return containsValue(o);
513        }
514
515        public void clear() {
516            SortedHashMap.this.clear();
517        }
518    }
519
520    public Set<Map.Entry<K, V>> entrySet() {
521        Set<Map.Entry<K, V>> es = entrySet;
522        return (es != null ? es : (entrySet = (Set<Map.Entry<K, V>>) new EntrySet()));
523    }
524
525    private class EntrySet extends AbstractSet {
526        public Iterator iterator() {
527            return newEntryIterator();
528        }
529
530        public boolean contains(Object o) {
531            if (!(o instanceof Map.Entry))
532                return false;
533            Map.Entry<K, V> e = (Map.Entry<K, V>) o;
534            Entry<K, V> candidate = getEntry(e.getKey());
535            return candidate != null && candidate.equals(e);
536        }
537
538        public boolean remove(Object o) {
539            return removeMapping(o) != null;
540        }
541
542        public int size() {
543            return size;
544        }
545
546        public void clear() {
547            SortedHashMap.this.clear();
548        }
549    }
550}