【问题标题】:Removing an element form linked list in java, unexpected behavior在java中删除元素表单链表,意外行为
【发布时间】:2020-06-20 18:02:57
【问题描述】:

我正在尝试使用 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;
    }

}

在上面的引用函数中,对于在地图中找到页面的 if 条件,该值已成功从链接列表中删除,我认为这不应该起作用,因为我只在地图中保留页面值。 现在有趣的是,当我把 ll.remove(page);在上面的代码中,尽管页面的值和它相同,但它会中断。

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;
    }

我对这种行为感到非常惊讶。

对于下面的测试用例,代码的第一个价格有效,第二个无效,唯一的区别是 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();
}

【问题讨论】:

  • 不是答案,但可能会为您节省大量调试此类问题的时间,您知道LinkedHashMap吗?
  • 直奔问题:我发现很难理解问题所在。您能否添加一个示例来解释您预期会发生什么以及您实际得到了什么?
  • 我的问题是为什么第一段代码有效,为什么第二段代码无效,即使 ll.remove() 函数在两种情况下都获得相同的值。是的 LinkedHashMap 是要走的路,但我需要知道在上述情况下发生了什么。
  • 你能举一个具体的例子,一个代码成功,另一个代码失败?
  • 用一个例子更新了问题来测试它。

标签: java intellij-idea data-structures lru


【解决方案1】:

无需深入了解代码的太多细节,调用ll.remove(page) 和调用ll.remove(it) 时调用的哪个 方法之间存在很大差异。

调用ll.remove(it)时,it的类型是Integer,所以调用的方法是LinkedList.remove(Object)。来自此方法的文档:

从该列表中删除指定元素的第一个匹配项, 如果它存在....

当您调用ll.remove(page) 时,page 的类型是int,因此您实际上调用的是:LinkedList.remove(int)。来自此方法的文档:

删除此列表中指定位置的元素....

一种方法是删除page 处的索引,而另一种方法是删除与it 匹配的值。

我认为您在调用ll.remove(page) 以实现类似行为时可能想要做的是ll.remove(new Integer(page))

这里有一个 simple code 演示了这个问题:

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));
}

【讨论】:

  • 是的,就是这样。我将页面从 int 更改为 Integer 它可以工作。非常感谢。
【解决方案2】:

保留两个集合对于缓存来说是昂贵的,为什么不使用单个集合。 Linkedhashmap 保持插入顺序。此外,您必须牢记并发性。也许两个同时命中会使您丢失数据。只需使用 Collections.synchronizedMap 包装地图。 Linkedhashmap 可以保持长类型的键。您可以以毫秒为单位的时间作为键。然后你可以通过键搜索找到最后使用的元素,或者只是简单地删除最后插入的元素。

【讨论】:

  • 这是一个非常好的提示(在 cmets 中也提供了),但这并不能回答问题本身。
猜你喜欢
  • 1970-01-01
  • 2018-07-08
  • 1970-01-01
  • 1970-01-01
  • 2018-05-23
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多