简体   繁体   中英

Removing an element form linked list in java, unexpected behavior

I am trying to implement lru cache in java using hashmap and linkedlist:

public static class LRUCache{
    LinkedList<Integer> ll;
    HashMap<Integer, Integer> map;
    //HashSet<Integer> map;
    int size;
    LRUCache(int n){
        ll = new LinkedList<>();
        map = new HashMap<>();
        size=n;
    }

    int refer(int page){
        
        if(map.containsKey(page)){
            Integer it = map.get(page);
            //System.out.println("m.get(page)= " + map.get(page));
            //System.out.println(it + " it+page " + page);
            ll.remove(it);
        } else{
            if(map.size() >= size){
                map.remove(ll.getLast());
                ll.removeLast();
            }
        }
        ll.addFirst(page);
        //System.out.println(page + " page+peek " + ll.peekFirst());
        map.put(page, ll.peekFirst());
        return page;
    }

}

In the refer function above, for the if condition where page is found in the map the value is getting successfully removed from the linked list, which I think should not work as I am keeping only the page value in the map. Now interestingly when I put ll.remove(page); in the above code it breaks although the value of both page and it are same.

int refer(int page){

        if(map.containsKey(page)){
            Integer it = map.get(page);
            //System.out.println("m.get(page)= " + map.get(page));
            //System.out.println(it + " it+page " + page);
            ll.remove(page);
        } else{
            if(map.size() >= size){
                map.remove(ll.getLast());
                ll.removeLast();
            }
        }
        ll.addFirst(page);
        //System.out.println(page + " page+peek " + ll.peekFirst());
        map.put(page, ll.peekFirst());`enter code here`
        return page;
    }

I am really surprised by this behavior.

For the below test case, the first price of code works but the second doesn't, the only difference is ll.remove(it) and ll.remove(page),the values of it and page are same.

        void printCache(){
        System.out.print("| ");

        for(int i=0;i<ll.size();i++){
            System.out.print(ll.get(i) + " |" + " ");
        }
        System.out.println();
    }

}

public static void main(String[] args) {
    LRUCache lruCache = new LRUCache(4);


    lruCache.refer(11);
    lruCache.refer(12);
    lruCache.refer(13);
    lruCache.printCache();
    lruCache.refer(11);
    lruCache.printCache();
    lruCache.refer(14);
    lruCache.printCache();
    lruCache.refer(13);
    lruCache.printCache();
    lruCache.refer(15);
    lruCache.printCache();
}

To keep two collection is expensive for cache why don't you use single collection. Linkedhashmap keeps the insertion order. Also you have to keep concurrency in mind. Maybe two hit at the same time will cost you data loss. Just wrap the map with Collections.synchronizedMap. Linkedhashmap can keep long type of key. You can put with time in millisecond as key. Then you can find the last used element by key search or just simply removing last inserted element.

Without diving into too much details of your code, there is very big difference between which method is being called when invoking ll.remove(page) and when involing ll.remove(it) .

When calling ll.remove(it) , the type of it is Integer , so the method called is LinkedList.remove(Object) . From the documentation of this method:

Removes the first occurrence of the specified element from this list, if it is present....

While when you call ll.remove(page) , the type of page is int , so you actually call: LinkedList.remove(int) . From the documentation of this method:

Removes the element at the specified position in this list....

One method is removing the index at page , while the other is removing the value matching it .

I think what you might wanted to do in the call to ll.remove(page) to achieve similar behavior is ll.remove(new Integer(page))

Here is a simple code that demonstrates this issue:

public static void foo(int x) {
    System.out.println("Called foo(int)");
}
public static void foo(Integer x) {
    System.out.println("Called foo(Integer)");
}
public static void main (String[] args) throws java.lang.Exception
{
    int page = 5;
    Integer it = new Integer(10);
    foo(page);
    foo(it);
    foo(new Integer(page));
}

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM