2018年9月28日 星期五

[Java] Map Implementations

Documents: https://docs.oracle.com/javase/tutorial/collections/implementations/map.html


General-Purpose Map Implementations:
  • HashMap
  • TreeMap
  • LinkedHashMap

Special-Purpose Map Implementations:
  • EnumMap
  • WeakHashMap
  • IdentityHashMap

Concurrent Map Implementations:
  • The java.util.concurrent package contains the ConcurrentMap interface, which extends Map with atomic putIfAbsent, remove, and replace methods, and the ConcurrentHashMap implementation of that interface.


Example: [LeetCode] 146. LRU Cache

public class LRUCache {
    private int capacity;
    private Map<Integer, Node> cache;
    private DoublyLinkedList doublyLinkedList;
    
    public LRUCache(int capacity) {
        this.capacity = capacity;
        this.cache = new HashMap<>();
        this.doublyLinkedList = new DoublyLinkedList();
    }

    public int get(int key) {
        if (!cache.containsKey(key)) {
            return -1;
        }
        Node node = cache.get(key);
        doublyLinkedList.moveToTail(node);
        return node.value;
    }

    public void put(int key, int value) {
        if (cache.containsKey(key)) {
            Node node = cache.get(key);
            doublyLinkedList.moveToTail(node);
            node.value = value;
            return;
        }

        if (cache.size() == capacity) {
            cache.remove(doublyLinkedList.first().key);
            doublyLinkedList.remove(doublyLinkedList.first());
        }

        Node insert = new Node(key, value);
        cache.put(key, insert);
        doublyLinkedList.addToTail(insert);
    }
    
    public static class DoublyLinkedList {
        private Node head;
        private Node tail;

        public DoublyLinkedList() {
            this.head = new Node();
            this.tail = new Node();
            this.head.next = this.tail;
            this.tail.prev = this.head;
        }
        
        public Node first() {
            return head.next;
        }
        
        public void remove(Node node) {
            node.prev.next = node.next;
            node.next.prev = node.prev;
        }

        public void addToTail(Node node) {
            node.prev = tail.prev;
            tail.prev = node;
            node.prev.next = node;
            node.next = tail;
        }
        
        public void moveToTail(Node node) {
            remove(node);
            addToTail(node);
        }
    }
    
    public static class Node {
        public int key;
        public int value;
        public Node prev;
        public Node next;
        
        public Node(int key, int value) {
            this.key = key;
            this.value = value;
        }
        
        public Node() {
            this.key = -1;
            this.value = -1;
        }
    }
}



Example: [LeetCode] 460. LFU Cache
class LFUCache {
    private int capacity;
    private HashMap<Integer, Integer> valueHash;
    private Node head;
    private HashMap<Integer, Node> nodeHash;

    public LFUCache(int capacity) {
        this.capacity = capacity;
        this.valueHash = new HashMap<>();
        this.nodeHash = new HashMap<>();
    }
    
    public int get(int key) {
        if (valueHash.containsKey(key)) {
            increaseFrequency(key);
            return valueHash.get(key);
        } else {
            return -1;
        }
    }
    
    public void put(int key, int value) {
        if (capacity == 0) {
            return;
        }
        if (!valueHash.containsKey(key)) {
            if (valueHash.size() == capacity) {
                removeOldestKey();
            }
            addToHead(key);
        }
        valueHash.put(key, value);    
        increaseFrequency(key);
    }
    
    private void addToHead(int key) {
        if (head == null) {
            head = new Node(0);
        } else if (head.frequency > 0) {
            Node node = new Node(0);
            node.next = head;
            head.prev = node;
            head = node;
        }
        head.bucket.add(key);
        nodeHash.put(key, head);      
    }
    
    private void increaseFrequency(int key) {
        Node node = nodeHash.get(key);
        node.bucket.remove(key);
        
        if (node.next == null) {
            node.next = new Node(node.frequency + 1);
            node.next.prev = node;
        } else if (node.next.frequency > node.frequency + 1) {
            Node middle = new Node(node.frequency + 1);
            middle.prev = node;
            middle.next = node.next;
            node.next.prev = middle;
            node.next = middle;
        }
        node.next.bucket.add(key);
        nodeHash.put(key, node.next);
        if (node.bucket.isEmpty()) {
            removeNode(node);
        }
    }
    
    private void removeOldestKey() {
        if (head == null) { 
            return;
        }
        int oldestKey = getOldestKey();
        head.bucket.remove(oldestKey);
        if (head.bucket.isEmpty()) {
            removeNode(head);   
        }
        nodeHash.remove(oldestKey);
        valueHash.remove(oldestKey);
    }
    
    private int getOldestKey() {
        for (int n : head.bucket) {
            return n;
        }
        return 0;
    }
    
    private void removeNode(Node node) {
        if (node.prev == null) {
            head = node.next;
        } else {
            node.prev.next = node.next;
        } 
        if (node.next != null) {
            node.next.prev = node.prev;
        }        
    }
    
    public static class Node {
        public int frequency;
        public LinkedHashSet<Integer> bucket;
        public Node prev;
        public Node next;
        
        public Node(int frequency) {
            this.frequency = frequency;
            this.bucket = new LinkedHashSet<>();
        }
    }
}

沒有留言:

張貼留言