文档章节

mark一个subList的坑

Hosee
 Hosee
发布于 2017/07/25 10:48
字数 1283
阅读 348
收藏 2

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

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

首先我们先看如下实例:

public static void main(String[] args) {
        List<Integer> list1 = new ArrayList<Integer>();
        list1.add(1);
        list1.add(2);
        
        //通过构造函数新建一个包含list1的列表 list2
        List<Integer> list2 = new ArrayList<Integer>(list1);
        
        //通过subList生成一个与list1一样的列表 list3
        List<Integer> 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的源码:

public List<E> subList(int fromIndex, int toIndex) {
        subListRangeCheck(fromIndex, toIndex, size);
        return new SubList(this, 0, fromIndex, toIndex);
    }

 subListRangeCheck方式是判断fromIndex、toIndex是否合法,如果合法就直接返回一个subList对象,注意在产生该new该对象的时候传递了一个参数 this ,该参数非常重要,因为他代表着原始list。

/**
     * 继承AbstractList类,实现RandomAccess接口
     */
    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;
        }

        //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常用的方法。但是它的构造函数有点特殊,在该构造函数中有两个地方需要注意:

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

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

        我们再看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细节:subList返回的只是原列表的一个视图,它所有的操作最终都会作用在原列表上

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

因为subList是原List的视图,所以在对subList操作的时候,会影响原List,而且也有可能导致ConcurrentModificationException异常,关于ConcurrentModificationException请查看ConcurrentModificationException的原因以及解决措施

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

//通过subList生成一个与list1一样的列表 list3
List<Integer> list3 = list1.subList(0, list1.size());
        
//对list1设置为只读状态
list1 = Collections.unmodifiableList(list1);

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

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

for(int i = 0 ; i < list1.size() ; i++){
   if(i >= 100 && i <= 200){
       list1.remove(i);
       /*
        * 当然这段代码存在问题,list remove之后后面的元素会填充上来,
         * 所以需要对i进行简单的处理,当然这个不是这里讨论的问题。
         */
   }
}

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

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

本文转载自:http://blog.csdn.net/chenssy/article/details/44102915

Hosee
粉丝 621
博文 135
码字总数 209956
作品 0
杭州
程序员
私信 提问
加载中

评论(0)

java中List.subList()方法的使用

sublist返回的东西,官方解释:Returns a view of the portion of this list between the specified fromIndex, inclusive, and toIndex bulabula。一个View,视图,想到了数据库视图。 做个......

liujiest
2016/08/15
3.9K
0
一文了解那些和Spring Bean有关的那些注解!

△Hollis, 一个对Coding有着独特追求的人△ 作者 l Hollis 来源 l Hollis(ID:hollischuang) 集合是Java开发日常开发中经常会使用到的。在之前的一些文章中,我们介绍过一些关于使用集合类...

HollisChuang
2019/07/01
35
0
技术点:注意Java的java.util.List.subList的坑

java中有一个返回子列表的方法: 返回一个fromIndex为起点,toIndex为终点(不包含终点)的子列表。从上实现代码中可以看到,先检查一下单签的fromIndex和toIndex是否合法,如果不合法,那么...

程序猿拿Q
2018/12/20
56
0
java ArrayList中的subList方法

本文是本人的学习笔记,把自己的理解总结记录下来。因本人水平有限,如果您在阅读中发现错误,还望谅解,并且希望能够告知本人改正,不胜感激! ArrayList中的subList()方法 subList方法传入的...

等到烟火清凉_
2018/07/02
233
0
【bug】 -- List.subList()未序列化导致Redis报错

别人遇到的bug,收集起来以防自己同样犯错。 bug产生的场景: 项目中对文章的详情内容通过Redis做了缓存,详情中以List形式关联了一些其他内容。在Redis中存储数据也将会是从业务中获取的Lis...

杜.
03/31
0
0

没有更多内容

加载失败,请刷新页面

加载更多

项目讲解4

分组取topN的方法: 数据的预处理为使用mapreduce 每日新访客: 回头/单次访客统计: 漏斗模型: 使用python产生数据 1.统计每个步骤的总访问人数 create table tmp_page_views like ods_web...

七宝1
34分钟前
33
0
笔记:pyecharts可视化

# 加载数据import pandas as pddf = pd.read_excel(r"D:\我的文档\数据源\采购商品出库综合查询 - Python.xlsx",header = 2)df # 计算出结果df1 = df.groupby(["部门"])["总销售额"......

tengyulong
46分钟前
56
0
如何在div中垂直对齐图像 - How to vertically align an image inside a div

问题: How can you align an image inside of a containing div ? 如何在包含div内部对齐图像? Example 例 In my example, I need to vertically center the <img> in the <div> with cla......

javail
56分钟前
59
0
用原生JS模拟Vue的State插件的写法

<html><body> <input type="text" id="myinput"></body><script> var myinput = document.querySelector('#myinput'); function Vue() { } Vue.Use = function......

lilugirl
58分钟前
57
0
space_sniffer 清理 磁盘

http://www.uderzo.it/main_products/space_sniffer/ 下载直接运行 , 可以看到占用空间比较大的一些文件夹, 然后根据情况删除 anaconda和pr的缓存占了很大一部分 金山居然也这么大?...

阿豪boy
今天
85
0

没有更多内容

加载失败,请刷新页面

加载更多

返回顶部
顶部