我有一个非常简单的数据结构(基本上是一个包含一些数组和单个值的结构),但我需要记录数据结构的历史记录,以便我可以在任何时候有效地获取数据结构的内容及时。
有相对简单的方法吗?
我能想到的最好的方法是将整个数据结构封装起来,通过在functional data structures中存储数据来处理所有变异操作,然后为每个变异操作缓存数据结构的副本通过时间排序索引的映射(例如,具有实时作为键的TreeMap,或具有变异操作计数器的HashMap以及存储在TreeMaps中的一个或多个索引,将实时/滴答计数等映射到变异操作)
有什么建议吗?
编辑:在一种情况下,我已将历史记录作为一系列事务(这是从数据文件中读取项目),因此我可以重放它们,但这需要O(n)步骤( n =每次我需要访问数据时的事务数量。我正在寻找其他选择。
答案 0 :(得分:3)
你是对的。将数据存储在纯函数数据结构中是可行的方法。使用do / undo动作支持任何适度复杂的任何事情依赖于程序员意识到每个操作的所有副作用,这不会缩放和破坏封装。
答案 1 :(得分:2)
您应该使用某种形式的不可变的持久性数据结构,并且基于结构共享(即,使得版本之间不会发生变化的部分只能存储一次)。
我在这里创建了一个这样的数据结构的开源Java库:
http://code.google.com/p/mikeralib/source/browse/#svn/trunk/Mikera/src/mikera/persistent
这些都受到了Clojure持久性数据结构的启发,这些数据结构也可能适用于您的目的(它们也是用Java编写的)。
答案 2 :(得分:0)
要么像你已经建议的那样做,要么有一个带有代表不同变化的子类的某种基类。然后在运行时获取正确的类,方法是将版本/时间戳/任何内容传递给工厂,然后将工具移回右侧。
答案 3 :(得分:0)
如果您只存储一点数据而且没有太多更改,那么存储每个版本都可以。
如果您不需要经常访问旧版本的数据,我就不会对每个数据进行缓存,我只是制作它以便您可以重建它。
您可以通过将突变保存为事务并重放事务(可以随时停止)来实现此目的。
所以你从一个空的数据结构开始,你可能会得到一个“添加”指令,然后是“更改”和另一个“添加”,然后可能是“删除”。每个对象都包含要添加或更改的对象的COPY(不是指向同一对象的指针)。
您可以将每个操作连接到一个列表中,同时改变您的集合。
如果您发现需要较旧时间戳的版本,请从新的空集合开始,重播直到您点击该时间戳,然后停止并且您拥有当时的集合。
如果这是一个运行时间非常长的应用程序,并且您经常需要访问最近的项目,则可以为每个添加/更改/删除操作对象编写“撤消”,并实际来回变换数据。
因此,假设您拥有数据对象和这一系列突变,您可以轻松地在突变列表中上下运行,将数据对象来回更改为您想要的任何版本。
您甚至可以包含多个数据对象,只需创建一个新的空数据对象并将其运行到变异数组(将其视为时间轴 - 每个存储的变异将包含时间戳或某个版本号),直到获得它为止到你想要的时间戳 - 这样你就可以拥有你可以立即获得的“里程碑” - 例如,如果你为每个线程分配了一个里程碑,你可以使addMutation方法同步,这个数据集合将变为100%线程安全。
请注意,如果您实际返回数据对象,则只应返回数据的副本 - 否则,下次更改该里程碑时,它将改变您返回的数据对象。
嗯,您还可以包含“Rollup”功能 - 如果您决定不需要访问尾部(前几个事务),您可以将它们应用于“开始”结构,然后删除它们 - 从那时起,您将开始结构复制为从开始开始,而不是始终以空数据结构开始。
伙计,这是一个很棒的模式 - 现在我想实现它。
答案 4 :(得分:-1)
多级撤销可以基于模型(即数据结构)和一系列动作。每个操作都支持两个操作:“执行”和“撤消”。要对模型执行更改,请注册新操作并“执行”。这允许您在历史记录中来回“走动”,但不能在固定时间内访问特定索引处的模型状态。
这样的事情可能适用于你的情况吗?
答案 5 :(得分:-1)
应用程序运行多长时间?
看起来你可以按照你的建议 - 回放交易 - 但是在特定时间点(每小时或每天?)缓存数据结构和交易清单,以减轻不得不去的痛苦每次需要从头开始重建集合时,通过O(n)操作。
当然,在空间(缓存占用)和重建它所需的操作数之间肯定存在权衡,但希望你能够找到一个快乐的媒介。