SubList到底怎么转化为ArrayList?

2023-10-19 02:38

本文主要是介绍SubList到底怎么转化为ArrayList?,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

SubList

大家好,今天 Tony 给大家讲个SubList转化的坑。

这个错误真的会被忽略,大家好好的看看,这个错误我们生产环境还真的遇到过。

集合类型相信大家都很熟悉,在 Java 中 ArrayList 使用的场景非常普遍。我们今天主要看的是 ArrayList 中的 subList 方法。

首先我们来看看源码

Returns a view of the portion of this list between the specified {@code fromIndex}, inclusive, and {@code toIndex}, exclusive.

在 jdk 的源码中清楚的写明了返回的是一个new SubList,方法的注释上面写的是返回一个 View,可以理解为视图。

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;}}

SubList 是 ArrayList 中的一个内部类,继承了 AbstractList,实现了 RandomAccess,从上面的代码中可以`看到,在 SubList 这个构造方法中还是直接引用的父类中的元素,只是单纯的将截取的索引重新赋值了一下。

使用场景

    public static void main(String[] args) {List<String> names = new ArrayList<String>() {{add("兔子");add("托尼");add("`啊");}};List<String> subList = names.subList(0, 3);System.out.println(subList);
}

上面的代码输出结果

[兔子, 托尼,]

在什么情况下会报错呢?接下来再看个例子,把上面的代码简单修改下,让数据返回 ArrayList

    public static void main(String[] args) {List<String> names = new ArrayList<String>() {{add("兔子");add("托尼");add("啊");}};ArrayList<String> subList = (ArrayList)names.subList(0, 3);System.out.println(subList);}

上面的代码直接抛出异常了

Exception in thread "main" java.lang.ClassCastException: java.util.ArrayList$SubList cannot be cast to java.util.ArrayList

为什么不能直接转换为 ArrayList 呢?上面的源码已经显示了,SubList 只是一个内部类,它继承 AbstractList 和 ArrayList 根本都没有关系,所以直接转化会报 Cast 异常。

ModificationException

SubList 同样具有集合原始的方法比如添加、删除等。我截取部分源码。

 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;}public E get(int index) {rangeCheck(index);checkForComodification();return ArrayList.this.elementData(offset + index);}public int size() {checkForComodification();return this.size;}public void add(int index, E e) {rangeCheckForAdd(index);checkForComodification();parent.add(parentOffset + index, e);this.modCount = parent.modCount;this.size++;}public E remove(int index) {rangeCheck(index);checkForComodification();E result = parent.remove(parentOffset + index);this.modCount = parent.modCount;this.size--;return result;}protected void removeRange(int fromIndex, int toIndex) {checkForComodification();parent.removeRange(parentOffset + fromIndex,parentOffset + toIndex);this.modCount = parent.modCount;this.size -= toIndex - fromIndex;}

上面的源码中每一个方法都包含有一个checkForComodification 方法。
这个方法是有什么作用呢?

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

