Accessing method of generic type - java

When I try to access a generic method from my main class it gives me an error.
When I do the following:
Integer key = map.remove(15);
then Eclipse says "The method remove(String) in the type IMiniMap<String,Integer> is not applicable for the arguments (int)" What's the way to access a method of generic type?
public class Tester {
public static void main(String[] args)
{
IMiniMap<String,Integer> map = new SimpleListMM<String,Integer>();
map.put("B",15);
map.put("A",5);
map.put("R",-5);
map.put("D",55);
map.put("Poems",128);
map.put("Plays",256);
// System.out.println(map.size());
// map.put("B", 22);
// System.out.println(map.keys());
//System.out.println(map.toString());
Integer key = map.remove(15);
}
}
public abstract class AbstractListMM<K,V> implements IMiniMap<K,V>{
protected List <K> keys;
protected List <V> vals;
// Initialize the lists of keys and values with a concrete instance
public AbstractListMM()
{
this.keys = new ArrayList<K>();
this.vals = new ArrayList<V>();
}
public AbstractListMM(List <K> keys, List <V> vals)
{
this.keys = keys;
this.vals = vals;
}
// Return the number of bindings based on the size of the key list
public int size()
{
return keys.size();
}
// Based on the lists size
public boolean isEmpty()
{
return (keys.isEmpty() && vals.isEmpty());
}
// Make a (shallow) copy of the keys list and return it
public List<K> keys()
{
List<K> newKeys = this.keys;
return newKeys;
}
// Make a (shallow) copy of the vals list and return it
public List<V> values()
{
List<V> vals = this.vals;
return vals;
}
// Use this.indexOf() to locate the given key as quickly as possible
public boolean contains(K key)
{
int pos = this.indexOf(key);
if(pos < 0)
return false;
else
return true;
/*if(this.indexOf(key) < 0)
return false;
else
return true;*/
}
// Use this.indexOf() to determine if a given key is present and
// return its associated value; return null if the key is not
// present
//
// TARGET COMPLEXITY: Same speed as indexOf()
public V get(K key)
{
int pos = this.indexOf(key);
if(pos < 0)
return null;
else
return vals.get(pos);
}
// Use this.indexOf() to determine the location of the given
// key/value and remove it from the corresponding lists
//
// TARGET COMPLEXITY: O(N) due to list elements shifting
public V remove(K key)
{
int pos = this.indexOf(key);
if(pos < 0)
return null;
else
return vals.remove(pos);
}
// Find the numeric index of the key as quickly as possible based on
// the type of ListMM being implemented. Return a negative number if
// the given key is not present.
public abstract int indexOf(K key);
// Associate the given key with the given value in the
// lists. Creates an ordering of keys that is compatible with how
// indexOf() works to locate keys.
public abstract V put(K key, V value);
}

You already specify that K type
AbstractListMM<K,V> implements IMiniMap<K,V>
will be a type String which is from here:
IMiniMap<String,Integer> map = new SimpleListMM<String,Integer>();
And therefore you need to supply your remove method remove(K key) as a String not an Integer
map.remove("B");

Related

HashMap.containsKey(key) failing to find key, with custom class used as key type

