gpt4 book ai didi

java - subList() 与其他类似的 Collections 方法有不同的行为,一旦 subList 被改变

转载 作者:行者123 更新时间:2023-11-30 07:14:39 27 4
gpt4 key购买 nike

谁能解释为什么 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+", "); }
}}

提前致谢!

最佳答案

很明显,行为符合记录。但我认为您的主要问题是为什么 ArrayListTreeSet 的行为不同。好吧,这与数据在两个集合中的内部存储方式有关。

ArrayList 在内部使用一个数组来存储数据,随着 ArrayList 的大小动态增加,它会重新调整大小。现在,当您创建给定列表的 subList 时,具有指定索引的原始列表与 subList 相关联。因此,在原始列表中进行的任何结构更改(破坏原始数组的索引)都会使作为子列表的一部分存储的索引变得毫无意义。这就是为什么在 ArrayList#subList 方法中不允许进行任何结构更改的原因。subList 方法返回 ArrayList 类中名为 SubListinner 类实例,如下所示:

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/

27 4 0
Copyright 2021 - 2024 cfsdn All Rights Reserved 蜀ICP备2022000587号
广告合作:1813099741@qq.com 6ren.com