目录

List增删元素后size大小发生变化带来的影响

List的几种遍历方式

报异常原因

增强for循环原理

异常原理

建议删除操作

性能对比

Stream流操作

Lambda表达式

语法

Lambda 表达式实例


List增删元素后size大小发生变化带来的影响

List的几种遍历方式

  • 一般型for循环遍历
for(int i = 0;i < list.size(); i ++){System.out.println(list.get(i));if(x.equals("del")){list.remove();}
}

这种方式的问题在于,删除某个元素后,list的大小发生了变化,而你的索引也在变化,所以会导致你在遍历的时候漏掉某些元素。比如当你删除第1个元素后,继续根据索引访问第2个元素时,因为删除的关系后面的元素都往前移动了一位,所以实际访问的是第3个元素。因此,这种方式可以用在删除特定的一个元素时使用,但不适合循环删除多个元素时使用。

  • 增强型for循环遍历
for(String item: list) {System.out.println(attribute .getId()+" *"+item.getName());if(x.equals("del")){list.remove();}
}

这种方式的问题在于,删除元素后继续循环会报错误信息ConcurrentModificationException,因为元素在使用的时候发生了并发的修改,导致异常抛出。但是删除完毕马上使用break跳出,则不会触发报错。

  • 集合类的通用遍历方式, 从很早的版本就有, 用迭代器迭代
Iterator it = list.iterator();
while(it.hasNext()){String x = it.next();if(x.equals("del")){it.remove();}
}

这种方式可以正常的循环及删除。但要注意的是,使用iterator的remove方法,如果用list的remove方法同样会报上面提到的ConcurrentModificationException错误。

  • Stream流
List<String> userNames = new ArrayList<String>() {{add("AAA");add("BBB");add("CCC");add("DDD");
}};
userNames = userNames.stream().filter(userName -> !userName.equals("BBB")).collect(Collectors.toList());
System.out.println(userNames);
  • Lambda表达式
list.forEach(a -> System.out.println(a));

总结:
  (1)循环删除list中特定一个元素的,可以使用三种方式中的任意一种,但在使用中要注意上面分析的各个问题。
  (2)循环删除list中多个元素的,应该使用迭代器iterator方式。

报异常原因

增强for循环原理

其实,增强for循环也是Java给我们提供的一个语法糖,如果将以上代码编译后的class文件进行反编译(使用jad工具)的话,可以得到以下代码:

Iterator iterator = userNames.iterator();
do
{if(!iterator.hasNext())break;String userName = (String)iterator.next();if(userName.equals("XiaoMing"))userNames.remove(userName);
} while(true);
System.out.println(userNames);

可以发现,原本的增强for循环,其实是依赖了while循环和Iterator实现的。

异常原理

异常:
java.util.ConcurrentModificationException

同样的,可以尝试下在增强for循环中使用add/remove方法添加元素,结果也会同样抛出该异常。
之所以会出现这个异常,是因为触发了一个Java集合的错误检测机制——fail-fast 。
增强for循环执行add/remove时,只修改了modCount,并没有对expectedModCount做任何操作,原码执行modCount != expectedModCount的判断时候,就会抛出ConcurrentModificationException。

简单总结一下,之所以会抛出ConcurrentModificationException异常,是因为我们的代码中使用了增强for循环,而在增强for循环中,集合遍历是通过iterator进行的,但是元素的add/remove却是直接使用的集合类自己的方法。这就导致iterator在遍历的时候,会发现有一个元素在自己不知不觉的情况下就被删除/添加了,就会抛出一个异常,用来提示用户,可能发生了并发修改。

建议删除操作

1.使用for循环删除(仅单个删除,然后break)
2.增强for循环删除(仅单个删除,然后break)
3.Iterator删除(可多个)
4.使用Java 8中提供的filter过滤

Java 8中可以把集合转换成流,对于流有一种filter操作, 可以对原始 Stream 进行某项测试,通过测试的元素被留下来生成一个新 Stream。

List<String> userNames = new ArrayList<String>() {{add("AAA");add("BBB");add("CCC");add("DDD");
}};
userNames = userNames.stream().filter(userName -> !userName.equals("BBB")).collect(Collectors.toList());
System.out.println(userNames);

性能对比

  • 对于ArrayList和LinkedList,在size小于1000时,每种方式的差距都在几ms之间,差别不大,选择哪个方式都可以。
  • 对于ArrayList,无论size是多大,差距都不大,选择哪个方式都可以。
  • 对于LinkedList,当size较大时,建议使用迭代器或for-each的方式进行遍历,否则效率会有较明显的差距。

