java ArrayList中的subList方法

1 篇文章 0 订阅

  • 本文是笔者的学习笔记,把自己的理解总结记录下来。因笔者水平有限,如果您在阅读中发现错误,还望谅解,并且希望你告知笔者,不胜感激!

  • ArrayList中的subList()方法

    ArrayList中的subList(int fromIndex, int toIndex)方法,是返回当前ArrayList中从索引为fromIndex的位置索引位置为toIndex-1的位置的列表 视图。这里需要注意几点:

    1. subList方法传入的参数是左闭右开的,也就是[formIndex,toIndex)的形式。所以如果传来两个相同的index进去会返回一个空的列表。

      public class SubListTest {
          public static void main(String[] args) {
              List<String> list = new ArrayList<String>();
              list.add("1");
              list.add("2");
              list.add("3");
              list.add("4");
              List<String> subList = list.subList(1, 1);
              System.out.println("subList: " + subList); // subList: []
          }
      }
      
    2. subList方法返回的只是一个视图,是一个对源列表的映射。这意味着如果对subList返回的列表进行编辑操作,源列表也会收到影响。

    3. 对subList方法返回的列表进行添加、删除元素会抛出异常。

        	public class SubListTest { 
    		public static void main(String[] args) { 
    		List<String>; list = new ArrayList<String>(); 
    		list.add("1"); 
    		list.add("2")
    		list.add("3");
    		list.add("4");
    		List<String> subList = list.subList(0, 2); 
    		System.out.println(subList);
    		list.remove(1);
    		System.out.println("subList:" + subList); 
    		System.out.println("list:" + list); } }
    

    抛出异常:

    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.util.AbstractCollection.toString(AbstractCollection.java:454)
    	at java.lang.String.valueOf(String.java:2994)
    	at java.lang.StringBuilder.append(StringBuilder.java:131)
    	at com.lijunpeng.List.SubListTest.main(SubListTest.java:23)
    

    这是因为触发fail-fast机制,导致抛出异常,关于fail-fase机制,可以参见这篇博客:Java中的fail-fast机制

  • subList方法的源码

    public List<E> subList(int fromIndex, int toIndex) {
        // 判断参数的合法性
        subListRangeCheck(fromIndex, toIndex, size);
        // 返回一个SubList实例
        return new SubList(this, 0, fromIndex, toIndex);
    }
    

    该方法会返回一个SubList的实例,SubList是一个ArrayList的内部类,其构造方法如下:

        // SubList中的属性
        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;
            // 这个参数就是触发fail-fast机制的关键
            this.modCount = ArrayList.this.modCount;
        }
    

    可以看到,SubList中并没有数组、列表之类的属性来存储数据,这进一步说明了,subList方法返回的只是一个视图。 之前说过,对subList方法返回的列表进行修改,源列表也会跟着发生变化,下面是SubList的set方法

        public E set(int index, E e) {
            // 传入的参数索引合法性校验
            rangeCheck(index);
            // 这个方法就是用来检验modCount从而触发fail-fast机制,
            // 但是set这里modCount不会发生变化,所以不会触发fial-fast机制
            checkForComodification();
            // 从外部类(源列表)获取值
            E oldValue = ArrayList.this.elementData(offset + index);
            // 讲外部类(源列表)的值替换成新的值,ArrayList的值是存在elementData的数组中的。
            ArrayList.this.elementData[offset + index] = e;
            return oldValue;
        }
    

    从上面代码看出,修改subList的值其实是在修改源列表的值的,所以源列表值发生变化是必然的。 接下来再看一下为什么在源列表中删除或添加会抛出异常呢,首先我们先看一下之前报错的信息:

        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.util.AbstractCollection.toString(AbstractCollection.java:454)
            at java.lang.String.valueOf(String.java:2994)
            at java.lang.StringBuilder.append(StringBuilder.java:131)
            at com.lijunpeng.List.SubListTest.main(SubListTest.java:23)

首先报错的是main函数的23也就是:System.out.println("subList:" + subList) 这一句。这一步并没有操作subList,是调用了其toString方法,进一步调用了SubList的迭代器方法。所以需要往下看错误信息,发现在checkForComodification这个方法中报错,上面在set方法中也有这个方法,并且这个方法就是触发java的fail-fast机制。所以看一下这个checkForComodification方法的代码:

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

checkForComodificatio中比较了SubList类中的modCount和外部类ArrayList中的modCount是否相等,不相等就会抛出ConcurrentModificationException,触发fail-fast机制。那为什么这两个modeCount不相等呢?首先需要再回顾一下SubList类中的构造函数:

    // SubList的构造函数     SubList(AbstractList parent, int offset, int fromIndex, int toIndex) {
	this.parent = parent; 
	this.parentOffset = fromIndex; 
	this.offset = offset + fromIndex; 
	this.size = toIndex - fromIndex; 
	// 在构造时,SubList中的modCount和ArrayList中的modCount是一致的 
	this.modCount = ArrayList.this.modCount; 
    }

