Java中一些处理分隔的方法,例如subString—String;subList—List;subMap—Map;subSet—Set;
这里学习并整理了一下关于subList的使用
一、subList仅返回一个list的部分视图
List subList(int fromIndex, int toIndex);
实例:
public static void main(String[] args) {
List<String> list1 = new ArrayList<>();
list1.add("aaa");
list1.add("bbb");
list1.add("ccc");
//通过构造函数新建一个包含list1的列表 list2
List<String> list2 = new ArrayList<>(list1);
//通过subList生成一个与list1一样的列表 list3
List<String> list3 = list1.subList(0, list1.size());
System.out.println("list1 == list3:" + list1.equals(list3));
//修改list3
list3.add("ddd");
System.out.println("list1 == list2:" + list1.equals(list2));
System.out.println("修改list3后 list1 == list3:" + list1.equals(list3));
System.out.println("---------------list1---------------");
list1.forEach(n -> System.out.println(n));
System.out.println("---------------list2---------------");
list2.forEach(n -> System.out.println(n));
System.out.println("---------------list3---------------");
list3.forEach(n -> System.out.println(n));
}
实例中list1是原list;list2是通过构造函数生成一个与list1一样的list;list3是通过subList分隔出与list1一样的list。
执行结果分析:
list3未增加新元素时list1 == list3执行结果为true
list3增加新元素后list1 == list3执行结果仍为true
由此可以判断出subList方法如get、set、add、remove等都是在原列表上面做操作,subList返回的只是原列表的一个视图,它所有的操作最终都会作用在原列表上。它并没有像subString一样生成一个新的对象。
subList源码
public List<E> subList(int fromIndex, int toIndex) {
subListRangeCheck(fromIndex, toIndex, size);
return new SubList(this, 0, fromIndex, toIndex);
}
SubList方法源码片段
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;
}
...
}
需要注意:
this.parent = parent;而parent就是在前面传递过来的list,也就是说this.parent就是原始list的引用。
this.modCount = ArrayList.this.modCount;
再看SubLsit内部类中其它的get、set、add、remove等list常用的方法
public E get(int index) {
rangeCheck(index);
checkForComodification();
return ArrayList.this.elementData(offset + index);
}
返回的是原列表offset + index位置的元素
二、subList生成子列表后,不要去操作原列表
public static void main(String[] args) {
List<String> list1 = new ArrayList<>();
list1.add("aaa");
list1.add("bbb");
List<String> list2 = list1.subList(0, list1.size());
System.out.println("---------------list1---------------");
list1.forEach(n -> System.out.println(n));
list1.add("ccc");
System.out.println("---------------list2---------------");
list2.forEach(n -> System.out.println(n));
}
执行结果分析:
---------------list1---------------
aaa
bbb
---------------list2---------------
Exception in thread "main" java.util.ConcurrentModificationException
at java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1239)
at java.util.ArrayList$SubList.listIterator(ArrayList.java:1099)
at java.util.AbstractList.listIterator(AbstractList.java:299)
at java.util.ArrayList$SubList.iterator(ArrayList.java:1095)
at java.lang.Iterable.forEach(Iterable.java:74)
遍历list1正常、遍历list2就抛出ConcurrentModificationException异常、从异常日志中可以看到会执行checkForComodification方法
private void checkForComodification() {
if (ArrayList.this.modCount != this.modCount)
throw new ConcurrentModificationException();
}
该方法表明当原列表的modCount与this.modCount不相等时就会抛出ConcurrentModificationException。同时我们在subList过程中modCount“继承”了原列表modCount,只有在修改子列表时才会修改该值(先表现在原列表后作用于子列表)。而该实例我们是操作原列表,原列表的modCount不会反应在子列表的modCount上,操作原列表子列表将会失效,变得不可用,所以才会抛出该异常,所以subList生成子列表后,不要去操作原列表,否则会造成子列表的不稳定而产生异常。
(关于ConcurrentModificationException异常,可以详细了解下fail-fast机制)
三、通过subList处理局部列表
如果想删除一个list的某个区段,比如删除list的第30-50个元素?
实例:
list1.subList(30, 50).clear();//list.subList(from, to).clear();因为子列表的操作都会反映在原列表上
文章仅作为个人学习整理