Java中的不可变/持久列表

Java中的不可变/持久列表,java,algorithm,functional-programming,linked-list,immutability,Java,Algorithm,Functional Programming,Linked List,Immutability,作为一个pet项目,我试图在Java中实现一个不可变的列表数据结构,同时尽可能减少副本;我知道谷歌收藏,但这不是我想要的,因为列表操作只会返回旧列表的新副本 我想出了两种不同的方法来解决这个问题;两者都基于双链接列表,如下所示: [head: element1] <--> [element2] <--> [tail: element3] while (...) list = list.addElement(...); A: [head: element1] &

作为一个pet项目,我试图在Java中实现一个不可变的列表数据结构,同时尽可能减少副本;我知道谷歌收藏,但这不是我想要的,因为列表操作只会返回旧列表的新副本

我想出了两种不同的方法来解决这个问题;两者都基于双链接列表,如下所示:

[head: element1] <--> [element2] <--> [tail: element3]
while (...)
    list = list.addElement(...);
A: [head: element1] <--> [element2] <--> [tail: element3]
B: source: A, {add, element_to_add, 1}
这是O(1)。由于对列表的迭代只发生在head和tail之间,
A
将不知道关于
B
前面或后面的新元素的任何信息

当我们尝试在列表中插入或删除任意元素时,它会变得有趣

索引元素法 每个列表都有一个从0开始的唯一顺序id。每个元素都有一个与列表ID对应的
{prev,next}
指针数组:

  [element1] <--> [element2] <--> [element3] <--> [element4]
A:   [0] <---------> [0] <---------> [0] <---------> [0]
B:   [0] <---------> [1] <-------------------------> [1]
C:   ...
因为在任何给定的时间只有一次对列表的引用

迭代器方法 另一种方法是滥用迭代器,以使生成的列表看起来像预期的修改版本;因此,每个修改的不可变列表都包含对其“源”列表的引用和一个附加元组
{operation,element,position}
,如下所示:

[head: element1] <--> [element2] <--> [tail: element3]
while (...)
    list = list.addElement(...);
A: [head: element1] <--> [element2] <--> [tail: element3]
B: source: A, {add, element_to_add, 1}
A:[head:element1][element2][tail:element3]
B:来源:A,{add,element_to_add,1}
B
的迭代器然后调用其源列表迭代器(在本例中为
A
),除非遇到已修改(添加、删除或替换)的元素,在这种情况下,它返回该元素,然后继续使用源迭代器

这里明显的缺点是嵌套迭代器的深度随着列表的每个修改版本而增加。这意味着偶尔制作一个原始拷贝也是必要的


有人对如何改进这一点有什么建议吗?此外,任何指向60年代发明的任何数据结构的指针都是非常有用的:)

您可以创建一个类似head::tail的列表,并获得易于创建和良好内存占用的好处,然后提供一个API,该API在需要时在顶部分层以获得有效的随机访问

至于中间的高效变异,skip列表视图可能有一个将变异索引映射到元素的边表,以及一个二进制可搜索数组,在插入和删除后将原始索引映射到索引偏移量


所有这些映射都提出了一个问题,即如何为一些高效的定义提供高效的不可变映射。我想到的最好的方法是使用它,它允许O(logn)访问可排序键,并在插入和删除时创建O(logn)节点。经过k次修改后,基于b-树的映射的两个持有者之间共享的节点数约为(n-k log n),这在实践中对于不经常更新的映射是非常好的。

不可变列表意味着您无法在创建后修改列表项。所以你滥用了符号。您要做的是:拥有一个可变列表并返回其不可变视图

Google Guava可以为您返回不变的视图

ImmutableList<T> view = ImmutableList.copyOf(mutableList);
ImmutableList视图=ImmutableList.copyOf(可变列表);

如果希望尽可能减少副本,可以在请求新视图之前对
可变表列表进行多次更新

如何将两个(不同的)元素添加到双链接列表中的工作中?当从A创建C时,你破坏了B.@Paŭlo Ebermann-这确实是个问题,即使您不销毁B,而是将A->B更改为C。我认为这可以通过不检查sourceListId和x之间的元素索引来解决。无论出于何种目的,即使内部实现本身是可变的,它对外部世界都是不可变的,是的。但在某些地方,您必须开始改变状态。这仍然会复制整个列表(例如,需要O(大小)操作)。如果不想复制,请使用Collections.unmodifiableList。