All Downloads are FREE. Search and download functionalities are using the official Maven repository.

com.hazelcast.util.SortedHashMap Maven / Gradle / Ivy

There is a newer version: 5.0-BETA-1
Show newest version
/*
 * Copyright (c) 2008-2013, Hazelcast, Inc. All Rights Reserved.
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 * http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */

package com.hazelcast.util;

import java.util.*;

public class SortedHashMap extends AbstractMap {

    static final int MAXIMUM_CAPACITY = 1 << 30;
    static final int DEFAULT_INITIAL_CAPACITY = 16;
    static final float DEFAULT_LOAD_FACTOR = 0.75f;

    int modCount = 0;
    Entry[] table;
    private transient Entry header = null;
    int size;
    int threshold;
    final float loadFactor;
    final OrderingType orderingType;

    public enum OrderingType {
        NONE, LRU, LFU, HASH
    }

    public SortedHashMap() {
        this(DEFAULT_INITIAL_CAPACITY, DEFAULT_LOAD_FACTOR, OrderingType.NONE);
    }

    public SortedHashMap(int initialCapacity) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR, OrderingType.NONE);
    }

    public SortedHashMap(int initialCapacity, OrderingType orderingType) {
        this(initialCapacity, DEFAULT_LOAD_FACTOR, orderingType);
    }

    public SortedHashMap(int initialCapacity, float loadFactor, OrderingType orderingType) {
        if (initialCapacity < 0)
            throw new IllegalArgumentException("Illegal initial capacity: " +
                    initialCapacity);
        if (initialCapacity > MAXIMUM_CAPACITY)
            initialCapacity = MAXIMUM_CAPACITY;
        if (loadFactor <= 0 || Float.isNaN(loadFactor))
            throw new IllegalArgumentException("Illegal load factor: " +
                    loadFactor);
        // Find a power of 2 >= initialCapacity
        int capacity = 1;
        while (capacity < initialCapacity)
            capacity <<= 1;
        this.orderingType = orderingType;
        this.loadFactor = loadFactor;
        threshold = (int) (capacity * loadFactor);
        table = new Entry[capacity];
        header = new Entry(-1, null, null, null);
        header.before = header.after = header;
    }

    public V put(K key, V value) {
        int hash = hash(key.hashCode());
        int i = indexFor(hash, table.length);
        for (Entry e = table[i]; e != null; e = e.next) {
            Object k;
            if (e.hash == hash && ((k = e.key) == key || key.equals(k))) {
                V oldValue = e.value;
                e.value = value;
                e.recordAccess(this);
                return oldValue;
            }
        }
        modCount++;
        addEntry(hash, key, value, i);
        return null;
    }

    public static void touch(SortedHashMap linkedMap, Object key, OrderingType orderingType) {
        Entry e = linkedMap.getEntry(key);
        if (e != null) {
            e.touch(linkedMap, orderingType);
        }
    }

    public static void moveToTop(SortedHashMap linkedMap, Object key) {
        Entry e = linkedMap.getEntry(key);
        if (e != null) {
            e.moveToTop(linkedMap);
        }
    }

    public static OrderingType getOrderingTypeByName(String orderingType) {
        return OrderingType.valueOf(orderingType.toUpperCase());
    }

    public boolean containsKey(Object key) {
        return getEntry(key) != null;
    }

    public V get(Object key) {
        Entry e = getEntry(key);
        if (e == null)
            return null;
        e.recordAccess(this);
        return e.value;
    }

    Entry getEntry(Object k) {
        int hash = hash(k.hashCode());
        int i = indexFor(hash, table.length);
        Entry e = table[i];
        while (e != null && !(e.hash == hash && eq(k, e.key)))
            e = e.next;
        return e;
    }

    static boolean eq(Object x, Object y) {
        return x == y || x.equals(y);
    }

    public void clear() {
        modCount++;
        Entry[] tab = table;
        for (int i = 0; i < tab.length; i++)
            tab[i] = null;
        size = 0;
        header.before = header.after = header;
    }

    public V remove(Object key) {
        Entry e = removeEntryForKey(key);
        return (e == null ? null : e.value);
    }

    Entry removeEntryForKey(Object k) {
        int hash = hash(k.hashCode());
        int i = indexFor(hash, table.length);
        Entry prev = table[i];
        Entry e = prev;
        while (e != null) {
            Entry next = e.next;
            if ((e.hash == hash) && (k == e.key || k.equals(e.key))) {
                modCount++;
                size--;
                if (prev == e)
                    table[i] = next;
                else
                    prev.next = next;
                e.recordRemoval(this);
                return e;
            }
            prev = e;
            e = next;
        }
        return e;
    }

    Entry removeMapping(Object o) {
        if (!(o instanceof Map.Entry))
            return null;
        Map.Entry entry = (Map.Entry) o;
        Object k = entry.getKey();
        int hash = hash(k.hashCode());
        int i = indexFor(hash, table.length);
        Entry prev = table[i];
        Entry e = prev;
        while (e != null) {
            Entry next = e.next;
            if (e.hash == hash && e.equals(entry)) {
                modCount++;
                size--;
                if (prev == e)
                    table[i] = next;
                else
                    prev.next = next;
                e.recordRemoval(this);
                return e;
            }
            prev = e;
            e = next;
        }
        return e;
    }

    void addEntry(int hash, K key, V value, int bucketIndex) {
        createEntry(hash, key, value, bucketIndex);
        Entry eldest = header.after;
        if (removeEldestEntry(eldest)) {
            removeEntryForKey(eldest.key);
        } else {
            if (size >= threshold)
                resize(2 * table.length);
        }
    }

    protected boolean removeEldestEntry(Map.Entry eldest) {
        return false;
    }

    void createEntry(int hash, K key, V value, int bucketIndex) {
        Entry old = table[bucketIndex];
        Entry e = new Entry(hash, key, value, old);
        table[bucketIndex] = e;
        e.addBefore(header);
        size++;
    }

    void resize(int newCapacity) {
        Entry[] oldTable = table;
        int oldCapacity = oldTable.length;
        if (oldCapacity == MAXIMUM_CAPACITY) {
            threshold = Integer.MAX_VALUE;
            return;
        }
        Entry[] newTable = new Entry[newCapacity];
        transfer(newTable);
        table = newTable;
        threshold = (int) (newCapacity * loadFactor);
    }

    void transfer(Entry[] newTable) {
        int newCapacity = newTable.length;
        for (Entry e = header.after; e != header; e = e.after) {
            int index = indexFor(e.hash, newCapacity);
            e.next = newTable[index];
            newTable[index] = e;
        }
    }

    static int hash(int h) {
        h ^= (h >>> 20) ^ (h >>> 12);
        return h ^ (h >>> 7) ^ (h >>> 4);
    }

    static int indexFor(int h, int length) {
        return h & (length - 1);
    }

    public boolean containsValue(Object value) {
        // Overridden to take advantage of faster iterator
        if (value == null) {
            for (Entry e = header.after; e != header; e = e.after)
                if (e.value == null)
                    return true;
        } else {
            for (Entry e = header.after; e != header; e = e.after)
                if (value.equals(e.value))
                    return true;
        }
        return false;
    }

    static class Entry implements Map.Entry {
        K key;
        V value;
        Entry next;
        Entry after;
        Entry before;
        long accessCount = 1;
        long lastAccess = 0;
        int hash = -1;

        Entry(int hash, K key, V value, Entry next) {
            this.key = key;
            this.value = value;
            this.hash = hash;
            this.next = next;
            lastAccess = Clock.currentTimeMillis();
        }

        public K getKey() {
            return key;
        }

        public V getValue() {
            return value;
        }

        public V setValue(V value) {
            this.value = value;
            return value;
        }

        public boolean equals(Object o) {
            if (!(o instanceof Map.Entry)) {
                return false;
            }
            Map.Entry e = (Map.Entry) o;
            Object k1 = getKey();
            Object k2 = e.getKey();
            if (k1 == k2 || (k1 != null && k1.equals(k2))) {
                Object v1 = getValue();
                Object v2 = e.getValue();
                if (v1 == v2 || (v1 != null && v1.equals(v2))) {
                    return true;
                }
            }
            return false;
        }

        public int hashCode() {
            return key.hashCode();
        }

        public String toString() {
            return "Entry key=" + getKey() + ", value=" + getValue();
        }

        private void remove() {
            before.after = after;
            after.before = before;
        }

        private void addBefore(Entry existingEntry) {
            after = existingEntry;
            before = existingEntry.before;
            before.after = this;
            after.before = this;
        }

        private void addAfter(Entry existingEntry) {
            addBefore(existingEntry.after);
        }

        /**
         * This method is invoked by the superclass whenever the value
         * of a pre-existing entry is read by Map.get or modified by Map.set.
         * If the enclosing Map is access-ordered, it moves the entry
         * to the end of the list; otherwise, it does nothing.
         *
         * @param lm
         */
        void recordAccess(SortedHashMap lm) {
            touch(lm, lm.orderingType);
        }

        void touch(SortedHashMap lm, OrderingType orderingType) {
            if (orderingType != OrderingType.NONE) {
                lastAccess = Clock.currentTimeMillis();
                accessCount++;
                lm.modCount++;
                if (orderingType == OrderingType.LFU) {
                    moveLFU(lm);
                } else if (orderingType == OrderingType.LRU) {
                    moveLRU(lm);
                } else if (orderingType == OrderingType.HASH) {
                    moveHash(lm);
                } else throw new RuntimeException("Unknown orderingType:" + lm.orderingType);
            }
        }

        void moveLRU(SortedHashMap lm) {
            remove();
            addBefore(lm.header);
        }

        void moveLFU(SortedHashMap lm) {
            Entry nextOne = after;
            boolean shouldMove = false;
            while (nextOne != null && accessCount >= nextOne.accessCount && nextOne != lm.header) {
                shouldMove = true;
                nextOne = nextOne.after;
            }
            if (shouldMove) {
                remove();
                addBefore(nextOne);
            }
        }

        void moveHash(SortedHashMap lm) {
            Entry nextOne = after;
            boolean shouldMove = false;
            while (nextOne != null && nextOne != lm.header && value.hashCode() >= nextOne.value.hashCode()) {
                shouldMove = true;
                nextOne = nextOne.after;
            }
            if (shouldMove) {
                remove();
                addBefore(nextOne);
            }
        }

        void moveToTop(SortedHashMap lm) {
            remove();
            addAfter(lm.header);
        }

        void recordRemoval(SortedHashMap lm) {
            remove();
        }
    }

    private abstract class LinkedHashIterator implements Iterator {
        Entry nextEntry = header.after;
        Entry lastReturned = null;

        int expectedModCount = modCount;

        public boolean hasNext() {
            return nextEntry != header;
        }

        public void remove() {
            if (lastReturned == null)
                throw new IllegalStateException();
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            SortedHashMap.this.remove(lastReturned.key);
            lastReturned = null;
            expectedModCount = modCount;
        }

        Entry nextEntry() {
            if (modCount != expectedModCount)
                throw new ConcurrentModificationException();
            if (nextEntry == header)
                throw new NoSuchElementException();
            Entry e = lastReturned = nextEntry;
            nextEntry = e.after;
            return e;
        }
    }

    private class KeyIterator extends LinkedHashIterator {
        public K next() {
            return nextEntry().getKey();
        }
    }

    private class ValueIterator extends LinkedHashIterator {
        public V next() {
            return nextEntry().value;
        }
    }

    private class EntryIterator extends LinkedHashIterator> {
        public Map.Entry next() {
            return nextEntry();
        }
    }
    // These Overrides alter the behavior of superclass view iterator() methods

    Iterator newKeyIterator() {
        return new KeyIterator();
    }

    Iterator newValueIterator() {
        return new ValueIterator();
    }

    Iterator> newEntryIterator() {
        return new EntryIterator();
    }

    private transient Set> entrySet = null;
    transient volatile Set keySet = null;
    transient volatile Collection values = null;

    public Set keySet() {
        Set ks = keySet;
        return (ks != null ? ks : (keySet = new KeySet()));
    }

    private class KeySet extends AbstractSet {
        public Iterator iterator() {
            return newKeyIterator();
        }

        public int size() {
            return size;
        }

        public boolean contains(Object o) {
            return containsKey(o);
        }

        public boolean remove(Object o) {
            return SortedHashMap.this.removeEntryForKey(o) != null;
        }

        public void clear() {
            SortedHashMap.this.clear();
        }
    }

    public Collection values() {
        Collection vs = values;
        return (vs != null ? vs : (values = new Values()));
    }

    private class Values extends AbstractCollection {
        public Iterator iterator() {
            return newValueIterator();
        }

        public int size() {
            return size;
        }

        public boolean contains(Object o) {
            return containsValue(o);
        }

        public void clear() {
            SortedHashMap.this.clear();
        }
    }

    public Set> entrySet() {
        Set> es = entrySet;
        return (es != null ? es : (entrySet = (Set>) new EntrySet()));
    }

    private class EntrySet extends AbstractSet {
        public Iterator iterator() {
            return newEntryIterator();
        }

        public boolean contains(Object o) {
            if (!(o instanceof Map.Entry))
                return false;
            Map.Entry e = (Map.Entry) o;
            Entry candidate = getEntry(e.getKey());
            return candidate != null && candidate.equals(e);
        }

        public boolean remove(Object o) {
            return removeMapping(o) != null;
        }

        public int size() {
            return size;
        }

        public void clear() {
            SortedHashMap.this.clear();
        }
    }
}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy