天天看点

循环性能优化

循环就是让我们的程序重复地执行某些业务。在程序设计时,需要处理大量的重复动作,采用循环结构可以降低程序书写的长度和复杂度,可使复杂问题简单化,提高程序的可读性和执行速度。其中,for循环就是循环结构的一种,另外还有while循环和do-while循环语句。但是for循环是开发者最常用的开发方式。

今天要说的是最简单的 for 循环,一个简单的 for 循环看似没有任何优化的意义,但实质上优化前后差距挺大的,那么该如何优化呢?

循环性能优化

完成同样的功能,用不同的代码来实现,性能上可能会有比较大的差别,所以对于一些性能敏感的模块来说,对代码进行一定的优化还是很有必要的。今天就来说一下java代码优化的事情,今天主要聊一下对于for(while等同理)循环的优化。

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

嵌套循环

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

应改为:

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

两者耗时对比:

外大内小耗时:200192114
外小内大耗时:97995997      

由以上对比可知,优化后性能提升了一倍,嵌套循环应该遵循“外小内大”的原则,这就好比你复制很多个小文件和复制几个大文件的区别。

提取与循环无关的表达式

stratTime = System.nanoTime();
for (int i = 0; i < 10000000; i++) {
  i=i*a*b;
}
endTime = System.nanoTime();
System.out.println("未提取耗时:"+(endTime - stratTime));      

应改为:

stratTime = System.nanoTime();
c = a*b;
for (int i = 0; i < 10000000; i++) {
  i=i*c;
}
endTime = System.nanoTime();
System.out.println("已提取耗时:"+(endTime - stratTime));      

两者耗时对比:

未提取耗时:45973050
已提取耗时:1955      

代码中a+b与我们的循环无关,所以应该把它放到外面,避免重复计算,可以看出,优化后性能提升了好几个数量级,这些是不容忽视的。

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

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

应改为:

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

两者耗时对比:

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

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

异常捕获

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

应改为:

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

两者耗时对比:

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

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

尽管传统 ​

​for​

​​ 循环非常强大,但它有些过于复杂。Java 8 和 Java 9 中的新方法可帮助简化迭代,甚至是简化复杂的迭代。方法 ​

​range​

​​、​

​iterate​

​​ 和 ​

​limit​

​ 的可变部分较少,这有助于提高代码效率。这些方法还满足了 Java 的一个长期以来的要求,那就是局部变量必须声明为 final,然后才能从内部类访问它。将一个可变索引变量更换为实际的 final 参数只有很小的语义差别,但它减少了大量垃圾变量。最终您会得到更简单、更优雅的代码。

循环性能优化

Java 8的优化

java8推出了强大的流stream

我们开始吧。男人类有很多卡类,先初始化一些数据。

List<Man> mans = new ArrayList<>();
        mans.add(new Man("001","张三",Arrays.asList(new Card("工商银行","9558800001"),new Card("工商银行","9558800002"),new Card("建设银行","6227001234"))));
        mans.add(new Man("002","李四",Arrays.asList(new Card("招商银行","6225800002"),new Card("建设银行","6227035248"))));
        mans.add(new Man("003","王五",Arrays.asList(new Card("建设银行","6227056547"),new Card("中国银行","6013832547"),new Card("民生银行","4074058542"))));
        mans.add(new Man("004","赵六",Arrays.asList(new Card("工商银行","9558832458"),new Card("工商银行","9558832547"),new Card("建设银行","6227032578"))));
        mans.add(new Man("005","孙七",Arrays.asList(new Card("中国银行","6013825847"),new Card("农业银行","6228836547"),new Card("招商银行","6225014582"))));
        mans.add(new Man("006","张三",Arrays.asList(new Card("工商银行","9558832587"),new Card("交通银行","6222814578"),new Card("工商银行","9558865427"))));      

查找张三的男人,for是这样的,

public List<Man> getByName(List<Man> mans){
        List<Man> temp = new ArrayList<>();
        for(Man man : mans){
            if("张三".equals(man.getName())){
                temp.add(man);
            }
        }
        return temp;
    }      

改进后为:

public List<Man> getByName8(List<Man> mans) {
        return mans.stream().filter(m -> "张三".equals(m.getName())).collect(Collectors.toList());
    }      

这里的集合相当与数据库的表,而filter相当于数据库的where。 

继续,查找id为007的男人,id唯一,for是这样的

public Man getById(List<Man> mans) {
        for (Man man : mans) {
            if ("007".equals(man.getId())) {
                return man;
            }
        }
        return null;
    }      

改进后为:

public Man getById8(List<Man> mans) {
        return mans.stream().filter(m -> "oo7".equals(m.getId())).findFirst().orElse(null);
    }      

继续,获取名字叫张三(因有同名)的所有银行卡,这里不讨论实际业务意义,只讲技术,哈哈,用for是这样的。

public List<Card> getAllCardByName(List<Man> mans) {
        List<Card> cards = new ArrayList<>();
        for (Man man : mans) {
            if ("张三".equals(man.getName())) {
                cards.addAll(man.getCards());
            }
        }
        return cards;
    }      

改进后是这样的

public List<Card> getAllCardByName8(List<Man> mans) {
        return mans.stream().filter(m -> "张三".equals(m.getName())).flatMap(m -> m.getCards().stream())
                .collect(Collectors.toList());
    }      

继续,在3的条件上加个工商银行条件,for

public List<Card> getSomeCardByName(List<Man> mans) {
        List<Card> cards = new ArrayList<>();
        for (Man man : mans) {
            if ("张三".equals(man.getName())) {
                for (Card card : man.getCards()) {
                    if ("工商银行".equals(card.getName())) {
                        cards.add(card);
                    }
                }
            }
        }
        return cards;
    }      

改进后是这样的

public List<Card> getSomeCardByName8(List<Man> mans) {
        return mans.stream().filter(m -> "张三".equals(m.getName())).flatMap(m -> m.getCards().stream())
                .filter(c -> "工商银行".equals(c.getName())).collect(Collectors.toList());
    }      

把张三的名字修改为新张三,for,注意会改变源数据

public List<Man> changeName(List<Man> mans) {
        for (Man man : mans) {
            if ("张三".equals(man.getName())) {
                man.setName("新张三");
            }
        }
        return mans;
    }      

改进:

public List<Man> changeName8(List<Man> mans) {
        return mans.stream().peek(m -> {
            if ("张三".equals(m.getName()))
                m.setName("新张三");
        }).collect(Collectors.toList());
    }