用于循环优化

如何解决用于循环优化

|
List<String> flowers = new ArrayList<String>();
我的for循环目前看起来像这样...
for (int i = 0; i < flowers.size(); i++) {
...
}
或者我应该将其更改为如下所示的代码
int size = flowers.size();
for (int i = 0; i < size; i++) {
...
}
哪个表现更好(假设我有很多花),我猜应​​该是后者。     

解决方法

最好使用for-each循环[更具可读性]
for (Flower flower :flowers){
    //...
}
我已将
javap
的指令转储为以下代码:
public void forLoop1() {
    List<String> lst = new ArrayList<String>();
    for (int i = 0; i < lst.size(); i++) {
        System.out.println(\"hi\");
    }
}

public void forLoop2() {
    List<String> lst = new ArrayList<String>();
    int size = lst.size();
    for (int i = 0; i < size; i++) {
        System.out.println(\"hi\");
    }
}
public void forLoop1();
  Code:
   0:   new     #2; //class java/util/ArrayList
   3:   dup
   4:   invokespecial   #3; //Method java/util/ArrayList.\"<init>\":()V
   7:   astore_1
   8:   iconst_0
   9:   istore_2
   10:  iload_2
   11:  aload_1
   12:  invokeinterface #4,1; //InterfaceMethod java/util/List.size:()I
   17:  if_icmpge       34
   20:  getstatic       #5; //Field java/lang/System.out:Ljava/io/PrintStream;
   23:  ldc     #6; //String hi
   25:  invokevirtual   #7; //Method java/io/PrintStream.println:(Ljava/lang/Str
ing;)V
   28:  iinc    2,1
   31:  goto    10
   34:  return

public void forLoop2();
  Code:
   0:   new     #2; //class java/util/ArrayList
   3:   dup
   4:   invokespecial   #3; //Method java/util/ArrayList.\"<init>\":()V
   7:   astore_1
   8:   aload_1
   9:   invokeinterface #4,1; //InterfaceMethod java/util/List.size:()I
   14:  istore_2
   15:  iconst_0
   16:  istore_3
   17:  iload_3
   18:  iload_2
   19:  if_icmpge       36
   22:  getstatic       #5; //Field java/lang/System.out:Ljava/io/PrintStream;
   25:  ldc     #6; //String hi
   27:  invokevirtual   #7; //Method java/io/PrintStream.println:(Ljava/lang/Str
ing;)V
   30:  iinc    3,1
   33:  goto    17
   36:  return
它没有为我优化。   Java版本\“ 1.6.0_22 \” Java(TM)SE   运行时环境(构建   1.6.0_22-b04)Java HotSpot(TM)客户端VM(内部版本17.1-b03,混合模式,   分享) 因此,如果您需要从上述两个中进行选择,请争取第二名,但我个人会选择
for-each
。 每个绩效 摘自Joshua Bloch撰写的有效Java的条款46:   for-each循环,在中引入   发布1.5,摆脱混乱   和错误的机会   隐藏迭代器或索引变量   完全。结果成语   同样适用于集合   数组:
// The preferred idiom for iterating over collections and arrays
for (Element e : elements) {
    doSomething(e);
}
     当您看到冒号(:)时,将其读为   因此,上面的循环读为   “对于元素中的每个元素e。”注意   没有性能损失   使用for-each循环,甚至   数组。实际上,它可能会提供一些   性能优于普通   在某些情况下for循环   计算数组索引的限制   只有一次。虽然您可以通过   手(项目45),程序员不会   总是这样做。 也可以看看 一个循环和每个循环之间有一个性能差异     ,很抱歉,@ Jigar的回答不正确。这是正确的答案。 (tldr;请勿使用
for : each
)。
import java.util.ArrayList;
import java.util.List;

public class LoopTest {