TL;DR: I made a hashmap object with my own "Pair" class objects to be used as Keys. When I use hashmap.containsKey(Pair), it fails to find the key.
I have a class called Pair, code shown below. It's supposed to be a container for two objects. The first object can be of any type, whereas the second object must be an integer. This isn't great design but I coded it this way so I could reuse the class for other purposes within my program.
import java.util.ArrayList;
public class Pair<L> {
private L left;
private int right;
public Pair(L left, int right) {
this.left = left;
this.right = right;
}
public L getLeft() { return left; }
public int getRight() { return right; }
public void ToString() {
System.out.println(left + "," + right);
}
public boolean equals(Pair p) {
return (this.getLeft().equals(p.getLeft()) && this.getRight() == p.getRight());
}
public ArrayList<Pair> neighbors(int rowLimit, int ColumnLimit) {
ArrayList<Pair> neighbors = new ArrayList<Pair>();
Pair neighborL;
Pair neighborR;
Pair neighborU;
Pair neighborD;
if (((int)this.left-1 >= 0)) {
neighborU = new Pair((int)this.left-1, this.right);
// neighborU.ToString();
neighbors.add(neighborU);
}
if ((int)this.left+1 < rowLimit) {
neighborD = new Pair((int)this.left+1, this.right);
// neighborD.ToString();
neighbors.add(neighborD);
}
if ((int)this.right-1 >= 0) {
neighborL = new Pair((int)this.left, this.right-1);
// neighborL.ToString();
neighbors.add(neighborL);
}
if ((int)this.right+1 < ColumnLimit) {
neighborR = new Pair((int)this.left, this.right+1);
// neighborR.ToString();
neighbors.add(neighborR);
}
return neighbors;
}
}
I'm storing Pairs as keys in a hashmap like this:
Map<Pair, Integer> costSoFar = new HashMap<Pair, Integer>();
costSoFar.put(sLocale, 0);
When I run the line below, which is to say, if the key is not in the hashmap:
if (!costSoFar.containsKey(next))
It evaluates to true, even when I know the key is in there, as I've checked through debugging.
If anyone can help clear up why the hashmap isn't recognizing the keys it'd be much appreciated. Perhaps my equals method isn't up to scratch?
Why are you using Generics if L seems to be int too?
Replace your equals:
public boolean equals(Object o) {
if (o instanceof Pair){
Pair p = (Pair)o;
return (this.getLeft().equals(p.getLeft()) && this.getRight() == p.getRight());
}
return false;
}
And implement int hashCode():
public int hashCode() {
return this.getLeft() * 31 + this.getRight();
}
Method containsKey(Object) in java.util.HashMap use the java.util.HashMap.getEntry(Object) which call hashCode() to obtain the hashCode for the Key and use it to retrive the object. You need to override java.lang.Object.hashCode() to make your code properly work.

why java class WeakReference does not override hashcode and equals

I was expecting the WeakReference class to override hashCode and equals methods like this
class WeakReference<T>{
T ref;
int hashCode(){
return ref.hashCode();
}
boolean equals(Object o){
return ref.equals(o);
}
}
So that I could use We WeakReference directly as key in hashmaps like
Person p1 = new Person("p1");
WeakReference<Person> wr = new WeakReference<Person>(p1);
map.put(wr, "some value object");
But when I tested I found out that hashCode and equals are not overridden
Person p1 = new Person("p1");
WeakReference<Person> wr = new WeakReference<Person>(p1);
WeakReference<Person> wr2 = new WeakReference<Person>(p1);
System.out.println(wr.hashCode()); // prints x
System.out.println(wr2.hashCode()); // prints y
System.out.println(wr.equals(wr2)); // prints false
Any specific reasons reasons that hashCode and equals are not overridden in WeakReference class?
An important aspect of any key on a Map (or element of a Set) is that it must be immutable (or at least not change) once it has been added to the collection. Changing a key has undefined behaviour which is highly unlikely to work.
A WeakReference can change at any time due to a GC being performed i.e. in ways you have no control over, which makes the equals/hashCode inappropriate for general collections which use these.
I was trying to make MyWeakConcurrentHashMap
A simple way of doing this is to have an array of WeakHashMaps. e.g. 32 partitions . Use the hashCode() to determine which WeakHashMap to use. This way you can have a thread accessing each of the individual WeakHashMap at once (best case)
As you have more concurrency you can increase the number of partitions.
It seems like WeakHashMap uses Entry which extends WeakReference
and overrides both hashCode and equals, you can take a look of their implementation.
/**
* The entries in this hash table extend WeakReference, using its main ref
* field as the key.
*/
private static class Entry<K,V> extends WeakReference<Object> implements Map.Entry<K,V> {
V value;
final int hash;
Entry<K,V> next;
/**
* Creates new entry.
*/
Entry(Object key, V value,
ReferenceQueue<Object> queue,
int hash, Entry<K,V> next) {
super(key, queue);
this.value = value;
this.hash = hash;
this.next = next;
}
#SuppressWarnings("unchecked")
public K getKey() {
return (K) WeakHashMap.unmaskNull(get());
}
public V getValue() {
return value;
}
public V setValue(V newValue) {
V oldValue = value;
value = newValue;
return oldValue;
}
public boolean equals(Object o) {
if (!(o instanceof Map.Entry))
return false;
Map.Entry<?,?> e = (Map.Entry<?,?>)o;
K k1 = getKey();
Object k2 = e.getKey();
if (k1 == k2 || (k1 != null && k1.equals(k2))) {
V v1 = getValue();
Object v2 = e.getValue();
if (v1 == v2 || (v1 != null && v1.equals(v2)))
return true;
}
return false;
}
public int hashCode() {
K k = getKey();
V v = getValue();
return Objects.hashCode(k) ^ Objects.hashCode(v);
}
public String toString() {
return getKey() + "=" + getValue();
}
}

