咱们常用subString方法来对String对象进行分割处理,同时咱们也可使用subList、subMap、subSet来对List、Map、Set进行分割处理,可是这个分割存在某些瑕疵。java

1、subList返回仅仅只是一个视图

首先咱们先看以下实例:dom

public static void main(String[] args) {

List list1 = new ArrayList();

list1.add(1);

list1.add(2);

//经过构造函数新建一个包含list1的列表 list2

List list2 = new ArrayList(list1);

//经过subList生成一个与list1同样的列表 list3

List list3 = list1.subList(0, list1.size());

//修改list3

list3.add(3);

System.out.println("list1 == list2:" + list1.equals(list2));

System.out.println("list1 == list3:" + list1.equals(list3));

}

这个例子很是简单,无非就是经过构造函数、subList从新生成一个与list1同样的list,而后修改list3,最后比较list1 == list2?、list1 == list3?。按照咱们常规的思路应该是这样的:由于list3经过add新增了一个元素,那么它确定与list1不等,而list2是经过list1构造出来的,因此应该相等,因此结果应该是(错):函数

list1 == list2:true

list1 == list3: false

首先咱们先不论结果的正确与否,咱们先看subList的源码:this

public List subList(int fromIndex, int toIndex) {

subListRangeCheck(fromIndex, toIndex, size);

return new SubList(this, 0, fromIndex, toIndex);

}

subListRangeCheck方式是判断fromIndex、toIndex是否合法,若是合法就直接返回一个subList对象,注意在产生该new该对象的时候传递了一个参数 this ,该参数很是重要,由于他表明着原始list。spa

/**

* 继承AbstractList类,实现RandomAccess接口

*/

private class SubList extends AbstractList implements RandomAccess {

private final AbstractList parent;    //列表

private final int parentOffset;

private final int offset;

int size;

//构造函数

SubList(AbstractList 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;

}

//set方法

public E set(int index, E e) {

rangeCheck(index);

checkForComodification();

E oldValue = ArrayList.this.elementData(offset + index);

ArrayList.this.elementData[offset + index] = e;

return oldValue;

}

//get方法

public E get(int index) {

rangeCheck(index);

checkForComodification();

return ArrayList.this.elementData(offset + index);

}

//add方法

public void add(int index, E e) {

rangeCheckForAdd(index);

checkForComodification();

parent.add(parentOffset + index, e);

this.modCount = parent.modCount;

this.size++;

}

//remove方法

public E remove(int index) {

rangeCheck(index);

checkForComodification();

E result = parent.remove(parentOffset + index);

this.modCount = parent.modCount;

this.size--;

return result;

}

}

该SubLsit是ArrayList的内部类,它与ArrayList同样,都是继承AbstractList和实现RandomAccess接口。同时也提供了get、set、add、remove等list经常使用的方法。可是它的构造函数有点特殊,在该构造函数中有两个地方须要注意:code

一、this.parent = parent;而parent就是在前面传递过来的list,也就是说this.parent就是原始list的引用。对象

二、this.offset = offset + fromIndex;this.parentOffset = fromIndex;。同时在构造函数中它甚至将modCount(fail-fast机制)传递过来了。blog

咱们再看get方法,在get方法中return ArrayList.this.elementData(offset + index);这段代码能够清晰代表get所返回就是原列表offset + index位置的元素。一样的道理还有add方法里面的:继承

parent.add(parentOffset + index, e);

this.modCount = parent.modCount;

remove方法里面的接口

E result = parent.remove(parentOffset + index);

this.modCount = parent.modCount;

诚然,到了这里咱们能够判断subList返回的SubList一样也是AbstractList的子类,同时它的方法如get、set、add、remove等都是在原列表上面作操做,它并无像subString同样生成一个新的对象。因此subList返回的只是原列表的一个视图,它全部的操做最终都会做用在原列表上。

那么从这里的分析咱们能够得出上面的结果应该偏偏与咱们上面的答案相反:

list1 == list2:false

list1 == list3:true

Java细节(3.1):subList返回的只是原列表的一个视图,它全部的操做最终都会做用在原列表上

