关于List比较好玩的操作
作为Java大家庭中的集合类框架,List应该是平时开发中最常用的,可能有这种需求,当集合中的某些元素符合一定条件时,想要删除这个元素。如:
public class ListTest {
public static void main(String[] args) {
List<Integer> intList = new ArrayList<Integer>();
Collections.addAll(intList, 1, 2, 3, 5, 6);
// for循环优化写法,只获取一次长度
for(int i = 0, size = intList.size(); i < size; i++) {
Integer value = intList.get(i);
// 符合条件,删除元素
if(value == 3 || value == 5) {
intList.remove(i);
}
}
System.out.println(intList);
}
}
执行后,会抛出IndexOutOfBoundsException,因为集合中存在符合条件的元素,删除后,集合长度动态改变,由于长度只获取一次,发生越界,所以,去掉for循环优化,如:
public class ListTest {
public static void main(String[] args) {
List<Integer> intList = new ArrayList<Integer>();
Collections.addAll(intList, 1, 2, 3, 5, 6);
for(int i = 0; i < intList.size(); i++) {
Integer value = intList.get(i);
// 符合条件,删除元素
if(value == 3 || value == 5) {
intList.remove(i);
}
}
System.out.println(intList);
}
}
输出:[1, 2, 5, 6],漏掉了5这个元素,当i=2的时候,值为3,删除后,后面的元素往前补一位,这时i=3的时候,值为6,跳过了5,这样也不行,随后想到了用for循环增强,不显示的操作下标,直接操作对象,如:
public class ListTest {
public static void main(String[] args) {
List<Integer> intList = new ArrayList<Integer>();
Collections.addAll(intList, 1, 2, 3, 5, 6);
for(Integer value : intList) {
// 符合条件,删除元素
if(value == 3 || value == 5) {
intList.remove(value);
}
}
System.out.println(intList);
}
}
执行后,会抛出ConcurrentModificationException,字面意思是并发修改异常。异常跟踪信息如下:
Exception inthread "main" java.util.ConcurrentModificationException
atjava.util.AbstractList$Itr.checkForComodification(AbstractList.java:449)
at java.util.AbstractList$Itr.next(AbstractList.java:420)
at ListTest.main(ListTest.java:13)
可以大概看出是执行到AbstractList中内部类Itr的checkForComodification方法抛出的异常,至于为什么出现异常,这里可以大概解释一下。集合遍历是使用Iterator, Iterator是工作在一个独立的线程中,并且拥有一个互斥锁。Iterator 被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照 fail-fast原则 Iterator 会马上抛出java.util.ConcurrentModificationException 异常。所以 Iterator 在工作的时候是不允许被迭代的对象被改变的。
而要解决这个问题,可以使用Iterator的remove方法,该方法会删除当前迭代对象的同时,维护索引的一致性。如:
public class ListTest {
public static void main(String[] args) {
List<Integer> intList = new ArrayList<Integer>();
Collections.addAll(intList, 1, 2, 3, 5, 6);
Iterator<Integer> it = intList.iterator();
while(it.hasNext()) {
Integer value = it.next();
if(value == 3 || value == 5) {
it.remove();
}
}
System.out.println(intList);
}
}
输出正确结果:[1, 2, 6]。
不使用迭代器的解决方案就是,自己维护索引,删除一个元素后,索引-1,如:
public class ListTest {
public static void main(String[] args) {
List<Integer> intList = new ArrayList<Integer>();
Collections.addAll(intList, 1, 2, 3, 5, 6);
for(int i = 0; i < intList.size(); i++) {
Integer value = intList.get(i);
if(value == 3 || value == 5) {
intList.remove(i);
i--;
}
}
System.out.println(intList);
}
}
输出正确结果:[1, 2, 6]。
还有种取巧的方式是从最后一个元素开始遍历,符合条件的删除,如:
public class ListTest {
public static void main(String[] args) {
List<Integer> intList = new ArrayList<Integer>();
Collections.addAll(intList, 1, 2, 3, 5, 6);
for(int i = intList.size() - 1; i >= 0; i--) {
Integer value = intList.get(i);
if(value == 3 || value == 5) {
intList.remove(i);
}
}
System.out.println(intList);
}
}
输出正确结果:[1, 2, 6]。
最后,Java集合类框架真是大大方便了开发,不用自己去维护数组,随时担心着越界等问题。当然List的实现类对插入、删除的效率不太一样,这取决于其实现的数据结构,是选择删除,还是选择新建个集合,这里就不做讨论了。
关于List比较好玩的操作的更多相关文章
- IDEA使用笔记(十一)——好玩的类图结构
今天使用 IntelliJ IDEA 发现一个好玩的操作,尤其对于研究源码了解类的层级关系有非常大的帮助! 1:先看效果 1-1:HashSet的类图结构——继承什么类.实现什么接口一目了然 1-2: ...
- NOR Flash擦写和原理分析 (二)
Nor Flash上电后处于数据读取状态(Reading Array Data).此状态可以进行正常的读.这和读取SDRAM/SRAM/ROM一样.(要是不一样的话,芯片上电后如何从NorFlash中 ...
- AutoTile 自动拼接(五) 学习与实践
今天不讲 权值检索,考虑到后期 自动拼接 做出来 更好玩,操作更方便.所以 今天我 补充一节, 网格计算与操作. 具体就是这么个效果,和地图编辑器一样,不过图块还是没有自然的拼接,这个一定一定是 下一 ...
- 第一次作业:基于Linux操作系统深入源码进程模型分析
1.Linux操作系统的简易介绍 Linux系统一般有4个主要部分:内核.shell.文件系统和应用程序.内核.shell和文件系统一起形成了基本的操作系统结构,它们使得用户可以运行程序.管理文件并使 ...
- NOR Flash的学习
NOR Flash简介 NOR FLASH是INTEL在1988年推出的一款商业性闪存芯片,它需要很长的时间进行抹写,大半生它能够提供完整的寻址与数据总线,并允许随机存取存储器上的任何区域,而且 ...
- 【数据结构】循环链表&&双向链表详解和代码实例
喜欢的话可以扫码关注我们的公众号哦,更多精彩尽在微信公众号[程序猿声] 01 循环链表 1.1 什么是循环链表? 前面介绍了单链表,相信大家还记得相关的概念.其实循环链表跟单链表也没有差别很多,只是在 ...
- Leetcode 344:Reverse String 反转字符串(python、java)
Leetcode 344:Reverse String 反转字符串 公众号:爱写bug Write a function that reverses a string. The input strin ...
- 这是不是你想要了解SQL的艺术,基础语法等等
一.基础sql语句: 模块定义 基础语句 基础功能 数据定义 create table 创建数据库表 drop table 删除数据表 alter table 修改表结构 create view 创建 ...
- C# PDF Page操作——设置页面切换按钮 C# 添加、读取Word脚注尾注 C#为什么不能像C/C++一样的支持函数只读传参 web 给大家分享一个好玩的东西,也许你那块就用的到
C# PDF Page操作——设置页面切换按钮 概述 在以下示例中,将介绍在PDF文档页面设置页面切换按钮的方法.示例中将页面切换按钮的添加分为了两种情况,一种是设置按钮跳转到首页.下页.上页或者 ...
随机推荐
- Jmeter后置处理器,正则表达式提取器的使用
[使用场景]:下一个请求参数需要从上一个请求的响应数据中获取 [jmeter正则表达式说明]:使用perl正则表达式(可参考:http://www.runoob.com/perl/perl-regul ...
- 非常简洁简单的tcp socket库 XKSocket
一个非常简洁简单的异步tcp socket库,主要就是分包的问题,处理组包,粘包等问题 非常适合新手:) 项目中带有使用示例. http://git.oschina.net/dreamzgj/XKSo ...
- C++ Primer 抄书笔记(二)——变量和基本类型(上)
一.基本内置类型 base build-in type[算数类型/类型转换/字面值常量] 基本内置类型(算数类型arithmetic type(整型integral type(字符,布尔bool),浮 ...
- 函数的默认值与动态参数arguments的总结
在js函数与作用域,了解函数基本概念中,我们发现当函数的实参有一个没有上传的时候,对应的形参time展示的值就是undefined,如下代码所示: <!DOCTYPE html> < ...
- css3基本选择器+属性选择器+动态伪类+UI状态伪类+结构类
后代选择器 祖先元素 后代元素{ } 子元素选择器(直接子元素选择器) 父元素>子元素{ } 兄弟选择器 元素+兄弟元素(紧邻该元素之后的下一个兄弟元素) 所有兄弟元素选择器 元素~兄弟元素(该 ...
- Linux命令详解之–chmod命令
在Linux中,一般使用chmod命令来修改文件的属性. 利用 chmod 可以藉以控制文件如何被他人所调用.此命令所有使用者都可使用. 一.Linux chmod命令语法Linux chmod 命令 ...
- 如何在vue-cli中使用vuex(配置成功
前言 众所周知,vuex 是一个专为 vue.js 应用程序开发的状态管理模式,在构建一个中大型单页应用中使用vuex可以帮助我们更好地在组件外部管理状态.而vue-cli是vue的官方脚手架,它能帮 ...
- 【剑指Offer】58:二叉树的下一个结点
题目描述 给定一个二叉树和其中的一个结点,请找出中序遍历顺序的下一个结点并且返回.注意,树中的结点不仅包含左右子结点,同时包含指向父结点的指针. 题解一:递归 //既然给了二叉树的某个结点,且二叉树存 ...
- Pr制作音乐相册
Pr制作音乐相册 设置蒙版效果
- 42.MySQL数据库安装,及驱动程序选择
MySQL驱动程序安装: 我们使用Django来操作Mysql,实际上底层还是通过Python来操作的,因此我们想要使用Django来操作mysql,首先还是需要安装一个驱动程序,在Python3中, ...