Implementation of Custom HashMap code issues

I am preparing my own custom HashMap implementation in Java. Below is my imlementation.
public class Entry<K,V> {
private final K key;
private V value;
private Entry<K,V> next;
public Entry(K key, V value, Entry<K,V> next) {
this.key = key;
this.value = value;
this.next = next;
}
public V getValue() {
return value;
}
public void setValue(V value) {
this.value = value;
}
public Entry<K, V> getNext() {
return next;
}
public void setNext(Entry<K, V> next) {
this.next = next;
}
public K getKey() {
return key;
}
}
public class MyCustomHashMap<K,V> {
private int DEFAULT_BUCKET_COUNT = 10;
private Entry<K,V>[] buckets;
public MyCustomHashMap() {
buckets = new Entry[DEFAULT_BUCKET_COUNT];
for (int i = 0;i<DEFAULT_BUCKET_COUNT;i++)
buckets[i] = null;
}
public void put(K key,V value){
/**
* This is the new node.
*/
Entry<K,V> newEntry = new Entry<K,V>(key, value, null);
/**
* If key is null, then null keys always map to hash 0, thus index 0
*/
if(key == null){
buckets[0] = newEntry;
}
/**
* get the hashCode of the key.
*/
int hash = hash(key);
/**
* if the index does of the bucket does not contain any element then assign the node to the index.
*/
if(buckets[hash] == null) {
buckets[hash] = newEntry;
} else {
/**
* we need to traverse the list and compare the key with each of the keys till the keys match OR if the keys does not match then we need
* to add the node at the end of the linked list.
*/
Entry<K,V> previous = null;
Entry<K,V> current = buckets[hash];
while(current != null) {
boolean done = false;
while(!done) {
if(current.getKey().equals(key)) {
current.setValue(value);
done = true; // if the keys are same then replace the old value with the new value;
} else if (current.getNext() == null) {
current.setNext(newEntry);
done = true;
}
current = current.getNext();
previous = current;
}
}
previous.setNext(newEntry);
}
}
public V getKey(K key) {
int hash = hash(key);
if(buckets[hash] == null) {
return null;
} else {
Entry<K,V> temp = buckets[hash];
while(temp != null) {
if(temp.getKey().equals(key))
return temp.getValue(); // returns value corresponding to key.
temp = temp.getNext();
}
return null; //return null if key is not found.
}
}
public void display() {
for(int i = 0; i < DEFAULT_BUCKET_COUNT; i++) {
if(buckets[i] != null) {
Entry<K,V> entry = buckets[i];
while(entry != null){
System.out.print("{"+entry.getKey()+"="+entry.getValue()+"}" +" ");
entry=entry.getNext();
}
}
}
}
public int bucketIndexForKey(K key) {
int bucketIndex = key.hashCode() % buckets.length;
return bucketIndex;
}
/**
*
* #param key
* #return
*/
private int hash(K key){
return Math.abs(key.hashCode()) % buckets.length;
}
public static void main(String[] args) {
// TODO Auto-generated method stub
MyCustomHashMap<String, Integer> myCustomHashMap = new MyCustomHashMap<String, Integer>();
myCustomHashMap.put("S", 22);
myCustomHashMap.put("S", 1979);
myCustomHashMap.put("V", 5);
myCustomHashMap.put("R", 31);
System.out.println("Value corresponding to key R: "+myCustomHashMap.getKey("R"));
System.out.println("Value corresponding to key V: "+myCustomHashMap.getKey("V"));
System.out.println("Displaying the contents of the HashMap:: ");
myCustomHashMap.display();
}
}
1) I feel that put (K key,V value) is somewhat flawed. Please do kindly validate and let me know what's wrong here. On entering the same key its giving me wrong result. I have not yet tested it for collision cases having different keys.
2) It is said that we rehash the hashCode so that it eliminates wrong implementation of hashCode. how do I do it because if I give hashCode of key i.e. hash(key.hashCode()) then it dosn't take as it can't compute hashCode of int. How to do this?
Any help would be highly appreciated.
Thanks
Sid
You handle null key incorrectly :
if(key == null){
buckets[0] = newEntry;
}
It's possible that buckets[0] already contains entries, in which case you will lose those entries.
The following loop has some issues :
Entry<K,V> previous = null;
Entry<K,V> current = buckets[hash];
while(current != null) {
boolean done = false;
while(!done) {
if(current.getKey().equals(key)) {
current.setValue(value);
done = true;
} else if (current.getNext() == null) {
current.setNext(newEntry);
done = true;
}
current = current.getNext();
previous = current; // you are not really setting previous to
// to the previous Entry in the list - you
// are setting it to the current Entry
}
}
previous.setNext(newEntry); // you don't need this statement. You
// already have a statement inside the
// loop that adds the new Entry to the list
It looks like removing any statements related to previous will fix this loop.
EDIT:
As kolakao commented, in order for your implementation to be efficient (i.e. require expected constant time for get and put), you must resize the HashMap when the number of entries exceeds some threshold (in order for the average number of entries in each bucket to be bound by a constant).
It is said that we rehash the hashCode so that it eliminates wrong implementation of hashCode. how do I do it because if I give hashCode of key i.e. hash(key.hashCode()) then it dosn't take as it can't compute hashCode of int. How to do this?
The idea of re-hashing doesn't involve calling hashCode for the hashCode of the key. It involves running some hardcoded function on the value obtained by key.hashCode().
For example, in Java 7 implementation of HashMap, the following function is used :
static int hash(int h) {
// This function ensures that hashCodes that differ only by
// constant multiples at each bit position have a bounded
// number of collisions (approximately 8 at default load factor).
h ^= (h >>> 20) ^ (h >>> 12);
return h ^ (h >>> 7) ^ (h >>> 4);
}
Then you use it with :
int hash = hash(key.hashCode());
int bucket = hash % buckets.length;

