我是靠谱客的博主 过时小海豚,最近开发中收集的这篇文章主要介绍增强for循环实现原理和for循环实战性能优化,觉得挺不错的,现在分享给大家,希望可以做个参考。

概述

一、增强for循环

1. 三种常用for循环

// 普通for循环遍历
for (int i = 0; i < list.size(); i++) {
   System.out.print(list.get(i) + ",");
}
// 迭代器循环遍历
Iterator iterator = list.iterator();
while (iterator.hasNext()) {
   System.out.print(iterator.next() + ",");
}
// 增强for循环
for (Integer i : list) {
   System.out.print(i + ",");
}

2. 增强for循环实现原理

编译前:

for (Integer i : list) {
   System.out.print(i + ",");
}

编译后:

Integer i;
for(Iterator iterator = list.iterator(); iterator.hasNext(); System.out.println(i)){
   i = (Integer)iterator.next();        
}

源码解析:

Integer i; // 定义一个临时变量i
Iterator iterator = list.iterator(); // 获取List的迭代器
iterator.hasNext(); // 判断迭代器中是否有未遍历过的元素
i = (Integer)iterator.next(); // 获取第一个未遍历的元素,赋值给临时变量i
System.out.println(i) // 输出临时变量i的值

通过反编译源码,我们看到,其实JAVA中的增强for循环底层是通过迭代器模式来实现的。

所以以下的代码:

Iterator<Map.Entry<String, Integer>> entries = myMap.entrySet().iterator();
while (entries.hasNext()) {
    Map.Entry<String, Integer> entry = entries.next();
    String key = entry.getKey();
    Integer value = entry.getValue();
}

 可以简化为:

for(Map.Entry<String, Integer> entry : myMap.entrySet()) {
    String qingActivityId = entry.getKey();
    Integer value = entry.getValue();
}

 


3. 注意:增强for循环可能遇到的坑

既然增强for循环通过迭代器实现,那么必然有迭代器的特性。

Java中有fail-fast机制。在使用迭代器遍历元素的时候,在对集合进行删除的时候一定要注意,使用不当有可能发生ConcurrentModificationException,这是一种运行时异常,编译期并不会发生。只有在程序真正运行时才会爆发。

// 代码示例
for (UserInfo user : userInfos) {    
   if (user.getId() == 2)     
      userInfos.remove(user);    
}

 会抛出ConcurrentModificationException异常。

Iterator是工作在一个独立的线程中,并且拥有一个 mutex 锁。 Iterator被创建之后会建立一个指向原来对象的单链索引表,当原来的对象数量发生变化时,这个索引表的内容不会同步改变,所以当索引指针往后移动的时候就找不到要迭代的对象,所以按照 fail-fast 原则 Iterator 会马上抛出
java.util.ConcurrentModificationException异常。

所以 Iterator 在执行的时候是不允许被迭代的对象被改变的

删除对象的正确做法:

你可以使用 Iterator 本身的方法 remove() 来删除对象,Iterator.remove() 方法会在删除当前迭代对象的同时维护索引的一致性。

正确的在遍历的同时删除元素的示例:

Iterator<UserInfo> userIterator = users.iterator();    
while (userIterator.hasNext()) {    
   UserInfo userInfo = userIterator.next();    
   if (userInfo.getId() == 2)    
       userIterator.remove();//这里要使用Iterator的remove方法移除当前对象,如果使用List的remove方法,则同样会出现ConcurrentModificationException    
}

 

二、for循环实战性能优化

循环结构让我们操作数组、集合和其他一些有规律的事物变得更加的方便,但是如果我们在实际开发当中运用不合理,可能会给程序的性能带来很大的影响。所以我们还是需要掌握一些技巧来优化我们的代码的。

1. 嵌套循环

1.1 代码示例

优化前代码示例

Long stratTime = System.nanoTime();  
for (int i = 0; i < 10000; i++) {  
    for (int j = 0; j < 10; j++) {  
          
    }  
}  
Long endTime = System.nanoTime();  
System.out.println("外大内小耗时:"+ (endTime - stratTime));   