所以,综合来看,建议使用 for-each,代码简洁,性能也不差。

Stream流操作

Java 8 API添加了一个新的抽象称为流Stream,可以让你以一种声明的方式处理数据。

Stream 使用一种类似用 SQL 语句从数据库查询数据的直观方式来提供一种对 Java 集合运算和表达的高阶抽象。

这种风格将要处理的元素集合看作一种流, 流在管道中传输, 并且可以在管道的节点上进行处理, 比如筛选, 排序,聚合等。

元素流在管道中经过中间操作(intermediate operation)的处理,最后由最终操作(terminal operation)得到前面处理的结果。

+--------------------+       +------+   +------+   +---+   +-------+
| stream of elements +-----> |filter+-> |sorted+-> |map+-> |collect|
+--------------------+       +------+   +------+   +---+   +-------+

以上的流程转换为 Java 代码为:

List<Integer> transactionsIds =
widgets.stream().filter(b -> b.getColor() == RED).sorted((x,y) -> x.getWeight() - y.getWeight()).mapToInt(Widget::getWeight).sum();

另外再单独学习一下

Lambda表达式

Lambda 表达式,也可称为闭包,它是推动 Java 8 发布的最重要新特性。

Lambda 允许把函数作为一个方法的参数(函数作为参数传递进方法中)。

使用 Lambda 表达式可以使代码变的更加简洁紧凑。

语法

lambda 表达式的语法格式如下:

(parameters) -> expression
或
(parameters) ->{ statements; }

以下是lambda表达式的重要特征:

  • 可选类型声明:不需要声明参数类型,编译器可以统一识别参数值。
  • 可选的参数圆括号:一个参数无需定义圆括号,但多个参数需要定义圆括号。
  • 可选的大括号:如果主体包含了一个语句,就不需要使用大括号。
  • 可选的返回关键字:如果主体只有一个表达式返回值则编译器会自动返回值,大括号需要指定表达式返回了一个数值。

Lambda 表达式实例

Lambda 表达式的简单例子:

// 1. 不需要参数,返回值为 5
() -> 5  // 2. 接收一个参数(数字类型),返回其2倍的值
x -> 2 * x  // 3. 接受2个参数(数字),并返回他们的差值
(x, y) -> x – y  // 4. 接收2个int型整数,返回他们的和
(int x, int y) -> x + y  // 5. 接受一个 string 对象,并在控制台打印,不返回任何值(看起来像是返回void)
(String s) -> System.out.print(s)

在 Java8Tester.java 文件输入以下代码:

public class Java8Tester {public static void main(String args[]){Java8Tester tester = new Java8Tester();// 类型声明MathOperation addition = (int a, int b) -> a + b;// 不用类型声明MathOperation subtraction = (a, b) -> a - b;// 大括号中的返回语句MathOperation multiplication = (int a, int b) -> { return a * b; };// 没有大括号及返回语句MathOperation division = (int a, int b) -> a / b;System.out.println("10 + 5 = " + tester.operate(10, 5, addition));System.out.println("10 - 5 = " + tester.operate(10, 5, subtraction));System.out.println("10 x 5 = " + tester.operate(10, 5, multiplication));System.out.println("10 / 5 = " + tester.operate(10, 5, division));// 不用括号GreetingService greetService1 = message ->System.out.println("Hello " + message);// 用括号GreetingService greetService2 = (message) ->System.out.println("Hello " + message);greetService1.sayMessage("Runoob");greetService2.sayMessage("Google");}interface MathOperation {int operation(int a, int b);}interface GreetingService {void sayMessage(String message);}private int operate(int a, int b, MathOperation mathOperation){return mathOperation.operation(a, b);}
}

执行以上脚本,输出结果为:

$ javac Java8Tester.java
$ java Java8Tester
10 + 5 = 15
10 - 5 = 5
10 x 5 = 50
10 / 5 = 2
Hello Runoob
Hello Google

使用 Lambda 表达式需要注意以下两点:

  • Lambda 表达式主要用来定义行内执行的方法类型接口,例如,一个简单方法接口。在上面例子中,我们使用各种类型的Lambda表达式来定义MathOperation接口的方法。然后我们定义了sayMessage的执行。
  • Lambda 表达式免去了使用匿名方法的麻烦,并且给予Java简单但是强大的函数化的编程能力。

List增删元素后size大小发生变化带来的影响、Stream流操作、Lambda表达式相关推荐

