如何以不可变的方式实现缓存?

5

我已经阅读并听说了很多关于不可变性的好处,所以我决定在我的一个兴趣项目中尝试它。我将所有字段声明为只读,并使通常会改变对象的所有方法返回一个新的修改版本。

这很好用,直到我遇到了一种情况:一个方法应该根据外部协议返回有关对象的某些信息而不修改它,但同时可以通过修改内部结构进行优化。特别是,在并查集算法中的树路径压缩中会发生这种情况。

当用户调用int find(int n)时,对象对外人来说似乎没有被修改。从概念上讲,它代表同一实体,但其内部字段被改变以优化运行时间。

我如何以不可变的方式实现这一点?


2
我认为,如果您以线程安全的方式进行变异,并且外部人员无法检测到,则可以将其视为不可变。 - Joe
@Joe 但是那样我就必须自己维护线程安全。就我所知,不可变性的美妙之处在于只需指定readonly关键字,就可以将其交给语言处理。 - Max Yankov
但是你说过你想要改变它。如果你不以线程安全的方式进行操作,那么你将失去不可变类型的一个好处(线程安全)。 - Joe
1
我可以想象一种解决方案,其中您的类具有延迟评估属性(可能使用 Lazy<T>),该属性实现了您的优化。 - Joe
1
有一本由Chris Okasaki所著的《函数数据结构》的书,也许它可以帮助你(如果你还没有阅读过它的话)。 - Pavel Davydov
显示剩余3条评论
1个回答

2
简短回答:您必须自己确保线程安全。
在字段上使用readonly关键字可以确保对象构造后包含该字段的对象不能被修改。因此,这个字段的唯一写入可以在构造函数(或字段初始化)中进行,并且在对象构造之前不会发生方法调用读取,因此readonly是线程安全的。
如果您想要实现缓存,就会打破只有一个写入发生的假设(因为“缓存写入”可能会在读取期间发生),因此在糟糕情况下可能会出现线程问题(比如从文件中读取行,两个线程可以使用相同的参数调用查找方法,但读取两行并因此获得不同的结果)。您想要实现的是观察型不可变性。这个相关问题关于记忆化也许可以帮助您找到一个优雅的答案。

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接