binarySearch implementation issues

When I tried to write the following line:
int foundIndex = Collections.<K>binarySearch(keys, key);
it shows the error: The parameterized method <K>binarySearch(List<? extends Comparable<? super K>>, K) of type
Collections is not applicable for the arguments (List<K>, K)
What does the above error mean and what did I do wrong in my code?
// Comparator used to sort elements; may be null if elements are Comparable
public final Comparator<K> cmp;
{
super(new ArrayList<K>(), new ArrayList<V>());
cmp = new MyComparator<K>();
}
// Use the given comparator to sort the keys
//super(new ArrayList<K>(), new ArrayList<V>());
this.cmp = cmp;
}
{
if(!(key instanceof Comparable) && cmp == null)
throw new RuntimeException("The key is not instance of Comparable or comparator object is null");
}
public int indexOf(K key)
{
int foundIndex = Collections.<K>binarySearch(keys, key);
return foundIndex;
}
public int compareTo(K otherKey)
{
int result = 0;
for(int i = 0; i < keys.size(); i++)
{
result = ((Comparable<K>) keys.get(i)).compareTo(otherKey);
}
return result;
}
MyComparator class
import java.util.Comparator;
#Override
public int compare(K key1, K key2)
{
return -1;
}
}
Your problem is that FastGetListMM<K, V> is implementing Comparable<K> but that Collections.<K>binarySearch(list, value) is expecting a list of Comparable<K>.
It is K that should implement Comparable<K>, not FastGetListMM. If you want to use Collections.<K>binarySearch(keys, key) you need FastGetListMM to implement List<Comparable<K>> and not Comparable<K> - and make sure that all the items in FastGetListMM are arranged in ascending order.
To use that implementation of binary search you need K to implement comparable there is another function that receives the comparable itself. In your case you need to call Collections.<K>binarySearch(keys, key, new MyComparator());

How to implement a Least Frequently Used (LFU) cache?