2、subList生成子列表后,不要试图去操做原列表

从上面咱们知道subList生成的子列表只是原列表的一个视图而已,若是咱们操做子列表它产生的做用都会在原列表上面表现,可是若是咱们操做原列表会产生什么状况呢?

public static void main(String[] args) {

List list1 = new ArrayList();

list1.add(1);

list1.add(2);

//经过subList生成一个与list1同样的列表 list3

List list3 = list1.subList(0, list1.size());

//修改list3

list1.add(3);

System.out.println("list1'size:" + list1.size());

System.out.println("list3'size:" + list3.size());

}

该实例若是不产生意外,那么他们两个list的大小都应该都是3,可是恰恰事与愿违,事实上咱们获得的结果是这样的:

list1'size:3

Exception in thread "main" java.util.ConcurrentModificationException

at java.util.ArrayList$SubList.checkForComodification(Unknown Source)

at java.util.ArrayList$SubList.size(Unknown Source)

at com.chenssy.test.arrayList.SubListTest.main(SubListTest.java:17)

list1正常输出,可是list3就抛出ConcurrentModificationException异常,看过我另外一篇博客的同仁确定对这个异常很是,fail-fast?不错就是fail-fast机制,在fail-fast机制中,LZ花了不少力气来说述这个异常,因此这里LZ就不对这个异常多讲了(更多请点这里:Java提升篇(三四)—–fail-fast机制)。咱们再看size方法:

public int size() {

checkForComodification();

return this.size;

}

size方法首先会经过checkForComodification验证,而后再返回this.size。

private void checkForComodification() {

if (ArrayList.this.modCount != this.modCount)

throw new ConcurrentModificationException();

}

该方法代表当原列表的modCount与this.modCount不相等时就会抛出ConcurrentModificationException。同时咱们知道modCount 在new的过程当中 “继承”了原列表modCount,只有在修改该列表(子列表)时才会修改该值(先表如今原列表后做用于子列表)。而在该实例中咱们是操做原列表,原列表的modCount固然不会反应在子列表的modCount上啦,因此才会抛出该异常。

对于子列表视图,它是动态生成的,生成以后就不要操做原列表了,不然必然都致使视图的不稳定而抛出异常。最好的办法就是将原列表设置为只读状态,要操做就操做子列表:

//经过subList生成一个与list1同样的列表 list3

List list3 = list1.subList(0, list1.size());

//对list1设置为只读状态

list1 = Collections.unmodifiableList(list1);

Java细节(3.2):生成子列表后,不要试图去操做原列表,不然会形成子列表的不稳定而产生异常

3、推荐使用subList处理局部列表

在开发过程当中咱们必定会遇到这样一个问题:获取一堆数据后,须要删除某段数据。例如,有一个列表存在1000条记录,咱们须要删除100-200位置处的数据,可能咱们会这样处理:

for(int i = 0 ; i

if(i >= 100 && i <= 200){

list1.remove(i);

/*

* 固然这段代码存在问题,list remove以后后面的元素会填充上来,

* 因此须要对i进行简单的处理,固然这个不是这里讨论的问题。

*/

}

}

这个应该是咱们大部分人的处理方式吧,其实还有更好的方法,利用subList。在前面LZ已经讲过,子列表的操做都会反映在原列表上。因此下面一行代码所有搞定:

list1.subList(100, 200).clear();

简单而不失华丽!!!!!

参考资料:编写高质量代码:改善Java程序的151个建议

