问:下面程序片段的输出结果是什么?为什么?
List<String> list = new ArrayList<>();
list.add("android");
list.add("java");
List<String> list1 = new ArrayList<>(list);
List<String> list2 = list.subList(0, list.size());
list2.add("unix C");
System.out.println(list.equals(list1));
System.out.println(list.equals(list2));
答:输出为 false、true。
因为通过构造方法创建的 list1 实质上新的列表,其内部实现是通过 copyof 动作生成的,生成的列表与原列表没有任何关系(虽然是浅拷贝,但是由于元素是 String 类型,所以可以理解成是深拷贝),所以当我们对 list 添加了元素后再与 list1 进行对比会发现没有任何关系,而依据集合的比较都是比较元素值,其实是因为 list2 添加了新元素,导致原来的列表 list 的元素改变了,所以 list 与 list1 自然不相等。对于 list2 来说 subList 产生的集合列表只是一个视图,所有的修改操作都会作用于原集合列表上,所以修改 list2 就相当于修改了 list 集合,可以去看 AbstractList 和 ArrayList 中 subList 的实现,虽然生成了新的 ArrayList public 内部类 SubList 实例,但是该实例中每个操作都传递操作了外部类 ArrayList 的对应操作,所以返回 true。
问:如何优雅的删除列表区间,譬如有一个 List 里面有 100 个元素,现在想删除 30 到 50 之间的元素,怎么写代码最优雅?
答:常见的删除操作都是通过遍历删除区间的,譬如 index 索引从 30 开始到 50 结束,总之都得两三句代码,而问题提到了优雅就是说美观简洁咯,下面的方式即可:
list.subList(30, 50).clear();
所以说遇到类似场景还是使用这种优雅方式吧,一行代码简洁大方。
问:下面程序片段运行结果是什么?为什么?
List<String> list = new ArrayList<>();
list.add("android");
list.add("java");
List<String> sublist = list.subList(0, list.size());
list.add("unix C");
System.out.println("list size=" + list.size());
System.out.println("sublist size=" + sublist.size());
答:这个程序运行会在 sublist.size() 处报错 ConcurrentModificationException。因为 subList 取出的列表只是原列表的一个视图,原数据集合修改了后 subList 取出的子列表不会重新生成新的列表,而 SubList 中每个方法都有修改计数检测,后面再对子列表操作时就检测到计数器与预期不相等了,所以抛出异常,切记通过 subList 生成子列表后不要再操作原列表。
问:下面程序有问题吗?为什么?
ArrayList<String> list = new ArrayList<>();
list.add("android");
ArrayList<String> subList = (ArrayList<String>) list.subList(0, 1);
subList.add("unix");
答:这个程序运行会抛出 java.lang.ClassCastException: java.util.ArrayList$SubList cannot be cast to java.util.ArrayList
异常。
因为 subList 返回的 List 是 ArrayList 内部类 SubList(继承自 AbstractList),看起来都是 List 的实现,但是不是同一个子类,无法强转为 ArrayList,修改方案为 subList 的返回接收声明为 List<String> 类型即可。