源码中写的很清楚,判断原始类型,可以理解为父类型原始的 ArrayList 和当前的 SubList 方法中的元素个数做比较,如果不一样就报异常。
1、 对 subList 视图做数据的删除

    public static void main(String[] args) {List<String> namesList = new ArrayList<String>() {{add("兔子");add("托尼");add("啊");}};System.out.println("namesList原始的:== ==>" + namesList);List<String> subList = namesList.subList(0, 2);System.out.println("subList截取的:== ==>" + subList);//删除SubList第2个元素subList.remove(1);System.out.println("subList删除的:== ==>" + subList);System.out.println("namesList删除的:== ==>" + namesList);}

上面的代码运行正常输出结果

namesList原始的:== ==>[兔子, 托尼,]
subList截取的:== ==>[兔子, 托尼]
subList删除的:== ==>[兔子]
namesList删除的:== ==>[兔子,]

2、 对 ArrayList 做数据的删除

 public static void main(String[] args) {List<String> namesList = new ArrayList<String>() {{add("兔子");add("托尼");add("啊");}};System.out.println("namesList原始的:== ==>" + namesList);List<String> subList = namesList.subList(0, 2);System.out.println("subList截取的:== ==>" + subList);//删除ArraList第2个元素namesList.remove(1);System.out.println("subList删除的:== ==>" + subList);System.out.println("namesList删除的:== ==>" + namesList);
}

输出结果报异常了

namesList原始的:== ==>[兔子, 托尼,]
subList截取的:== ==>[兔子, 托尼]
Exception in thread "main" java.util.ConcurrentModificationExceptionat java.util.ArrayList$SubList.checkForComodification(ArrayList.java:1231)at java.util.ArrayList$SubList.listIterator(ArrayList.java:1091)at java.util.AbstractList.listIterator(AbstractList.java:299)at java.util.ArrayList$SubList.iterator(ArrayList.java:1087)at java.util.AbstractCollection.toString(AbstractCollection.java:454)at java.lang.String.valueOf(String.java:2994)at java.lang.StringBuilder.append(StringBuilder.java:131)

当我们对父元素 ArrayList 中对数据进行删除操作的时候,我们会发现 SubList 会报一个
ConcurrentModificationException 异常,这个异常是对数据比较发现元素被更改过,可以理解为脏数据吗?

总结

1、 SubList 和 ArrayList 之间没有任何关系

2、千万不要将 SubList 转化为 ArrayList 会报转换异常

3、对 SubList 视图元素修改会影响原始父 ArrayList 中的数据。

4、对 ArrayList 数据删除添加等修改,SubList 会报 Modification 异常

其实我们可以理解下,SubList 理解为一个视图,其实就是一个内部类,它的实现就是在原始的 ArrayList 中改变了截取的索引位置。

对视图的操作结果会反映到原始的 ArrayList 中,如果对原始的 ArrayList 做数据的添加删除操作,不好意思此刻的 SubList 已经报异常了。

通俗一点,可以修改儿子,不能修改父亲。

结果

SubList 转化为 ArrayList 可以用 Guava 中的封装方法

Lists.newArrayList(subList)

这篇关于SubList到底怎么转化为ArrayList?的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/236716

相关文章

怎么用idea创建一个SpringBoot项目

《怎么用idea创建一个SpringBoot项目》本文介绍了在IDEA中创建SpringBoot项目的步骤,包括环境准备(JDK1.8+、Maven3.2.5+)、使用SpringInitializr... 目录如何在idea中创建一个SpringBoot项目环境准备1.1打开IDEA,点击New新建一个项

qt5cored.dll报错怎么解决? 电脑qt5cored.dll文件丢失修复技巧

《qt5cored.dll报错怎么解决?电脑qt5cored.dll文件丢失修复技巧》在进行软件安装或运行程序时,有时会遇到由于找不到qt5core.dll,无法继续执行代码,这个问题可能是由于该文... 遇到qt5cored.dll文件错误时,可能会导致基于 Qt 开发的应用程序无法正常运行或启动。这种错

电脑提示xlstat4.dll丢失怎么修复? xlstat4.dll文件丢失处理办法

《电脑提示xlstat4.dll丢失怎么修复?xlstat4.dll文件丢失处理办法》长时间使用电脑,大家多少都会遇到类似dll文件丢失的情况,不过,解决这一问题其实并不复杂,下面我们就来看看xls... 在Windows操作系统中,xlstat4.dll是一个重要的动态链接库文件,通常用于支持各种应用程序

Python使用Code2flow将代码转化为流程图的操作教程

《Python使用Code2flow将代码转化为流程图的操作教程》Code2flow是一款开源工具,能够将代码自动转换为流程图,该工具对于代码审查、调试和理解大型代码库非常有用,在这篇博客中,我们将深... 目录引言1nVflRA、为什么选择 Code2flow?2、安装 Code2flow3、基本功能演示

Mac备忘录怎么导出/备份和云同步? Mac备忘录使用技巧

《Mac备忘录怎么导出/备份和云同步?Mac备忘录使用技巧》备忘录作为iOS里简单而又不可或缺的一个系统应用,上手容易,可以满足我们日常生活中各种记录的需求,今天我们就来看看Mac备忘录的导出、... 「备忘录」是 MAC 上的一款常用应用,它可以帮助我们捕捉灵感、记录待办事项或保存重要信息。为了便于在不同

springboot+vue项目怎么解决跨域问题详解

《springboot+vue项目怎么解决跨域问题详解》:本文主要介绍springboot+vue项目怎么解决跨域问题的相关资料,包括前端代理、后端全局配置CORS、注解配置和Nginx反向代理,... 目录1. 前端代理(开发环境推荐)2. 后端全局配置 CORS(生产环境推荐)3. 后端注解配置(按接口

JavaScript时间戳与时间的转化常用方法

《JavaScript时间戳与时间的转化常用方法》在JavaScript中,时间戳(Timestamp)通常指Unix时间戳,即从1970年1月1日00:00:00UTC到某个时间点经过的毫秒数,下面... 目录1. 获取当前时间戳2. 时间戳 → 时间对象3. 时间戳php → 格式化字符串4. 时间字符

一文带你搞懂Python中__init__.py到底是什么

《一文带你搞懂Python中__init__.py到底是什么》朋友们,今天我们来聊聊Python里一个低调却至关重要的文件——__init__.py,有些人可能听说过它是“包的标志”,也有人觉得它“没... 目录先搞懂 python 模块(module)Python 包(package)是啥?那么 __in

电脑死机无反应怎么强制重启? 一文读懂方法及注意事项

《电脑死机无反应怎么强制重启?一文读懂方法及注意事项》在日常使用电脑的过程中,我们难免会遇到电脑无法正常启动的情况,本文将详细介绍几种常见的电脑强制开机方法,并探讨在强制开机后应注意的事项,以及如何... 在日常生活和工作中,我们经常会遇到电脑突然无反应的情况,这时候强制重启就成了解决问题的“救命稻草”。那

Java强制转化示例代码详解

《Java强制转化示例代码详解》:本文主要介绍Java编程语言中的类型转换,包括基本类型之间的强制类型转换和引用类型的强制类型转换,文中通过代码介绍的非常详细,需要的朋友可以参考下... 目录引入基本类型强制转换1.数字之间2.数字字符之间引入引用类型的强制转换总结引入在Java编程语言中,类型转换(无论