Data structures 分类浏览历史的数据结构

Data structures 分类浏览历史的数据结构,data-structures,browser-history,Data Structures,Browser History,假设我想要实现浏览器历史记录功能。如果我第一次访问url,它会进入历史记录,如果我再次访问同一页面,它会出现在历史记录列表中。 假设我只显示排名前20位的网站,但我可以选择查看上个月、上周等的历史记录 最好的方法是什么?我会使用哈希映射来插入/检查它是否在之前访问过,但如何对最近访问过的文件进行有效排序,我不想使用树映射或树集。此外,如何存储周和月的历史记录。浏览器关闭时是否将其写入磁盘?当我单击clear history时,数据结构是如何删除的?这是Java ish代码 您将需要两种数据结构:

假设我想要实现浏览器历史记录功能。如果我第一次访问url,它会进入历史记录,如果我再次访问同一页面,它会出现在历史记录列表中。 假设我只显示排名前20位的网站,但我可以选择查看上个月、上周等的历史记录


最好的方法是什么?我会使用哈希映射来插入/检查它是否在之前访问过,但如何对最近访问过的文件进行有效排序,我不想使用树映射或树集。此外,如何存储周和月的历史记录。浏览器关闭时是否将其写入磁盘?当我单击clear history时,数据结构是如何删除的?

这是Java ish代码

您将需要两种数据结构:哈希映射和双链接列表。双链接列表包含按时间戳排序的历史对象(包含url字符串和时间戳);哈希映射是一个
映射
,以URL作为键

class History {
  History prev
  History next
  String url
  Long timestamp
  void remove() {
    prev.next = next
    next.prev = prev
    next = null
    prev = null
  }
}
将url添加到历史记录时,请检查它是否在哈希映射中;如果是,则更新其时间戳,将其从链表中删除,并将其添加到链表的末尾。如果它不在哈希映射中,则将其添加到哈希映射中,并将其添加到链接列表的末尾。添加url(无论它是否已经在哈希映射中)是一个固定时间操作

class Main {
  History first // first element of the linked list
  History last // last element of the linked list
  HashMap<String, History> map

  void add(String url) {
    History hist = map.get(url)
    if(hist != null) {
      hist.remove()
      hist.timestamp = System.currenttimemillis()
    } else {
      hist = new History(url, System.currenttimemillis())
      map.add(url, hist)
    }
    last.next = hist
    hist.prev = last
    last = hist
  }
}
主类{
历史记录第一//链表的第一个元素
历史记录最后//链表的最后一个元素
哈希映射
无效添加(字符串url){
History hist=map.get(url)
if(hist!=null){
hist.remove()
hist.timestamp=System.currenttimemillis()
}否则{
hist=新历史记录(url,System.currenttimemillis())
map.add(url,hist)
}
last.next=hist
hist.prev=last
最后=历史
}
}
要获取例如上周的历史记录,请向后遍历链接列表,直到找到正确的时间戳

如果关注线程安全,那么使用线程安全队列将URL添加到历史记录中,并使用单个线程处理该队列;这样,你的地图和链表就不需要线程安全,也就是说,你不需要担心锁等问题

对于持久性,您可以序列化/反序列化链表;反序列化链表时,通过遍历哈希映射并将其元素添加到映射中来重构哈希映射。然后,要清除历史记录,您需要在内存中为列表和映射设置空值,并删除将数据序列化到的文件


在内存消耗和IO(即(反)序列化成本)方面,更有效的解决方案是使用无服务器数据库,如;这样,您就不需要将历史记录保存在内存中,如果您想从(例如)上周获取历史记录,您只需要查询数据库,而不是遍历链表。然而,SQLite本质上是一个树映射(特别是一个B树,它针对存储在磁盘上的数据进行了优化)。

这里是一个基于Zim Zam O'Pootertoot的答案的Swift 4.0实现,包括一个用于遍历历史的迭代器:

import Foundation

class SearchHistory: Sequence {
    var first: SearchHistoryItem
    var last: SearchHistoryItem
    var map = [String: SearchHistoryItem]()
    var count = 0
    var limit: Int

    init(limit: Int) {
        first = SearchHistoryItem(name: "")
        last = first
        self.limit = Swift.max(limit, 2)
    }

    func add(name: String) {
        var item: SearchHistoryItem! = map[name]
        if item != nil {
            if item.name == last.name {
                last = last.prev!
            }
            item.remove()
            item.timestamp = Date()
        } else {
            item = SearchHistoryItem(name: name)
            count += 1
            map[name] = item
            if count > limit {
                first.next!.remove()
                count -= 1
            }
        }
        last.next = item
        item.prev = last
        last = item
    }

    func makeIterator() -> SearchHistory.SearchHistoryIterator {
        return SearchHistoryIterator(item: last)
    }

    struct SearchHistoryIterator: IteratorProtocol {
        var currentItem: SearchHistoryItem

        init(item: SearchHistoryItem) {
            currentItem = item
        }

        mutating func next() -> SearchHistoryItem? {
            var item: SearchHistoryItem? = nil
            if let prev = currentItem.prev {
                item = currentItem
                currentItem = prev
            }
            return item
        }
    }
}

class SearchHistoryItem {
    var prev: SearchHistoryItem?
    var next: SearchHistoryItem?
    var name: String
    var timestamp: Date

    init(name: String) {
        self.name = name
        timestamp = Date()
    }

    func remove() {
        prev?.next = next
        next?.prev = prev
        next = nil
        prev = nil
    }
}

如果使用的是哈希映射,则无法快速检索排序结果。你为什么不想使用树形图,又称红黑搜索树呢?因为红黑树在内部需要大量的旋转来保持平衡,特别是如果有大量的添加,我假设这发生在浏览器中,因为用户可以从已知站点跳到新站点。HashMap的性能会更好,问题是使用辅助数据结构对内容进行排序和移动。您希望历史缓存中有多少个站点?如果你一整年有10万页,我会很惊讶,但即使是100万页也没什么大不了的。只需将它们存储在线性列表中并按顺序搜索即可。这对于像浏览器这样的用户界面应用来说足够快了。太好了!。但这两者是如何联系在一起的呢?hashmap值是历史记录列表,那么它到底包含什么呢?对象在列表中的位置?@user775093使用哈希映射确保url不会在历史记录列表中出现两次。假设一个url位于历史记录列表中的
list[i]
-当您第二次从哈希映射中检索
list[i]
时添加url,然后在其上调用
remove
,将
list[i-1]
拼接到
list[i+1]
,从而从列表中删除
list[i]
(现在是
list[null]
),然后在列表末尾追加
List[null]
,使其成为
List[last]
。如果没有哈希映射,从历史记录列表中删除重复的URL将是一个线性时间操作。