简体   繁体   English

删除 java 中的元素表单链表,意外行为

[英]Removing an element form linked list in java, unexpected behavior

I am trying to implement lru cache in java using hashmap and linkedlist:我正在尝试使用 hashmap 和链表在 java 中实现 lru 缓存:

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.在上面的参考 function 中,对于在 map 中找到页面的 if 条件,该值已成功从链接列表中删除,我认为这不应该工作,因为我只保留 Z1D78DC8ED51214E51AEZB51144 中的页面值。 Now interestingly when I put ll.remove(page);现在有趣的是,当我把 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.对于下面的测试案例,代码的第一个价格有效,第二个无效,唯一的区别是 ll.remove(it) 和 ll.remove(page),它和 page 的值是一样的。

        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. Linkedhashmap 保持插入顺序。 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.只需用 Collections.synchronizedMap 包装 map。 Linkedhashmap can keep long type of key. Linkedhashmap 可以保留长类型的密钥。 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) .无需深入了解代码的太多细节,调用ll.remove(page)和调用 ll.remove ll.remove(it)时调用方法之间存在很大差异。

When calling ll.remove(it) , the type of it is Integer , so the method called is LinkedList.remove(Object) .调用ll.remove(it)时, it的类型是Integer ,所以调用的方法是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) .而当你调用ll.remove(page)时, page的类型是int ,所以你实际上调用了: LinkedList.remove(int) From the documentation of this method:从此方法的文档中:

Removes the element at the specified position in this list....删除此列表中指定 position 处的元素....

One method is removing the index at page , while the other is removing the value matching it .一种方法是删除page处的索引,而另一种方法是删除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))我认为您在调用ll.remove(page)以实现类似行为时可能想要做的是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));
}

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

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