优化后代码示例

Long  stratTime = System.nanoTime();  
for (int i = 0; i <10 ; i++) {  
    for (int j = 0; j < 10000; j++) {  
          
    }  
}  
Long  endTime = System.nanoTime();  
System.out.println("外小内大耗时:"+(endTime - stratTime));  

运行结果:

外大内小耗时:1957590
外小内大耗时:1228223

由运行结果来看采用外大内小的方式性能差距还是比较大的。

1.2 原理

如果遇到分支结构,就可以利用分支目标缓冲器预测并读取指令的目标地址。分支目标缓冲器在程序运行时将动态记录和调整转移指令的目标地址,可以记录多个地址,对其进行表格化管理。当发生转移时,如果分支目标缓冲器中有记录,下一条指令在取指令阶段就会将其作为目标地址。如果记录地址等于实际目标地址,则并行成功;如果记录地址不等于实际目标地址,则流水线被冲洗。同一个分支,多次预测失败,则更新记录的目标地址。因此,分支预测属于“经验主义”或“机会主义”,会存在一定误测。
————摘抄来源<<C++反汇编与逆向分析技术解密>> 4.4.2 分支优化规则

1.3 原理解析

// 外小内大
for (int i = 0; i <10 ; i++) {  
    // 下面每次循环会预测成功9999次
    // 第1次没有预测,最后退出循环时预测失败1次
    // 这样的过程重复10次
    for (int j = 0; j < 10000; j++) {  
          a[i][j]++;
    }  
}  
// 外大内小
for (int i = 0; i < 10000; i++) {  
    // 下面每次循环会预测成功9次
    // 第1次没有预测,最后退出循环时预测失败1次
    // 这样的过程重复10000次
    for (int j = 0; j < 10; j++) {  
          a[i][j]++;
    }  
}  

2. 消除循环终止判断时的方法调用

2.1 代码示例

未优化前代码示例

Long stratTime = System.nanoTime();  
for (int i = 0; i < list.size(); i++) {  
      
}  
Long  endTime = System.nanoTime();  
System.out.println("未优化list耗时:"+(endTime - stratTime));  

优化后代码示例

Long  stratTime = System.nanoTime();  
int size = list.size();  
for (int i = 0; i < size; i++) {  
      
}  
Long  endTime = System.nanoTime();  
System.out.println("优化list耗时:"+(endTime - stratTime)); 

运行结果

未优化list耗时:27375  
优化list耗时:2444

2.2原理

list.size()每次循环都会被执行一次,这无疑会影响程序的性能,所以应该将其放到循环外面,用一个变量来代替,优化前后的对比也很明显。


3. 异常捕获

3.1 代码示例

优化前代码示例

Long stratTime = System.nanoTime();  
for (int i = 0; i < 10000000; i++) {  
    try {  
    } catch (Exception e) {  
    }  
}  
Long  endTime = System.nanoTime();  
System.out.println("在内部捕获异常耗时:"+(endTime - stratTime));  

优化后代码示例

Long  stratTime = System.nanoTime();  
try {  
    for (int i = 0; i < 10000000; i++) {  
    }  
} catch (Exception e) {  
  
}  
Long  endTime = System.nanoTime();  
System.out.println("在外部捕获异常耗时:"+(endTime - stratTime));  

运行结果

在内部捕获异常耗时:12150142  
在外部捕获异常耗时:1955  

3.2 总结

捕获异常是很耗资源的,所以不要讲try catch放到循环内部,优化后同样有好几个数量级的提升。

 


来源于:

https://www.jianshu.com/p/f7c82aa62439

最后

以上就是过时小海豚为你收集整理的增强for循环实现原理和for循环实战性能优化的全部内容,希望文章能够帮你解决增强for循环实现原理和for循环实战性能优化所遇到的程序开发问题。

如果觉得靠谱客网站的内容还不错,欢迎将靠谱客网站推荐给程序员好友。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(51)

评论列表共有 0 条评论

立即
投稿
返回
顶部