接着,看一下ArrayList中的add方法,注意:代码中调用的是list.add而不是subList.add,所以需要看ArrayList的add源码:

    public boolean add(E e) {
        ensureCapacityInternal(size + 1);
        elementData\[size++\] = e;
        return true;
    }

        private void ensureCapacityInternal(int minCapacity) {
	    ensureExplicitCapacity(calculateCapacity(elementData, minCapacity));
	}

        private void ensureExplicitCapacity(int minCapacity) { 
	    // 这里ArrayList的modCount发生了变化!
	    modCount++;
	    if (minCapacity - elementData.length > 0)
	    grow(minCapacity);
	}

示例代码中:在list.add("5"),之后就直接打印了,也就是说我们没有再对SubList进行操作,SubList中的modCount也没有发生改变,所有 ArrayList.this.modCount != this.modCount 成立,导致抛出了ConcurrentModificationException异常。这里不仅打印会抛出异常,只要对subList进行操作都会抛出,因为SubList的方法都进行了checkForComodificatio,检查这两个modCount。

但是如果只对subList方法返回的列表:subList进行增删元素的操作,并不会抛出异常,但是同样会影响源列表,因为subList的所有操作都是在操作源列表:

    public class SubListTest {
        public static void main(String[] args) {
            List<String> list = new ArrayList<String>();
            list.add("1");
            list.add("2");
            list.add("3");
            list.add("4");    List&lt;String&gt; subList = list.subList(0, 2);
        System.out.println(subList);
        subList.add("3");
        System.out.println("subList:" + subList); // subList:[1, 2, 3]
        System.out.println("list:" + list); // list:[1, 2, 3, 3, 4]
    }}
  • 如果想要对subList的操作,可以新建一个ArrayList,将subList addAll进这个新的列表,从而操作这一个新的列表。
 public class SubListTest {
     public static void main(String[] args) {
         List<String> list = new ArrayList<String>();
         list.add("1");
         list.add("2");
         list.add("3");
         list.add("4");
         List<String> subList = list.subList(0, 2);
         List<String> newList = new ArrayList<String>();
         newList.addAll(subList);
         newList.set(0, "new1");
         System.out.println("list:" + list); // list:[1, 2, 3, 4]
         System.out.println("subList:" + subList); // subList:[1, 2]
         System.out.println("newList:" + newList); // newList:[new1, 2]
     }
 }
  • 小结

    • ArrayList.subList方法返回的只是视图
    • subList的所有操作都是在操作源列表ArrayList,所以增删改都会以影响源列表。
    • 对ArrayList进行添加、删除的操作之后,在操作subList会抛出异常
    • 仅仅subList操作并不会抛出异常

  • 8
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
这个问题是因为 `subList` 方法返回的是 `ArrayList$SubList` 类型,而不是 `ArrayList` 类型。你可以通过将它转换为 `ArrayList` 类型来解决这个问题。 下面是一个示例代码,展示如何将 `subList` 返回的 `ArrayList$SubList` 转换为 `ArrayList`: ```java import java.util.ArrayList; import java.util.Collections; import java.util.List; public class Main { public static void main(String[] args) { // 假设这是原始的脉搏波数据 ArrayList<Double> pulseData = new ArrayList<>(); pulseData.add(10.0); pulseData.add(20.0); pulseData.add(15.0); pulseData.add(25.0); pulseData.add(12.0); // 对脉搏波数据进行值滤波 ArrayList<Double> filteredData = medianFilter(pulseData); // 打印滤波后的数据 for (Double data : filteredData) { System.out.println(data); } } public static ArrayList<Double> medianFilter(ArrayList<Double> data) { ArrayList<Double> filteredData = new ArrayList<>(); int windowSize = 3; // 滑动窗口大小 for (int i = 0; i < data.size(); i++) { int startIndex = Math.max(0, i - windowSize / 2); int endIndex = Math.min(data.size() - 1, i + windowSize / 2); // 获取滑动窗口内的数据,并转换为 ArrayList 类型 List<Double> window = new ArrayList<>(data.subList(startIndex, endIndex + 1)); // 对滑动窗口内的数据进行排序 Collections.sort(window); // 取值作为滤波后的数据 double median = window.get(window.size() / 2); filteredData.add(median); } return filteredData; } } ``` 在上述代码,我们将 `subList` 返回的结果使用 `ArrayList` 的构造函数转换为 `ArrayList` 类型,然后进行后续的处理。 请尝试使用这个方法,看看是否能够解决你的问题。如果你还有其他问题,请随时追问。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值