Least Frequently Used (LFU) is a type of cache algorithm used to manage memory within a computer. The standard characteristics of this method involve the system keeping track of the number of times a block is referenced in memory. When the cache is full and requires more room the system will purge the item with the lowest reference frequency.
What would be the best way to implement a most-recently-used cache of objects, say in Java?
I've already implemented one using LinkedHashMap(by maintaining the no. of times objects are accessed) But I'm curious if any of the new concurrent collections would be better candidates.
Consider this case : Suppose cache is full and we need to make space for another one. Say two objects are noted in cache which are accessed for one time only. Which one to remove if we come to know that other(which is not in cache)object is being accessed for more than once ?
Thanks!
You might benefit from the LFU implementation of ActiveMQ: LFUCache
They have provided some good functionality.
I think, the LFU data structure must combine priority queue (for maintaining fast access to lfu item) and hash map (for providing fast access to any item by its key); I would suggest the following node definition for each object stored in cache:
class Node<T> {
// access key
private int key;
// counter of accesses
private int numAccesses;
// current position in pq
private int currentPos;
// item itself
private T item;
//getters, setters, constructors go here
}
You need key for referring to an item.
You need numAccesses as a key for priority queue.
You need currentPos to be able to quickly find a pq position of item by key.
Now you organize hash map (key(Integer) -> node(Node<T>)) to quickly access items and min heap-based priority queue using number of accesses as priority. Now you can very quickly perform all operations (access, add new item, update number of acceses, remove lfu). You need to write each operation carefully, so that it maintains all the nodes consistent (their number of accesses, their position in pq and there existence in hash map). All operations will work with constant average time complexity which is what you expect from cache.
According to me, the best way to implement a most-recently-used cache of objects would be to include a new variable as 'latestTS' for each object. TS stands for timestamp.
// A static method that returns the current date and time as milliseconds since January 1st 1970
long latestTS = System.currentTimeMillis();
ConcurrentLinkedHashMap is not yet implemented in Concurrent Java Collections.
(Ref: Java Concurrent Collection API). However, you can try and use ConcurrentHashMap and DoublyLinkedList
About the case to be considered: in such case, as I have said that you can declare latestTS variable, based upon the value of latestTS variable, you can remove an entry and add the new object. (Don't forget to update frequency and latestTS of the new object added)
As you have mentioned, you can use LinkedHashMap as it gives element access in O(1) and also, you get the order traversal.
Please, find the below code for LFU Cache:
(PS: The below code is the answer for the question in the title i.e. "How to implement LFU cache")
import java.util.LinkedHashMap;
import java.util.Map;
public class LFUCache {
class CacheEntry
{
private String data;
private int frequency;
// default constructor
private CacheEntry()
{}
public String getData() {
return data;
}
public void setData(String data) {
this.data = data;
}
public int getFrequency() {
return frequency;
}
public void setFrequency(int frequency) {
this.frequency = frequency;
}
}
private static int initialCapacity = 10;
private static LinkedHashMap<Integer, CacheEntry> cacheMap = new LinkedHashMap<Integer, CacheEntry>();
/* LinkedHashMap is used because it has features of both HashMap and LinkedList.
* Thus, we can get an entry in O(1) and also, we can iterate over it easily.
* */
public LFUCache(int initialCapacity)
{
this.initialCapacity = initialCapacity;
}
public void addCacheEntry(int key, String data)
{
if(!isFull())
{
CacheEntry temp = new CacheEntry();
temp.setData(data);
temp.setFrequency(0);
cacheMap.put(key, temp);
}
else
{
int entryKeyToBeRemoved = getLFUKey();
cacheMap.remove(entryKeyToBeRemoved);
CacheEntry temp = new CacheEntry();
temp.setData(data);
temp.setFrequency(0);
cacheMap.put(key, temp);
}
}
public int getLFUKey()
{
int key = 0;
int minFreq = Integer.MAX_VALUE;
for(Map.Entry<Integer, CacheEntry> entry : cacheMap.entrySet())
{
if(minFreq > entry.getValue().frequency)
{
key = entry.getKey();
minFreq = entry.getValue().frequency;
}
}
return key;
}
public String getCacheEntry(int key)
{
if(cacheMap.containsKey(key)) // cache hit
{
CacheEntry temp = cacheMap.get(key);
temp.frequency++;
cacheMap.put(key, temp);
return temp.data;
}
return null; // cache miss
}
public static boolean isFull()
{
if(cacheMap.size() == initialCapacity)
return true;
return false;
}
}
Here's the o(1) implementation for LFU - http://dhruvbird.com/lfu.pdf
I have tried to implement this below LFU cache implementation. Took reference from this -
LFU paper. My implementation is working nicely.
If anyone wants to provide any further suggestion to improve it again, please let me know.
import java.util.HashMap;
import java.util.Map;
import java.util.Objects;
import java.util.TreeMap;
public class LFUCacheImplementation {
private Map<Integer, Node> cache = new HashMap<>();
private Map<Integer, Integer> counts = new HashMap<>();
private TreeMap<Integer, DoublyLinkedList> frequencies = new TreeMap<>();
private final int CAPACITY;
public LFUCache(int capacity) {
this.CAPACITY = capacity;
}
public int get(int key) {
if (!cache.containsKey(key)) {
return -1;
}
Node node = cache.get(key);
int frequency = counts.get(key);
frequencies.get(frequency).remove(new Node(node.key(), node.value()));
removeFreq(frequency);
frequencies.computeIfAbsent(frequency + 1, k -> new DoublyLinkedList()).add(new Node(node.key(), node.value()));
counts.put(key, frequency + 1);
return cache.get(key).value();
}
public void set(int key, int value) {
if (!cache.containsKey(key)) {
Node node = new Node(key, value);
if (cache.size() == CAPACITY) {
int l_count = frequencies.firstKey();
Node deleteThisNode = frequencies.get(l_count).head();
frequencies.get(l_count).remove(deleteThisNode);
int deleteThisKey = deleteThisNode.key();
removeFreq(l_count);
cache.remove(deleteThisKey);
counts.remove(deleteThisKey);
}
cache.put(key, node);
counts.put(key, 1);
frequencies.computeIfAbsent(1, k -> new DoublyLinkedList()).add(node);
}
}
private void removeFreq(int frequency) {
if (frequencies.get(frequency).size() == 0) {
frequencies.remove(frequency);
}
}
public Map<Integer, Node> getCache() {
return cache;
}
public Map<Integer, Integer> getCounts() {
return counts;
}
public TreeMap<Integer, DoublyLinkedList> getFrequencies() {
return frequencies;
}
}
class Node {
private int key;
private int value;
private Node next;
private Node prev;
public Node(int key, int value) {
this.key = key;
this.value = value;
}
public Node getNext() {
return next;
}
public void setNext(Node next) {
this.next = next;
}
public Node getPrev() {
return prev;
}
public void setPrev(Node prev) {
this.prev = prev;
}
public int key() {
return key;
}
public int value() {
return value;
}
#Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof Node)) return false;
Node node = (Node) o;
return key == node.key &&
value == node.value;
}
#Override
public int hashCode() {
return Objects.hash(key, value);
}
#Override
public String toString() {
return "Node{" +
"key=" + key +
", value=" + value +
'}';
}
}
class DoublyLinkedList {
private int size;
private Node head;
private Node tail;
public void add(Node node) {
if (null == head) {
head = node;
} else {
tail.setNext(node);
node.setPrev(tail);
}
tail = node;
size++;
}
public void remove(Node node) {
if(null == head || null == node) {
return;
}
if(this.size() == 1 && head.equals(node)) {
head = null;
tail = null;
} else if (head.equals(node)) {
head = node.getNext();
head.setPrev(null);
} else if (tail.equals(node)) {
Node prevToTail = tail.getPrev();
prevToTail.setNext(null);
tail = prevToTail;
} else {
Node current = head.getNext();
while(!current.equals(tail)) {
if(current.equals(node)) {
Node prevToCurrent = current.getPrev();
Node nextToCurrent = current.getNext();
prevToCurrent.setNext(nextToCurrent);
nextToCurrent.setPrev(prevToCurrent);
break;
}
current = current.getNext();
}
}
size--;
}
public Node head() {
return head;
}
public int size() {
return size;
}
}
Client code to use the above cache implementation -
import java.util.Map;
public class Client {
public static void main(String[] args) {
Client client = new Client();
LFUCache cache = new LFUCache(4);
cache.set(11, function(11));
cache.set(12, function(12));
cache.set(13, function(13));
cache.set(14, function(14));
cache.set(15, function(15));
client.print(cache.getFrequencies());
cache.get(13);
cache.get(13);
cache.get(13);
cache.get(14);
cache.get(14);
cache.get(14);
cache.get(14);
client.print(cache.getCache());
client.print(cache.getCounts());
client.print(cache.getFrequencies());
}
public void print(Map<Integer, ? extends Object> map) {
for(Map.Entry<Integer, ? extends Object> entry : map.entrySet()) {
if(entry.getValue() instanceof Node) {
System.out.println("Cache Key => "+entry.getKey()+", Cache Value => "+((Node) entry.getValue()).toString());
} else if (entry.getValue() instanceof DoublyLinkedList) {
System.out.println("Frequency Key => "+entry.getKey()+" Frequency Values => [");
Node head = ((DoublyLinkedList) entry.getValue()).head();
while(null != head) {
System.out.println(head.toString());
head = head.getNext();
}
System.out.println(" ]");
} else {
System.out.println("Count Key => "+entry.getKey()+", Count Value => "+entry.getValue());
}
}
}
public static int function(int key) {
int prime = 31;
return key*prime;
}
}
How about a priority queue? You can keep elements sorted there with keys representing the frequency. Just update the object position in the queue after visiting it. You can update just from time to time for optimizing the performance (but reducing precision).
Many implementations I have seen have runtime complexity O(log(n)). This means, when the cache size is n, the time needed to insert/remove an element into/from chache is logarithmic. Such implementations use usually a min heap to maintain usage frequencies of elements. The root of the heap contains the element with lowest frequency, and can be accessed in O(1) time. But to maintain the heap property we have to move an element, every time it is used (and frequency is incremented) inside of the heap, to place it into proper position, or when we have to insert new element into the cache (and so put it into the heap).
But the runtime complexity can be reduced to O(1), when we maintain a hashmap (Java) or unordered_map (C++) with the element as key. Additinally we need two sorts of lists, frequency list and elements lists. The elements lists contain elements that have same frequency, and the frequency list contain the element lists.
frequency list
1 3 6 7
a k y x
c l z
m n
Here in the example we see the frequency list that has 4 elements (4 elements lists). The element list 1 contains elements (a,c,m), the elements list 3 contains elements (k, l, n) etc.
Now, when we use say element y, we have to increment its frequency and put it in the next list. Because the elements list with frequency 6 becomes empty, we delete it. The result is:
frequency list
1 3 7
a k y
c l x
m n z
We place the element y in the begin of the elements list 7. When we have to remove elements from the list later, we will start from the end (first z, then x and then y).
Now, when we use element n, we have to increment its frequency and put it into the new list, with frequencies 4:
frequency list
1 3 4 7
a k n y
c l x
m z
I hope the idea is clear. I provide now my C++ implementation of the LFU cache, and will add later a Java implementation.
The class has just 2 public methods, void set(key k, value v)
and bool get(key k, value &v). In the get method the value to retrieve will be set per reference when the element is found, in this case the method returns true. When the element is not found, the method returns false.
#include<unordered_map>
#include<list>
using namespace std;
typedef unsigned uint;
template<typename K, typename V = K>
struct Entry
{
K key;
V value;
};
template<typename K, typename V = K>
class LFUCache
{
typedef typename list<typename Entry<K, V>> ElementList;
typedef typename list <pair <uint, ElementList>> FrequencyList;
private:
unordered_map <K, pair<typename FrequencyList::iterator, typename ElementList::iterator>> cacheMap;
FrequencyList elements;
uint maxSize;
uint curSize;
void incrementFrequency(pair<typename FrequencyList::iterator, typename ElementList::iterator> p) {
if (p.first == prev(elements.end())) {
//frequency list contains single list with some frequency, create new list with incremented frequency (p.first->first + 1)
elements.push_back({ p.first->first + 1, { {p.second->key, p.second->value} } });
// erase and insert the key with new iterator pair
cacheMap[p.second->key] = { prev(elements.end()), prev(elements.end())->second.begin() };
}
else {
// there exist element(s) with higher frequency
auto pos = next(p.first);
if (p.first->first + 1 == pos->first)
// same frequency in the next list, add the element in the begin
pos->second.push_front({ p.second->key, p.second->value });
else
// insert new list before next list
pos = elements.insert(pos, { p.first->first + 1 , {{p.second->key, p.second->value}} });
// update cachMap iterators
cacheMap[p.second->key] = { pos, pos->second.begin() };
}
// if element list with old frequency contained this singe element, erase the list from frequency list
if (p.first->second.size() == 1)
elements.erase(p.first);
else
// erase only the element with updated frequency from the old list
p.first->second.erase(p.second);
}
void eraseOldElement() {
if (elements.size() > 0) {
auto key = prev(elements.begin()->second.end())->key;
if (elements.begin()->second.size() < 2)
elements.erase(elements.begin());
else
elements.begin()->second.erase(prev(elements.begin()->second.end()));
cacheMap.erase(key);
curSize--;
}
}
public:
LFUCache(uint size) {
if (size > 0)
maxSize = size;
else
maxSize = 10;
curSize = 0;
}
void set(K key, V value) {
auto entry = cacheMap.find(key);
if (entry == cacheMap.end()) {
if (curSize == maxSize)
eraseOldElement();
if (elements.begin() == elements.end()) {
elements.push_front({ 1, { {key, value} } });
}
else if (elements.begin()->first == 1) {
elements.begin()->second.push_front({ key,value });
}
else {
elements.push_front({ 1, { {key, value} } });
}
cacheMap.insert({ key, {elements.begin(), elements.begin()->second.begin()} });
curSize++;
}
else {
entry->second.second->value = value;
incrementFrequency(entry->second);
}
}
bool get(K key, V &value) {
auto entry = cacheMap.find(key);
if (entry == cacheMap.end())
return false;
value = entry->second.second->value;
incrementFrequency(entry->second);
return true;
}
};
Here are examples of usage:
int main()
{
LFUCache<int>cache(3); // cache of size 3
cache.set(1, 1);
cache.set(2, 2);
cache.set(3, 3);
cache.set(2, 4);
rc = cache.get(1, r);
assert(rc);
assert(r == 1);
// evict old element, in this case 3
cache.set(4, 5);
rc = cache.get(3, r);
assert(!rc);
rc = cache.get(4, r);
assert(rc);
assert(r == 5);
LFUCache<int, string>cache2(2);
cache2.set(1, "one");
cache2.set(2, "two");
string val;
rc = cache2.get(1, val);
if (rc)
assert(val == "one");
else
assert(false);
cache2.set(3, "three"); // evict 2
rc = cache2.get(2, val);
assert(rc == false);
rc = cache2.get(3, val);
assert(rc);
assert(val == "three");
}
Here is a simple implementation of LFU cache in Go/Golang based on here.
import "container/list"
type LFU struct {
cache map[int]*list.Element
freqQueue map[int]*list.List
cap int
maxFreq int
lowestFreq int
}
type entry struct {
key, val int
freq int
}
func NewLFU(capacity int) *LFU {
return &LFU{
cache: make(map[int]*list.Element),
freqQueue: make(map[int]*list.List),
cap: capacity,
maxFreq: capacity - 1,
lowestFreq: 0,
}
}
// O(1)
func (c *LFU) Get(key int) int {
if e, ok := c.cache[key]; ok {
val := e.Value.(*entry).val
c.updateEntry(e, val)
return val
}
return -1
}
// O(1)
func (c *LFU) Put(key int, value int) {
if e, ok := c.cache[key]; ok {
c.updateEntry(e, value)
} else {
if len(c.cache) == c.cap {
c.evict()
}
if c.freqQueue[0] == nil {
c.freqQueue[0] = list.New()
}
e := c.freqQueue[0].PushFront(&entry{key, value, 0})
c.cache[key] = e
c.lowestFreq = 0
}
}
func (c *LFU) updateEntry(e *list.Element, val int) {
key := e.Value.(*entry).key
curFreq := e.Value.(*entry).freq
c.freqQueue[curFreq].Remove(e)
delete(c.cache, key)
nextFreq := curFreq + 1
if nextFreq > c.maxFreq {
nextFreq = c.maxFreq
}
if c.lowestFreq == curFreq && c.freqQueue[curFreq].Len() == 0 {
c.lowestFreq = nextFreq
}
if c.freqQueue[nextFreq] == nil {
c.freqQueue[nextFreq] = list.New()
}
newE := c.freqQueue[nextFreq].PushFront(&entry{key, val, nextFreq})
c.cache[key] = newE
}
func (c *LFU) evict() {
back := c.freqQueue[c.lowestFreq].Back()
delete(c.cache, back.Value.(*entry).key)
c.freqQueue[c.lowestFreq].Remove(back)
}

Categories

Resources