subList
不会创建与指定范围内的原始列表具有相同元素的新列表。相反,它创建了一个“视图”(docs https://docs.oracle.com/javase/8/docs/api/java/util/List.html#subList-int-int-):
返回此列表的一部分的视图 [...]。返回的列表受此列表支持,因此返回列表中的非结构性更改会反映在此列表中,反之亦然。
另请注意:
如果支持列表(即此列表)以除返回列表之外的任何方式进行结构修改,则此方法返回的列表的语义将变得不确定。
这正是你正在做的merge
。您正在创建子列表left
。然后修改left
结构上与add
。到目前为止,一切都很好。但后来你创建了另一个子列表right
并修改it以及。这使得“语义left
变得未定义”。这会导致下一次调用get
抛出异常。
最小可重现示例:
ArrayList<String> list = new ArrayList<>(List.of("1", "2", "3", "4"));
List<String> left = list.subList(0, 2);
List<String> right = list.subList(2, 4);
right.add("5");
left.get(0);
在这方面,子列表有点像迭代器(你只能remove通过迭代器,如果通过原始列表删除,可能会抛出 CME https://stackoverflow.com/questions/18448671/how-to-avoid-concurrentmodificationexception-while-removing-elements-from-arr).
解决此问题的一种简单方法是创建子列表的副本,以便它们不再是“视图”,而实际上是独立的列表:
List<Product> left = new ArrayList<>(list.subList(p,q));
List<Product> right = new ArrayList<>(list.subList(q+1,r));