    public static void main(String s[]) {

        long start,end;

        List<Integer> a =  new ArrayList<Integer>();

        for (int i = 0; i < 2500000; i++) {
            a.add(i);
        }

        ///// TESTING FOR : EACH LOOP

        start = System.currentTimeMillis();

        for (Integer j : a) {
            int x = j + 3;
        }

        end = System.currentTimeMillis();

        System.out.println(end - start
                + \" milli seconds for [ Integer j : a ] \");

        ////// TESTING DEFAULT LOOP

        start = System.currentTimeMillis();
        for (int i = 0; i < a.size(); i++) {
            int x = a.get(i) + 3;
        }

        end = System.currentTimeMillis();

        System.out.println(end - start
                + \" milli seconds for [ int i = 0; i < a.length; i++ ] \");


        ////// TESTING SLIGHTLY OPTIMIZED LOOP

        start = System.currentTimeMillis();
        int size = a.size();
        for (int i = 0; i < size; i++) {
            int x = a.get(i) + 3;
        }

        end = System.currentTimeMillis();

        System.out.println(end - start
                + \" milli seconds for [ int i = 0; i < size; i++ ] \");        

        //// TESTING MORE OPTIMIZED LOOP

        start = System.currentTimeMillis();
        for (int i = size; --i >= 0;) {
            int x = a.get(i) + 3;
        }

        end = System.currentTimeMillis();

        System.out.println(end - start
                + \" milli seconds for [ int i = size; --i >= 0; ] \");       

    }

}
结果:
96 milli seconds for [ Integer j : a ] 
57 milli seconds for [ int i = 0; i < a.length; i++ ] 
31 milli seconds for [ int i = 0; i < size; i++ ] 
31 milli seconds for [ int i = size; --i >= 0; ] 
您可以下定决心,但是将过多的归因于JVM优化器。您仍然必须对自己的代码保持精明,并且使用
for : each
表示法不是一个好主意(几乎)。如您所见,将size放入其自己的变量中是一个好主意。 即使其中的某些优化可能是依赖于JVM的(某些可能会与JIT配合使用),但重要的是要知道Java做什么和不做什么。     ,JVM无法优化它,因为
size()
是一种方法,并且JVM无法(也不会尝试)确定ѭ13always在此上下文中将始终返回相同的值。假设
size()
的值不变,第二个的性能会略高一些,但是增益却是如此,以至于您根本不必考虑使用它。     ,如果性能至关重要,请使用普通计数器循环,但是对于98%的情况,代码的清晰度和简洁性更为重要(例如1000x或更多),因此应使用for-each循环。 @David指出使用计数器的速度更快,但我要指出的是,即使是10,000个条目,其差异也将达到亚微秒。 如果您有超过10,000个条目的集合,则很可能不应该对每种可能性都进行强力迭代。无论您有什么想法,像Map这样具有查找功能的集合更有可能是一个更好的解决方案。如果您的条目少于10,000个,那么性能就不太重要了。     ,如果数组列表在迭代时更改,则行为会有所不同。但是我猜你不这样做。根据我的测试,后者通常更快(特别是在Android之类的系统上)。我将其编写如下:
for (int i = 0,size = flowers.size(); i < size; i++) {
    ...
}
    ,根据Java语言规范(14.14.1):   基本的for语句执行一些初始化代码,然后执行   表达式,语句和一些更新代码重复执行,直到   表达式为假。 在第一个示例中,表达式为
i < flowers.size()
,并且在每次迭代中都对其求值一次。在您的特殊情况下,它应该没有明显的区别,因为ѭ19上的
flowers.getSize()
是很短的方法。但是,总的来说,如果表达式的结果对于每次迭代都是相同的,并且代价昂贵,则进行预计算。 结果:在Java虚拟机的每个实现中都必须产生相同的输出,并证明Expression在每个迭代中都被评估一次:
int counter2 = 10;
for (int counter1 = 0; counter1 < counter2; counter1++) {
  System.out.println(counter1 + \",\" + counter2);
  counter2--;
}
输出:
0,10
1,9
2,8
3,7
4,6
    ,最好的选择是
[ int i = 0; i < size; i++ ]
您的结果将根据JVM和-client vs -server等其他设置而有所不同 因为某些测量是如此之小,所以您需要使用纳秒级的时间进行测量,并且您需要进行许多测试,否则最终会导致GC弄乱结果。同样,这些类型的测试也具有JVM将循环优化为零的习惯。我试图通过将在代码末尾修改的变量放到屏幕上来消除这种风险。
1.6
-server
7.968242071 milli seconds for [ Integer j : a ] 
7.206275775999999 milli seconds for [ int i = 0; i < a.length; i++ ]  
1.5864E-5 milli seconds for [ int i = 0; i < size; i++ ] 
14.774186076999998 milli seconds for [ int i = size; --i >= 0; ] 

-client
83.36101683999999 milli seconds for [ Integer j : a ] 
44.288568631 milli seconds for [ int i = 0; i < a.length; i++ ]  
2.3191E-5 milli seconds for [ int i = 0; i < size; i++ ] 
24.826621246 milli seconds for [ int i = size; --i >= 0; ] 

1.7

-server
7.029150422 milli seconds for [ Integer j : a ] 
6.6269827779999995 milli seconds for [ int i = 0; i < a.length; i++ ]  
1.3852E-5 milli seconds for [ int i = 0; i < size; i++ ] 
13.842110377 milli seconds for [ int i = size; --i >= 0; ] 
13.868426141 milli seconds for [ int i = a.size()-1; i >= 0; i-- ] 
1.6618000000000003E-5 milli seconds for [ int i = 0; i < a.size(); i++ ] 

-client
7.382479727 milli seconds for [ Integer j : a ] 
6.748068759 milli seconds for [ int i = 0; i < a.length; i++ ]  
1.4162999999999998E-5 milli seconds for [ int i = 0; i < size; i++ ] 
13.951547335999999 milli seconds for [ int i = size; --i >= 0; ] 
13.929234053999998 milli seconds for [ int i = a.size()-1; i >= 0; i-- ] 
1.6873E-5 milli seconds for [ int i = 0; i < a.size(); i++ ] 
测试代码:
public static void main(String s[]) {
long start=0,end = 0,delta = 0;
//int[] a = new int[2500000];
List<Integer> a = new ArrayList<Integer>();
int x = 0;

for (int i = 0; i < 2500000; i++) {
    a.add(i);
}

start=0; end = 0; delta = 0;
for (int ctr = 0; ctr < 1000; ctr++) {
    start = System.nanoTime();
    for (Integer j : a) {
         x = j + 3;
    }
    end = System.nanoTime();
    delta += end - start;
}
System.out.println(Math.pow(10,-6) * delta / 1000 + \" milli seconds for [ Integer j : a ] \");


start=0; end = 0; delta = 0;
for (int ctr = 0; ctr < 1000; ctr++) {
    start = System.nanoTime();
    for (int i = 0; i < a.size(); i++) {
         x = a.get(i) + 3;
    }
    end = System.nanoTime();
    delta += end - start;
}
System.out.println(Math.pow(10,-6) * delta / 1000 + \" milli seconds for [ int i = 0; i < a.length; i++ ]  \");

int size = a.size();

start=0; end = 0; delta = 0;
for (int ctr = 0; ctr < 1000; ctr++) {
    start = System.currentTimeMillis();

    for (int i = 0; i < size; i++) {
         x = a.get(i) + 3;
    }
    end = System.currentTimeMillis();
    delta += end - start;
}
System.out.println(Math.pow(10,-6) * delta / 1000 + \" milli seconds for [ int i = 0; i < size; i++ ] \");

start=0; end = 0; delta = 0;
for (int ctr = 0; ctr < 1000; ctr++) {
    start = System.nanoTime();
    for (int i = size; --i >= 0;) {
         x = a.get(i) + 3;
    }
    end = System.nanoTime();
    delta += end - start;
}
System.out.println(Math.pow(10,-6) * delta / 1000 + \" milli seconds for [ int i = size; --i >= 0; ] \");


start=0; end = 0; delta = 0;
for (int ctr = 0; ctr < 1000; ctr++) {
    start = System.nanoTime();
    for (int i = a.size()-1; i >= 0; i--) {
         x = a.get(i) + 3;
    }
    end = System.nanoTime();
    delta += end - start;
}
System.out.println(Math.pow(10,-6) * delta / 1000 + \" milli seconds for [ int i = a.size()-1; i >= 0; i-- ] \");

start=0; end = 0; delta = 0;
for (int ctr = 0; ctr < 1000; ctr++) {
    start = System.currentTimeMillis();

    for (int i = 0; i < a.size(); i++) {
         x = a.get(i) + 3;
    }
    end = System.currentTimeMillis();
    delta += end - start;
}
System.out.println(Math.pow(10,-6) * delta / 1000 + \" milli seconds for [ int i = 0; i < a.size(); i++ ] \");        

System.out.println(x);
}
    ,这仅是通过示例来说明情况如何。 我测试了循环“ 25”的“ normal”执行和优化了循环“ 26”的微执行。我从命令行在Eclipse中运行了测试,并注意到了巨大的差异。 在Eclipse中运行的结果:
Time for Original: 32552 ms   Time for MicroOptimized 32707 ms
Fastest Loop: Original
Slowest loop takes 0.47616121897272057% more time
从命令行运行的结果:
Time for Original: 274489 ms   Time for MicroOptimized 30516 ms
Fastest Loop: MicroOptimized
Slowest loop takes 799.4920697339101% more time
因此,在eclipse中,两个for循环花费相同的时间,但是从命令行运行时,原始版本比微优化版本花费800%的时间。差异的幅度令人震惊。我想eclipse使用了不同的JVM,该JVM应用了一些智能优化技巧。 但是,这并不意味着您应该开始使用微优化版本。在几乎所有情况下,您要遍历的列表可能很小,因此性能差异可以忽略不计。而且,使用标准版本所获得的可读性比每个人都可以更快地认识和理解,这比不明显的性能提升更有利。 为了完整起见,这是我运行的代码:
public static void main(String[] args) {
        List<Byte> list = initializeList();
        byte value = 0;
        final int NUM_LOOPS = 100;

        long startOriginal,startOptimized,endOriginal,endOptimized;

        startOptimized = System.currentTimeMillis();
        for (int j = 0; j < NUM_LOOPS; j++) {
            for (int i = -1,size = list.size(); ++i < size;) {
                value = list.get(i);
            }
        }
        endOptimized = System.currentTimeMillis();

        startOriginal = System.currentTimeMillis();
        for (int j = 0; j < NUM_LOOPS; j++) {
            for (int i = 0; i < list.size(); i++) {
                value = list.get(i);
            }
        }
        endOriginal = System.currentTimeMillis();

        System.out.println(value);
        printResults(startOriginal,endOptimized);
    }

    private static void printResults(long startOriginal,long endOriginal,long startOptimized,long endOptimized) {

        long timeOriginal = endOriginal - startOriginal;
        long timeOptimized = endOptimized - startOptimized;

        long diff = Math.abs(timeOriginal - timeOptimized);
        long min = Math.min(timeOriginal,timeOptimized);

        System.out.println(\"Time for Original: \" + timeOriginal + \" ms\"
                + \"   Time for MicroOptimized \" + timeOptimized + \" ms\");

        System.out.println(\"Fastest Loop: \"
                + ((timeOriginal < timeOptimized) ? \"Original\"
                        : \"MicroOptimized\"));

        System.out.println(\"Slowest loop takes \" + ((double) 100 * diff / min)
                + \"% more time\");       
    }

    public static List<Byte> initializeList(){
        List<Byte> list = new ArrayList<Byte>();
        final Byte ONE = new Byte((byte) 1);

        for (int i = 0; i < Integer.MAX_VALUE / 10; i++) {
            list.add(ONE);
        }

        return list;
    }
}
    ,此外,如果您想知道将方法调用用作源集合是否会对性能产生任何影响。也就是说-该方法将被调用多次-答案是否定的。这是一个例子:
import java.util.*;
public class TestForeach {
    public static void main (String[] args) {

        for (String s : getStrings()) {
            System.out.println(\"The string was: \"+s);
        }
    } 

    private static List<String> getStrings() {
        System.out.println(\"IN GET STRINGS\");
        return Arrays.asList(\"A\",\"B\",\"C\");
    }
}
这将导致:
IN GET STRINGS
The string was: A
The string was: B
The string was: C
因此,该方法将仅被调用一次。     ,我的方法在这个问题上有点不同。对我来说,选择哪种方法都没关系。原因是您将获得“最佳性能”的最佳优化方法是250万次迭代〜50ms! (根据@David \的帖子)。显然,这种改进并不是您想浪费宝贵的时间来寻找优化的解决方案。 (但是,按照OP最初的问题,我也想建议最后一种方法。) 我知道答案有点怪异和罕见,但这是事实。     ,为了避免在编写代码时进行所有这些编号,迭代和检查,请使用以下具有最强性能的简单易读代码。为什么这具有最佳性能(细节即将出现)
for (Object object : aCollection) { 
// Do something here
}
如果需要索引,则: 在以上两种形式之间进行选择: 第二个更好,因为您正在使用局部变量进行检查。当退出该方法时,变量将进入堆栈的垃圾箱。     ,简单而有效
 for (ConfigDataModel.VisaTypesBean.AddedBean visatype : visaTypesBeans) {
                            if (visatype.getId() == 24) {

                            }
    ,任一个都会做。根据JVM的不同,第二个可能会快几个时钟周期,但这将是不可估量的或微不足道的差异。注意这些类型的子优化。除非您要构建一个实时系统,每个CPU滴答声都在其中计数,否则它们只会增加复杂性并增加错误来源。 我建议使用迭代器构造(正如已经建议的那样)
for (Flower flower: flowers) { ...
它清晰,灵活且可预测。     ,只去第一个,因为两者相同,但是在第二个代码片段中创建了一个多余的int变量。 所以去第一个代码片段
for (int i = 0; i < flowers.size(); i++) {
 ...
} 
    ,
String d = JOptionPane.showInputDialog(\"enter start\");
int s = Integer.parseInt(d);
String h = JOptionPane.showInputDialog(\"enter the end\");
int z = Integer.parseInt(h);
for (int a = 1 ; a<10 ; a++) { 
    if (a%2 == 0 ) {
        JOptionPane.showMessageDialog(null,a);
        System.out.print(a);
    }
}    
    

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐


依赖报错 idea导入项目后依赖报错,解决方案:https://blog.csdn.net/weixin_42420249/article/details/81191861 依赖版本报错:更换其他版本 无法下载依赖可参考:https://blog.csdn.net/weixin_42628809/a
错误1:代码生成器依赖和mybatis依赖冲突 启动项目时报错如下 2021-12-03 13:33:33.927 ERROR 7228 [ main] o.s.b.d.LoggingFailureAnalysisReporter : *************************** APPL
错误1:gradle项目控制台输出为乱码 # 解决方案:https://blog.csdn.net/weixin_43501566/article/details/112482302 # 在gradle-wrapper.properties 添加以下内容 org.gradle.jvmargs=-Df
错误还原:在查询的过程中,传入的workType为0时,该条件不起作用 &lt;select id=&quot;xxx&quot;&gt; SELECT di.id, di.name, di.work_type, di.updated... &lt;where&gt; &lt;if test=&qu
报错如下,gcc版本太低 ^ server.c:5346:31: 错误:‘struct redisServer’没有名为‘server_cpulist’的成员 redisSetCpuAffinity(server.server_cpulist); ^ server.c: 在函数‘hasActiveC
解决方案1 1、改项目中.idea/workspace.xml配置文件,增加dynamic.classpath参数 2、搜索PropertiesComponent,添加如下 &lt;property name=&quot;dynamic.classpath&quot; value=&quot;tru
删除根组件app.vue中的默认代码后报错:Module Error (from ./node_modules/eslint-loader/index.js): 解决方案:关闭ESlint代码检测,在项目根目录创建vue.config.js,在文件中添加 module.exports = { lin
查看spark默认的python版本 [root@master day27]# pyspark /home/software/spark-2.3.4-bin-hadoop2.7/conf/spark-env.sh: line 2: /usr/local/hadoop/bin/hadoop: No s
使用本地python环境可以成功执行 import pandas as pd import matplotlib.pyplot as plt # 设置字体 plt.rcParams[&#39;font.sans-serif&#39;] = [&#39;SimHei&#39;] # 能正确显示负号 p
错误1:Request method ‘DELETE‘ not supported 错误还原:controller层有一个接口,访问该接口时报错:Request method ‘DELETE‘ not supported 错误原因:没有接收到前端传入的参数,修改为如下 参考 错误2:cannot r
错误1:启动docker镜像时报错:Error response from daemon: driver failed programming external connectivity on endpoint quirky_allen 解决方法:重启docker -&gt; systemctl r
错误1:private field ‘xxx‘ is never assigned 按Altʾnter快捷键,选择第2项 参考:https://blog.csdn.net/shi_hong_fei_hei/article/details/88814070 错误2:启动时报错,不能找到主启动类 #
报错如下,通过源不能下载,最后警告pip需升级版本 Requirement already satisfied: pip in c:\users\ychen\appdata\local\programs\python\python310\lib\site-packages (22.0.4) Coll
错误1:maven打包报错 错误还原:使用maven打包项目时报错如下 [ERROR] Failed to execute goal org.apache.maven.plugins:maven-resources-plugin:3.2.0:resources (default-resources)
错误1:服务调用时报错 服务消费者模块assess通过openFeign调用服务提供者模块hires 如下为服务提供者模块hires的控制层接口 @RestController @RequestMapping(&quot;/hires&quot;) public class FeignControl
错误1:运行项目后报如下错误 解决方案 报错2:Failed to execute goal org.apache.maven.plugins:maven-compiler-plugin:3.8.1:compile (default-compile) on project sb 解决方案:在pom.
参考 错误原因 过滤器或拦截器在生效时,redisTemplate还没有注入 解决方案:在注入容器时就生效 @Component //项目运行时就注入Spring容器 public class RedisBean { @Resource private RedisTemplate&lt;String
使用vite构建项目报错 C:\Users\ychen\work&gt;npm init @vitejs/app @vitejs/create-app is deprecated, use npm init vite instead C:\Users\ychen\AppData\Local\npm-