sublist用法Java_Java集合细节(三):subList的缺陷相关推荐

  1. Java集合篇:集合细节:为集合指定初始容量、asList的缺陷、subList的缺陷

    一.为集合指定初始容量: 集合是我们在Java编程中使用非常广泛的,它就像大海,海纳百川,像万能容器,盛装万物,而且这个大海,万能容器还可以无限变大(如果条件允许).当这个海.容器的量变得非常大的时候 ...

  2. Java SubList 类 Java subList方法 Java ArrayList$SubList 方法特点 SubList 用法

    Java SubList 类 Java subList方法 Java ArrayList$SubList 方法特点 SubList 用法 一.概述 在java集合中,常用ArrayList类中,若需要 ...

  3. Map集合及遍历 debug用法 以及集合的斗地主应用练习 D190326

    Map集合及遍历 debug用法 以及集合的斗地主应用练习 第一个独立应用:斗地主,分步骤一点点把项目拆分每一步去完成. 01_第一章:回顾_三种集合的存储方式: 02_第一章:Map集合_常用方法: ...

  4. java comparable接口_Java面试题之Java集合篇三

    Java面试题之Java集合篇三1.HashMap和HashTable有何不同? (1)HashMap允许key和value为null,而HashTable不允许. (2)HashTable是同步的, ...

  5. Java 数组转成集合List三种方法和(数组、集合List、Set相互转换)

    Java 数组转成集合List 三种方法 package com.list;import java.util.ArrayList; import java.util.Arrays; import ja ...

  6. 【零基础学Java】—List集合(三十九)

    [零基础学Java]-List集合(三十九) java.util.list接口 extends Collection接口 list接口的特点: 1.有序的集合,存储元素和取出元素的顺序是一致的(存储1 ...

  7. 【零基础学Java】—this关键字的三种用法+Java继承的三个特点(二十一)

    [零基础学Java]-this关键字的三种用法+Java继承的三个特点(二十一) 一.this关键字的三种用法 在本类的成员方法中,访问本类的成员变量 在本类的成员方法中,访问本类的另一个成员方法 在 ...

  8. Java中遍历Set集合的三种方法

    Map集合:链接: Map集合的五种遍历方式及Treemap方法 Set集合:链接: Java中遍历Set集合的三种方法 TreeSet集合:链接: Java深入了解TreeSet,和迭代器遍历方法 ...

  9. Java中List集合的三种遍历方式(全网最详)

    Map集合:链接: Map集合的五种遍历方式及Treemap方法 Set集合:链接: Java中遍历Set集合的三种方法 TreeSet集合:链接: Java深入了解TreeSet,和迭代器遍历方法 ...

最新文章

  1. mysql表自关联_数据库之自关联
  2. ALEIDoc EDI(9)--others
  3. ●洛谷P3687 [ZJOI2017]仙人掌
  4. 基于灰度世界、完美反射、动态阈值等图像自动白平衡算法的原理、实现及效果...
  5. 【链表】牛客网:链表内指定区间反转
  6. Java中VO/DTO/DO/PO/POJO/BO/DAO概念及其区别
  7. UnityWebPlayer缓存清理工具
  8. 360奇云的架构演进之路
  9. 0基础学习3dmax游戏建模有这4个技巧
  10. 001_KNN与线性分类器
  11. 计算机主机报警 声,电脑开机报警4声长鸣解决教程 | 专业网吧维护
  12. PS2游戏Android模拟器,安卓PS2游戏大全|安卓PS2模拟器游戏_小鸡模拟器
  13. 一个对话让你明白架构师是做什么的?
  14. 快车解密php,PHP迅雷、快车、旋风下载专用链转换代码
  15. 本田智能驾驶功能介绍-TSR/CTM/MVC360
  16. Google登录授权详细过程
  17. cdrx8如何批量导出jpg_coreldraw2019中把所有页面批量导出jpg图的解决方法
  18. [激光原理与应用-39]:《光电检测技术-6》- 光干涉的原理与基础
  19. 【JM】电脑检测不到U盘
  20. 安森美为何「上热搜」?汽车业务增量背后的隐忧已经出现

热门文章

  1. i5 12600kf和i7 10700k选哪个
  2. uniapp-微信公众号静默授权
  3. 八皇后问题详解(四种解法)
  4. virtualenv看这一篇就够了
  5. 台湾生育率全球最低 民众想生却不敢生
  6. NTLM Relay利用
  7. 【How to read a paper】如何读论文
  8. BAT脚本--一键打开多个应用/微信多开
  9. 阿里云 OSS browser 官方提供的图形化管理工具
  10. win10一按右键就闪屏_Win10屏幕闪烁是什么原因 Win10屏幕闪烁现象的原因及解决办法...