`java.util.List.subList()`究竟应该如何工作?

时间:2013-03-10 15:08:20

标签: java list

List通过调用List.subList(int fromIndex, int toIndex)获得的子列表的方法进行结构修改时,索引和列表元素之间的索引和关系会发生什么?

对事实上的操作不感兴趣,如Oracle JVM所述;我对接口行为规范很感兴趣,因此可以可靠地实现一个自己的列表类(这里的可靠性是指能够通过实现java.util.List接口来交换一个Javas列表类与一个自己的列表类。)< / p>

Oracle的documentation of List似乎没有说明上述情况。请注意,这与尝试通过除子列表之外的任何其他方式修改列表无关,这是关于通过子列表仅修改,正如文档所支持的那样。

实施例

说我有一个包含6个元素A, B, C, D, E, F的列表。在列表上调用subList(1, 4)会产生一个包含元素 B,C,D 的子列表。然后我在此子列表上调用remove(D)。我想知道删除D后子列表中包含哪些元素?一些替代方案:

  1. B,C,E (子列表保留原始索引范围)?
  2. B,C (实际上不再是subList(1, 4))?
  3. 我的猜测是,由于子列表是用subList(1, 4)指定的,因此列表本身的“窗口”必须是相同的“大小”,因此E会滑入视图以便说,因为视图的结束索引仍然是4,而D现在已经超过E。第二种选择似乎对我来说不是很明智,但不过是另一种选择。

4 个答案:

答案 0 :(得分:4)

以下引用的规范可以回答这个问题,已在documentation of java.util.AbstractList class中找到(有点令人惊讶的是,java.util.List的文档中):

  

此实现返回一个子类AbstractList的列表。子类在私有字段中存储子列表在支持列表中的偏移量,子列表的大小(可以在其生命周期内更改),以及支持列表的预期modCount值。 / p>

由于视图的大小可以改变,因此索引也可以改变,这意味着在问题中概述的示例场景中,元素E“滑入视图”。因此第二种选择是正确的,因为删除D后子列表的大小减少了1,而该点的子列表包含2个元素B, C

可能是Java设计人员的一个深思熟虑的选择,要在AbstractList页面中指定上述内容,而不是List的文档页面 - 以便后者保留了模糊性。但是,我希望实现List的两个不同列表类可以在这个特定的细节级别上轻松交换和兼容。此外,并非每个List实现都会扩展AbstractList - 这是一种便利,而不是一项要求。

以下是将引用的规范(以及其他一些详细信息)从AbstractList页面移至List文档页面。

答案 1 :(得分:2)

由于子列表是List,删除元素必须减小其大小。此外,还必须从支持列表中删除该元素。 (Javadoc写道:

  

返回的列表由此列表支持

  

返回的列表支持此列表支持的所有可选列表操作。

答案 2 :(得分:1)

List接口是合约,不同的实现可以自由地做他们喜欢的合同没有指定的任何内容。

归结为您是否对“ x y 之间的此列表部分视图”这一短语有“静态”或“动态”解释 - 是subList在创建子列表时(静态)或当时在 x y 之间的支持列表部分的remove视图子列表上的每个操作都是执行的(动态的)?根据合同,这两个读数都是可以接受的,并且不同的实现都可以自由地进行。

特别是// lst contains B, C, D lst.remove(D); // lst contains B, C, E 的合同中没有任何内容表明删除后的列表必须比以前更小,这并不像它的情况那么奇怪在存在多线程或事件监听器时发出声音。像

这样的案例
subList

可能是动态{{1}}的结果,或者它可能是另一个将E添加到列表中的线程,或者它可能是一个列表实现,它触发元素删除事件并且事件侦听器已添加E.无论哪种方式没有什么打破了List合同。

答案 3 :(得分:0)

不出所料,在List界面的Javadoc中找到了答案:

  

返回的列表由此列表支持,因此返回列表中的非结构更改将反映在此列表中,反之亦然。返回的列表支持此列表支持的所有可选列表操作。 ...如果支持列表(即此列表)在结构上以除返回列表之外的任何方式进行修改,则此方法返回的列表的语义将变为未定义。

换句话说,可以通过子列表修改列表。