谁能解释为什么 subList() 不像 subSet() 方法那样运行,并抛出 ConcurrentModificationException,而 子集 不是。这两种方法都创建了一个 Backed Collection,所以可能 subList() 方法 设计者创建这个方法依赖于不可修改的原始列表,但如果所有 Backed Collections 具有相同的行为(如 subSet())?
//代码
public class ConcurrentModificationException {
public static void main(String[] args) {
String[] array = {"Java","Python","Pearl","Ada","Javascript","Go","Clojure"};
subListEx(array);
subSetEx(array);
}
private static void subListEx(String[] array) {
List<String> l = new ArrayList<String>(Arrays.asList(array));
List<String> l2 = l.subList(2, 4);
System.out.println(l.getClass().getName());
// l.add("Ruby"); // ConcurrentModificationException
// l.remove(2); // ConcurrentModificationException
l2.remove("Ada"); // OK
for (String s:l) { System.out.print(s+", "); }
System.out.println();
for (String s:l2) { System.out.print(s+", "); }
}
private static void subSetEx(String[] array) {
SortedSet<String> s1 = new TreeSet<String>(Arrays.asList(array));
SortedSet<String> s2 = s1.subSet("Java", "Python");
s1.remove("Ada");
for (String s:s1) { System.out.print(s+", "); }
System.out.println();
for (String s:s2) { System.out.print(s+", "); }
}}
提前致谢!
最佳答案
很明显,行为符合记录。但我认为您的主要问题是为什么 ArrayList
和 TreeSet
的行为不同。好吧,这与数据在两个集合中的内部存储方式有关。
ArrayList
在内部使用一个数组来存储数据,随着 ArrayList
的大小动态增加,它会重新调整大小。现在,当您创建给定列表的 subList
时,具有指定索引的原始列表与 subList
相关联。因此,在原始列表中进行的任何结构更改(破坏原始数组的索引)都会使作为子列表的一部分存储的索引变得毫无意义。这就是为什么在 ArrayList#subList
方法中不允许进行任何结构更改的原因。
subList 方法返回 ArrayList
类中名为 SubList
的 inner
类实例,如下所示:
private class SubList extends AbstractList<E> implements RandomAccess {
private final AbstractList<E> parent;
private final int parentOffset;
private final int offset;
int size;
SubList(AbstractList<E> parent,
int offset, int fromIndex, int toIndex) {
this.parent = parent;
this.parentOffset = fromIndex;
this.offset = offset + fromIndex;
this.size = toIndex - fromIndex;
this.modCount = ArrayList.this.modCount;
}
如您所见,SubList
包含对原始列表的引用。 parentOffset
只是您正在创建的 subList
的起始索引。现在修改原始 list
可能会更改原始列表中 fromIndex
处的值,但不会更改 SubList
中的值。在这种情况下,SubList
类中的 parentOffset
和原始列表中的 fromIndex
将指向不同的数组元素。也有可能在某个时候原始数组变得足够短,使 SubList
中存储的索引无效并使其 OutOfRange
。这当然是不可取的,并且在对原始列表进行此类结构更改时,返回的子列表的语义被认为是未定义。
另一方面,TreeSet
在内部将其数据存储在 TreeMap
中。现在,由于 Map
中没有这样的索引概念,因此不存在索引中断的问题。 Map
只不过是 key-value 对的映射。创建一个 SubSet 涉及创建一个由原始 Map
支持的 SubMap。修改原始 Set
只需要使相应的 key-value 映射无效,从而将更改传播到为 创建的
.subMap
子集
关于java - subList() 与其他类似的 Collections 方法有不同的行为,一旦 subList 被改变,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/18367173/