Home > Java > javaTutorial > Java Improvement Configuration (37)—Java Collection Details (3): Defects of subList

Java Improvement Configuration (37)—Java Collection Details (3): Defects of subList

黄舟
Release: 2017-02-11 10:37:00
Original
1370 people have browsed it

We often use the subString method to split String objects. At the same time, we can also use subList, subMap, and subSet to split List, Map, and Set. processing, but this segmentation has certain flaws.

1. subList returns just a view

First, let’s look at the following example:

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));
    }
Copy after login

This example is very simple , it is nothing more than regenerating a list the same as list1 through the constructor and subList, then modifying list3, and finally comparing list1 == list2?, list1 == list3?. According to our conventional thinking, it should be like this: because list3 adds a new element through add, then it must not be equal to list1, and list2 is constructed through list1, so it should be equal, so the result should be:

list1 == list2:true
list1 == list3: false
Copy after login

First of all, regardless of whether the result is correct or not, let’s look at the source code of subList:

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

, whether toIndex is legal. If it is legal, a subList object will be returned directly. Note that a parameter this is passed when generating the new object. This parameter is very important because it represents the original 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;
        }
    }
Copy after login


The SubLsit is the internal class of ArrayList. It is the same as ArrayList and is inherited. AbstractList and implements the RandomAccess interface. It also provides commonly used list methods such as get, set, add, and remove. But its constructor is a bit special. There are two places to pay attention to in the constructor:

1. this.parent = parent; and parent is the list passed in front, In other words, this.parent is a reference to the original list.

2. this.offset = offset + fromIndex; this.parentOffset = fromIndex;. At the same time, it even passes modCount (fail-fast mechanism) in the constructor.

Let’s look at the get method again. In the get method, return ArrayList.this.elementData(offset + index); this code can clearly show that what get returns is the offset + index position of the original list. Elements. The same principle applies to the add method:

parent.add(parentOffset + index, e);
this.modCount = parent.modCount;
Copy after login

The remove method

E result = parent.remove(parentOffset + index);
this.modCount = parent.modCount;
Copy after login

It is true that we are here We can judge that the SubList returned by subList is also a subclass of AbstractList. At the same time, its methods such as get, set, add, remove, etc. all operate on the original list. It does not generate a new object like subString. So subList returns only a view of the original list, and all its operations will eventually be applied to the original list.

          Then from the analysis here we can conclude that the above result should be exactly the opposite of our above answer:

list1 == list2:false
list1 == list3:true
Copy after login

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

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

从上面我们知道subList生成的子列表只是原列表的一个视图而已,如果我们操作子列表它产生的作用都会在原列表上面表现,但是如果我们操作原列表会产生什么情况呢?

public static void main(String[] args) {
        List<Integer> list1 = new ArrayList<Integer>();
        list1.add(1);
        list1.add(2);
        
        //通过subList生成一个与list1一样的列表 list3
        List<Integer> list3 = list1.subList(0, list1.size());
        //修改list3
        list1.add(3);
        
        System.out.println("list1&#39;size:" + list1.size());
        System.out.println("list3&#39;size:" + list3.size());
    }
Copy after login


该实例如果不产生意外,那么他们两个list的大小都应该都是3,但是偏偏事与愿违,事实上我们得到的结果是这样的:

list1&#39;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)
Copy after login


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

public int size() {
            checkForComodification();
            return this.size;
        }
Copy after login


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

private void checkForComodification() {
            if (ArrayList.this.modCount != this.modCount)
                throw new ConcurrentModificationException();
        }
Copy after login

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

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

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

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

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

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

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

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

list1.subList(100, 200).clear();
Copy after login

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

以上就是Java提高配(三七)—–Java集合细节(三):subList的缺陷的内容,更多相关内容请关注PHP中文网(m.sbmmt.com)!


source:php.cn
Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn
Popular Tutorials
More>
Latest Downloads
More>
Web Effects
Website Source Code
Website Materials
Front End Template