  1. Java8——Stream流操作List排序_List集合中每个对象元素按时间顺序排序

    一个学生类的实体类 @Data public class Student {private Long id;private String name;private int age;private Do ...

  2. 容器删除元素后迭代器失效_STL 4: STL之容器:选择时机,删除元素,迭代器失效...

    一. 种类: 标准STL序列容器:vector.string.deque和list. 标准STL关联容器:set.multiset.map和multimap. 非标准序列容器slist和rope.sl ...

  3. 妙用0元素数组 实现大小可变结构体

    妙用0元素数组 实现大小可变结构体 2008-04-06 20:31 http://hi.baidu.com/phps/blog/item/4de94efbe9595660024f56fb.html ...

  4. 浅析深度学习中Batch Size大小对训练过程的影响

    点击上方"视学算法",选择加"星标"或"置顶" 重磅干货,第一时间送达 作者丨陈志远@知乎(已授权) 来源丨https://zhuanlan ...

  5. batch size 训练时间_深度学习 | Batch Size大小对训练过程的影响

    转自:面试中问你 Batch Size大小对训练过程的影响​mp.weixin.qq.com 先看两个问题: (1)深度学习中batch size的大小对训练过程的影响是什么样的? (2)有些时候不可 ...

  6. LeetCode 1619. 删除某些元素后的数组均值

    文章目录 1. 题目 2. 解题 1. 题目 给你一个整数数组 arr ,请你删除最小 5% 的数字和最大 5% 的数字后,剩余数字的平均值. 与 标准答案 误差在 10^-5 的结果都被视为正确结果 ...

  7. python多维数组添加元素_numpy中三维数组中加入元素后的位置详解

    今天做数据处理时,遇到了从三维数组中批量加入二维数组的需求.其中三维数组在深度学习的特征数据处理时经常会使用到,所以读者有必要对该小知识点做到清楚了解并掌握.现对三维数组中的元素位置结合代码做详细归纳 ...

  8. 使用伪元素插入图片大小调整问题

    css使用伪元素插入图片大小调整问题 需求描述: 想要利用伪元素在a标签后插入图片,调整为合适大小. 效果图: 问题描述: 使用伪元素例如::after的content属性插入图片不能调整图片大小问题 ...

  9. 深入剖析深度学习中Batch Size大小对训练过程的影响

    点击上方"AI算法与图像处理",选择加"星标"或"置顶" 重磅干货,第一时间送达 推荐文章[点击下面可直接跳转]: 来源:https://z ...

最新文章

  1. gRPC 的 4 种基础通信模式
  2. 腾讯的电商,在东南亚击败了阿里巴巴
  3. C++继承时的对象内存位置(一)
  4. [网络安全自学篇] 五十九.Windows安全缺陷利用之MS08-067远程代码执行及深度防御解析
  5. C#启动停止SQL数据库服务
  6. Java IO: InputStream
  7. 有意思的小学数学竞赛题-2
  8. 终于去掉了location.reload()弹出的问题。
  9. Java并发编程:同步容器
  10. 正则表达式:模式修饰符(自用)
  11. Java语言程序设计(基础篇)课后答案
  12. 固态硬盘简称是不是ssd_小白科普:没想到你是这样的固态硬盘
  13. r2游戏服务器网站,神秘揭晓《R2》公测服务器名称首度公布
  14. 初识内存控制器和SDRAM【一文了解】
  15. SCI分区:JCR分区和中科院分区 的差别
  16. 在选用矿物质防火电缆的时候应该注意什么?
  17. codeup 1006
  18. 全球 40 位 40 岁以下的富豪
  19. Java Gradle入门指南之gretty插件(安装、命令与核心特性)
  20. C3P Software 发布 Cast-Designer V7.7版本

热门文章

  1. Unieap3.5-Grid翻页不提示修改
  2. poj2125最小点权覆盖
  3. Kinect 开发 —— 近距离探测
  4. C/C++浮点数在内存中的存储方式《转》
  5. Response.Redirect和Server.Transfer的区别
  6. layer弹出层闪退_jQuery使用Layer弹出层插件闪退问题
  7. QT之Win10安装(五)
  8. Gstreamer1.18.4编译(二十六)
  9. MAC上使用gdb(Mac10.12未解决)
  10. 红茶一杯话Binder(初始篇)