2
\$\begingroup\$

This is a follow-on qestion from: LRU cache design

Revised Implementation

package lrucache;

import java.util.LinkedHashMap;


public class LRUCacheV2<K, V> extends LinkedHashMap<K, V>{

    private static final int MAX_ENTRIES = 3;
         /**
     * super class constructor arguments are
     * cache size = MAX_ENTRIES
     * load factor = 0.75F
     * access order = true
     */
    public LRUCacheV2(){
        super(MAX_ENTRIES,0.75F,true);
    }

    @Override
    protected boolean removeEldestEntry(java.util.Map.Entry<K, V> entry) {
        return size() > MAX_ENTRIES;
    }

    public static void main(String[] args) {
        LRUCacheV2<String, Integer> cache = new LRUCacheV2<String, Integer>();
        cache.put("abc", 1);
        cache.put("def", 2);
        cache.put("ghi", 3);
        cache.put("jkl", 4);
        cache.put("mno", 5);
        cache.put("abc", 1);
        System.out.println(cache);
    }
}

Output

{jkl=4, mno=5, abc=1}
\$\endgroup\$
  • \$\begingroup\$ Thanks @ratchetfreak. Now i found an easy way to implement LRU cache. Here is the link for LinkedHashMap code. They gave nice explanation link \$\endgroup\$ – Venkat Kondeti Feb 11 '15 at 11:51
  • \$\begingroup\$ I'd prefer composition over inheritance here. \$\endgroup\$ – guido Feb 11 '15 at 11:56
  • \$\begingroup\$ @guido like this one? \$\endgroup\$ – ratchet freak Feb 11 '15 at 11:59
  • \$\begingroup\$ @ratchetfreak didn't see that code into detail, but definitely yes that's the idea \$\endgroup\$ – guido Feb 11 '15 at 12:03
  • \$\begingroup\$ @ratchetfreak can somebody please provide me links for locks concept. I want to get some good clarity about locks concept. \$\endgroup\$ – Venkat Kondeti Feb 11 '15 at 12:06
5
\$\begingroup\$

Your code looks fine, depending on the use case in which it is applied. A few people have mentioned "composition over inheritance". This is a good point, but it is not directly practical in this situation. You need inheritance in order to get access to the functionality you use in the LinkedHashMap. But, there is an approach where you use inheritance to access the functionality, and composition to expose it.

If you assume the direct approach you have taken is useful for accessing additional functionality from the LinkedHashMap, then your code is good. The variables are well named, the code is consistent (there's a glitch on the one comment line...), and it's all fine.

Whether the hybrid approach is useful to you depends on whether your use case requires just a smallish subset of the the LinkedHashMap functionality. For example, if you needed to access the entries on the content as Map.Entry instances, or if you need to do more complicated iterations, then you start to lose the benefit of the composition.

In this case, the first approach you have using straight inheritance has a place. Th secnd approach requires wrapping the functionality you need from the private inner cache.

This second approach requires tailoring your LRU cache to its specific use cases. At the moment, you have a very general LRU cache. On the other hand, you are only using the put method (and presumably the get()). You can create a class with just these methods (and a selection of other ones that make sense) and gave an internal inherited instance for access to the LinkedHashMap. Consider the following:

public final LRUCacheVC<K,V>() {

    // use inheritence to access LinkedHashMap functionality.
    private static class AccessOrderCache<T,U> extends LinkedHashMap<T,U> {
        private final int capacity;
        AccessOrderCache(final int capacity) {
            super(capacity, 0.75f, true);
            this.capacity = capacity;
        }
        @Override
        protected boolean removeEldestEntry(java.util.Map.Entry<T, U> entry) {
            return size() > MAX_ENTRIES;
        }
    }


    // Use composition to access the Inherited Cache.    
    private final AccessOrderCache<K,V> cache;

    public LRUCacheVC(final int capacity) {
        cache = new AccessOrderCache<>(capacity);
    }


    public V get(K key) {
        return cache.get(key);
    }

    public V put(K key, V value) {
        return cache.put(key, value)
    }

    @Override
    public int hashCode() {
        return cache.hashCode();
    }

    @Override
    public boolean equals(Object other) {
        return other != null 
            && (other instanceof LRUCacheVC)
            && cache.equals(((LRUCacheVC)other).cache);
    }

    @Override
    public String toString() {
        return cache.toString();
    }
}

Note how you use inheritence to access the LinkedHashMap concepts, then use composition to incorporate that new class in to your code.

You only expose that limited functionality that you want (get and put), as well as a few other standard methods that make life easier.

\$\endgroup\$
  • \$\begingroup\$ thanks a lot. I never think of advantage of inheritance and composition. Now i got some idea where to use inheritance and composition. \$\endgroup\$ – Venkat Kondeti Feb 11 '15 at 16:36

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.