精华内容
下载资源
问答
  • 循环外和循环内定义变量的区别

    千次阅读 2020-08-10 17:48:38
    具体的jvm知识可以查看我的这篇博客https://blog.csdn.net/bird_tp/article/details/88560468 四、循环内和循环外定义变量的本质区别 结合上面的堆、栈信息,我们可以明白变量的定义是放在栈里面的,变量的实例化...

    一、代码展示

    import java.util.HashMap;
    import java.util.Map;
    
    /**
     * @Package: raymond
     * @ClassName: Test
     * @Author: tanp
     * @Description: ${description}
     * @Date: 2020/8/10 14:12
     */
    public class Test {
    
        public static void main(String[] args) {
            TestThread2 testThread = new TestThread2();
            testThread.start();
        }
    
        static class TestThread extends Thread {
            @Override
            public void run() {
                for(int i=0; i<10; i++){
                    Map<String,Object> map = new HashMap<>();
                    map.put(i+"",i);
                }
            }
        }
    
        static class TestThread2 extends Thread {
            Map<String,Object> map;
            @Override
            public void run() {
                for(int i=0; i<10; i++){
                    map = new HashMap<>();
                    map.put(i+"",i);
                }
            }
        }
    }
    

    二、问题

    从上面的代码可以看到,TestThread和TestThread2的区别在于map的定义是放在for循环外面还是里面,那么就有一个问题,在写代码的时候到底是要将变量定义在外面还是里面呢。

    三、JVM运行时数据区

    首先我们要知道JVM运行时数据区中有几大块,其中的两块分别为栈和堆

    JVM

    JVM栈是线程私有的,每个线程创建的同时都会创建JVM栈,JVM栈中存放的为当前线程中局部基本类型的变量(java中定义的八种基本类型:boolean、char、byte、short、int、long、float、double)、部分的返回结果以及Stack Frame,非基本类型的对象在JVM栈上仅存放一个指向堆上的地址

    堆(Heap

    它是JVM用来存储对象实例以及数组值的区域,可以认为Java中所有通过new创建的对象的内存都在此分配,Heap中的对象的内存需要等待GC进行回收。

    (1)       堆是JVM中所有线程共享的,因此在其上进行对象内存的分配均需要进行加锁,这也导致了new对象的开销是比较大的

    (2)       Sun Hotspot JVM为了提升对象内存分配的效率,对于所创建的线程都会分配一块独立的空间TLAB(Thread Local Allocation Buffer),其大小由JVM根据运行的情况计算而得,在TLAB上分配对象时不需要加锁,因此JVM在给线程的对象分配内存时会尽量的在TLAB上分配,在这种情况下JVM中分配对象内存的性能和C基本是一样高效的,但如果对象过大的话则仍然是直接使用堆空间分配

    (3)       TLAB仅作用于新生代的Eden Space,因此在编写Java程序时,通常多个小的对象比大的对象分配起来更加高效。

    具体的jvm知识可以查看我的这篇博客https://blog.csdn.net/bird_tp/article/details/88560468

    四、循环内和循环外定义变量的本质区别

    结合上面的堆、栈信息,我们可以明白变量的定义是放在栈里面的,变量的实例化对象是放在堆里面的,所以我们前面两个线程中定义map的本质区别在于

    线程TestThread在栈声明了10个栈内存地址,10个堆内存地址,而TestThread2只声明了1个栈内存地址,10个堆内存地址。TestThread2线程在循环实例化map时,只是通过一个栈内存地址移动引用指向了的10个堆内存地址。

    总结起来就是
    1、在循环外面的定义的变量,只在栈中声明了一次;
    2、在循环里面的定义的变量,循环多少次就在栈中声明了多少次;

    五、循环时具体如何定义变量

    放在循环内部定义,确实会多次申请栈帧的内存空间,但其实话说回来,这样对性能的影响其实可以忽略不计,没什么大的问题。因为栈内存的分配非常之快,仅次于寄存器,所以,可以忽略不计。

    总的来说,循环内的话,每次循环内部的局部变量在每次进for循环的时候都要重新定义一遍变量,也就是执行申请内存空间,变量压入堆栈的过程。
    循环外定义的话,for循环一直用的是同一块内存空间,效率比较高,但是变量的作用域就大了,耗内存
    所以,其实内外都可以的,总之就是空间和时间的权衡,看实际情况了,局部变量的数据类型、大小什么的都有关

    下面粘贴下jdk7的hashmap的get方法,我们可以看到Object k就是定义在循环里面的。

    public V get(Object key) {
            if (key == null)
                return getForNullKey();
            int hash = hash(key.hashCode());
            for (Entry<K,V> e = table[indexFor(hash, table.length)];
                 e != null;
                 e = e.next) {
                Object k;
                if (e.hash == hash && ((k = e.key) == key || key.equals(k)))
                    return e.value;
            }
            return null;
        }

    六、总结

    在for循环外面和里面定义变量,对于内存,性能影响其实没有什么多大区别。看个人使用情况而言,对于我个人使用而言,还是习惯定义在外面

    展开全文
  • 我们会发现,当循环变量处于内循环和外循环时,其性能是不一样的,即便是运算结果一致。 我们知道不管是一维数组还是多维数组,在内存中都是线性排列的,以二维数组为例子,为了能将二维的数组拉成一维的,一般需要...

    ∇ \nabla 联系方式:

    e-mail: FesianXu@gmail.com

    QQ: 973926198

    github: https://github.com/FesianXu

    知乎专栏: 计算机视觉/计算机图形理论与应用

    微信公众号
    qrcode


    在图像处理相关的代码中,我们经常有类似于以下的代码,去遍历多维数组(张量)的每一个元素:

    #define LENGTH 10000
    void proc(){
    	uint8 datas[LENGTH][LENGTH];
    	int i, j;
    	long long sum = 0;
    	for (i = 0; i < LENGTH; i++){
    		for (j = 0; j < LENGTH; j++){
    			sum += datas[i][j];
    		}
    	}
    }
    

    其中的sum += datas[i][j];从语义上是和sum += datas[j][i];效果一致的,然而从性能上来说是否是一致的呢?答案是不是的,要看程序的编译优化程度。我们会发现,当循环变量处于内循环和外循环时,其性能是不一样的,即便是运算结果一致。


    我们知道不管是一维数组还是多维数组,在内存中都是线性排列的,以二维数组为例子,为了能将二维的数组拉成一维的,一般需要考虑编译器在编译代码时,其在内存中是行优先(row-major)排列还是列优先(colum-major)排列的。如下图所示,如果一个数组是行优先排列的,那么其在连续内存上的排列顺序如红色线的顺序。举个例子,比如现在有个数组int vars[3][3],如果是行优先排列的,那么有:(===的意思是等价, (vars+i)表示对以vars作为数组指针的前提下,偏移i个元素的地址,而*(vars+i)是对其进行取内容。)

    vars[0, 0] === *(vars+0);
    vars[0, 1] === *(vars+1);
    vars[1, 0] === *(vars+3);
    ==>
    vars[i, j] === *(vars+3*i+j)
    

    如果是列优先排列呢,则是

    vars[0, 0] === *(vars+0);
    vars[0, 1] === *(vars+1*3+1);
    vars[1, 0] === *(vars+0*3+1);
    ==>
    vars[i, j] === *(vars+3*j+i)
    

    在这里插入图片描述
    到这里为止都好理解,不过后续我们需要理解的一点是,我们现在跑得程序很多都不是在裸机上跑的。这里指的裸机就是没有操作系统的计算机,比如单片机等,或者是没有任何操作系统的其他CPU。在操作系统上跑程序,那么我们的程序的内存空间其实都是虚拟内存空间,是一种逻辑地址,其和物理的内存位置是没有必然关联的,需要受到操作系统的控制。采用虚拟内存有很多好处,其中最明显的就是:

    1. 不需要考虑不同程序之间的相对地址偏移,每个程序都有其独自的内存空间,其地址范围都是从0到系统定义的最大值max_mem
    2. 虚拟内存可以看成是一个巨大的线性内存空间,不需要考虑内存不足的情况,因为当内存不足的情况或者需要访问的数据不在内存上时,就发生了缺页错误(page fault),操作系统会自动进行“换页”(paging),将物理内存暂时不用了的内存页保存到存储空间巨大的硬盘上,然后把需要读取的内存加载到内存上。在这种情况下,可以把整个硬盘都看成是一个可以换入和切出的内存(虽然速度很慢,没法和真正的内存比)

    虚拟内存的细节太多,是操作系统设计的一个主要概念,其他细节需要参考其他书籍,比如[1,2]。我们在这里需要知道的是,我们程序中随时可能遇到数据在内存中找不到的事情发生,这个时候就会发生换页的操作,从硬盘中加载数据到内存(IO操作)是非常花时间的,因此很多程序的瓶颈都会在此。我们这里的关键点也正是在此。

    在一个以行优先排序的编译器上,每一行的数据在内存地址上都是比较接近的,而列数据的地址总是相差着sizeof(data_type) * N,其中 N N N是每一行的元素数量。这就导致每一行的数据可能是处于同一个内存页帧(page frame)上的,而每一个列的数据处于不同的页帧上。在以行优先排序的编译器上,如果外循环是列索引,内循环是行索引,有可能会发生频繁地进行缺页,换页的操作(准确地说是n*m次),严重影响程序的性能。当然,这里只是可能,具体情况和你的数组大小,系统的页大小设置等有关。(这里的缺页特别在物理内存比较小的时候更为严重)

    这里举个代码例子,说明性能上的差别。


    考虑代码:

    # code 1
    #define LENGTH 20000
    int main(){
    	float vector[LENGTH][LENGTH];
    	int i, j;
    	float sum = 0.0f;
    	for (i = 0; i < LENGTH; i++){
    		for (j = 0; j < LENGTH; j++){
    			sum += datas[i][j];
    		}
    	}
    	return 0;
    }
    
    # code 2
    #define LENGTH 20000
    int main(){
    	float vector[LENGTH][LENGTH];
    	int i, j;
    	float sum = 0.0f;
    	for (i = 0; i < LENGTH; i++){
    		for (j = 0; j < LENGTH; j++){
    			sum += datas[j][i];
    		}
    	}
    	return 0;
    }
    

    这两个代码除了索引的顺序不同(相当于内外循环调换)之外,其他别无差别。我们为了防止编译器对代码进行优化,影响分析,我们采用-O0编译参数以去掉任何gcc的优化。(其实用其他优化等级效果也是类似的,读者可以自行尝试,并且用指令gcc -O0 -S code1.c观察分析其汇编代码。)

    gcc -O0 code1.c
    /usr/bin/time -v ./a.out
    

    我们用/usr/bin/time分析程序的运行时间,我们有这两者的运行时间分别为:

    在这里插入图片描述
    code 1的运行时间为2.05s
    在这里插入图片描述
    而code 2的时间则变成了5.68s,性能明显差code 1很多。

    但是如果我们把数组的大小从20000改成200会怎么样呢?我们发现其性能将不会有明显区别了,就是因为尺寸大小的不同影响了页帧的换页过程。


    Reference

    [1]. Bryant R E, David Richard O H, David Richard O H. Computer systems: a programmer’s perspective[M]. Upper Saddle River: Prentice Hall, 2003.
    [2]. Tanenbaum A S. Structured computer organization[M]. Pearson Education India, 2016.

    展开全文
  • Java中for循环嵌套的内循环和外循环

    万次阅读 多人点赞 2018-08-05 15:14:25
    循环控制行数,内循环控制每一行的个数) 下面以冒泡排序为例说明: 第一个(外层)for循环作用:控制排序的轮数 第二个(内层)for循环作用:控制每一轮里的每一个比较步骤 代码: public c...

    关于for循环嵌套作如下解释:

          首先内层循环属于外层循环循环体的一部分,当循环体执行完以后外层循环才进入第二次循环,此过程中内层循环需要执行符合条件的完整循环。(外循环控制行数,内循环控制每一行的个数)

    下面以冒泡排序为例说明:

    • 第一个(外层)for循环作用:控制排序的轮数

    • 第二个(内层)for循环作用:控制每一轮里的每一个比较步骤

    代码:

    public class Test {
    
    	public static void main(String[] args) {
    
    		int[] array = { 7, 3, 10, 0, 6 };
    
    		// 共4轮排序,每轮都是把最大的元素排在后面
    		for (int i = 0; i < array.length - 1; i++) {
    
    			// 每轮排序中:需要比较的元素个数比上一轮少一个
    			for (int j = 0; j < array.length - i - 1; j++) {
    
    				// 中间变量,用于两个元素交换位置
    				int temp = 0;
    				if (array[j] > array[j + 1]) {
    
    					temp = array[j];
    					array[j] = array[j + 1];
    					array[j + 1] = temp;
    				}
    			}
    
    		}
    
    		// 遍历数组,打印元素
    		for (int i = 0; i < array.length; i++) {
    			System.out.print(array[i] + " ");
    		}
    	}
    }

    例1:循环打印下图1

    图1

    代码如下:

    public static void testFor() {
        int n=5;
        for(int i=1;i<=n;i++){
    	for(int j=1;j<=n-i;j++){
    	    System.out.print(" ");
    	}
    	for(int j=1;j<=2*i-1;j++){
    	    System.out.print("*");
    	}
    	System.out.println();
        }
    }
    
    public static void testFor1() {
        for (int i =1; i <=5; i++) {
                for(int j=5; j>=i; j--){
                    System.out.print(" ");
                }
                for(int j=1;j<=i*2-1;j++){
                    System.out.print("*");
                }
                System.out.println();
            }
    }

     

    例:循环打印tu 2

    图2

    代码如下:

    public class Test {
    
    	  public static void main(String[] args) {
    	        /*
    	         * 外层控制行数
    	         * 内层控制星号的个数
    	         */
    	        for (int i = 1; i <=5; i++) {
    	            for(int j=1;j<=i;j++){
    	                System.out.print("*");
    	            }
    	            System.out.println();
    	        }
    	    }
    }

     

    展开全文
  • 在C++里面,我们通常会这样写for循环:for(int i = 0;...}  首先是在循环中定义变量到底会不会存在重复分配的问题,这涉及到编译器的优化,不过主流编译器(如vsgcc)这一块优化都比较好,

      在C++里面,我们通常会这样写for循环:

    for(int i = 0;i<10;i++)
    {
         cout << i;      
    }

      还有另外一种写法:

    int i = 0;
    for(; i<10 ;i++)
    {
          cout << i;     
    }

      首先是在内层循环中定义变量到底会不会存在重复分配的问题,这涉及到编译器的优化,不过主流编译器(如vs和gcc)这一块优化都比较好,不会反复分配变量。函数的定义是编译器的事情,运行的时候不存在什么定义,更没有什么开销。

       除非是类对象或者结构体对象, 在for循环里面与外面, 开销可能会不一样.基本数据类型, 那是一样的, 编译器肯定会优化这个东西。

      一般来说, 在进入函数时, 所有的栈变量都分配好空间了。所以那个for变量写在哪里都是一样的. 具体你可以看一下反汇编代码, 全部就展现在你眼前了。

      自己在编程的时候要注意不要让上一次的结果影响到下一次循环,比如上一次 a = 3, 当下一次循环在给a赋值的时候出了错误,而你捕获了错误,却没修正a的值,程序继续执行,那么这时候a=3还成立,可能就会有问题了,如果是每次都重新定义,那就不存在这样的问题。

      最后总结:

      1、对于使用int等基本数据类型作为循环变量,只要你用的优化方面足够给力的主流的编译器,完全不需要关心在循环外还是循环内定义循环变量。

      2、如果循环变量本身是复杂的对象,建议在循环外定义好,并且在for循环的赋值语句、判断语句中,都要避免重复创建对象。

    展开全文
  • 分析: 在循环外创建一份对象,申请一份内存空间,然而在for循环内赋值,student只会保留最后一次的值。又因list保存的只是student的引用,也就是list中的student引用 指向不断被·更改,指向了 同一块内存区间,...
  • 循环取个名字
  • 跳出内循环,继续循环: def work(): #定义函数 for i in range(5): for j in range(5): for k in range(5): if k=3: break #跳出内循环,继续外面两层循环 else: print
  • 今天小编就为大家分享一篇解决Python层for循环如何break出外层的循环的问题,具有很好的参考价值,希望对大家有所帮助。一起跟随小编过来看看吧
  • 本文在指出此研究的目的、意义的基础上,提出了江津县社会经济生态系统运行的内循环循环的目标,以及实现良性循环的基本要求途径,对于搞活江津县经济提供了一种思路。
  • 跳出内循环,继续循环: def work(): #定义函数 for i in range(5): for j in range(5): for k in range(5): if k=3: break #跳...
  • int main() {  show();  char ch;     cin >> ch;  while (ch !...此时ch在外输入,运行时输入a,b,c,d程序进入死循环。改为 while(cin>>ch&&ch!='q') 程序正常运行。 点解
  • def main(): door_status = [4,5,5,4,5,5,5,4] lock_signal = 5 open_door = False for k in range(1,3): for x in range(5): door = door_status[k] if door == lock_signal: ...
  • 在学习python循环语句的时候,发现else竟然可以循环语句使用,但是它却与if中else语句的运行完全不同,有时候你真的感觉掉进这个else陷阱里了,完全不知道该怎么用,那么现在咱们一起... # 没有余数那么就跳出内循环
  • 总结下Random()方法在for循环内和外差别: 1创建一个Random()在for循环内的和一个在Random()在for循环外的方法。 2.再在主函数中调用这两个方法: 3.此次两个方法都是产生10个随机数,看下控制台输出结果...
  • python for循环内输出和外输出

    千次阅读 2018-07-30 15:58:30
    通过for循环求和,结果发现输出完全不一样,一个循环是输出每一步的结果,另一个循环是输出最终一次的结果,今天终于弄懂了。如下所示:
  • for循环内外定义变量的区别

    万次阅读 2019-05-22 22:06:24
    下面再测试纯粹基本类型在for循环内外定义的区别 for循环内部定义基本变量类型,每次执行for循环都重新申明变量,不覆盖之前的值,对于for循环外部定义的基本类型变量,代码如下 var indexOuter:int=-1; ...
  • 关于变量在for循环内外定义的思考 在c#或java里面,我们通常会这样写for循环: for(int i = 0;i10;i++) { Console.WriteLine(i); } 前一阵子,我突发奇想,如果按照下面这样写,会不会比上面的运行效率高...
  • 就是当层的for循环满足条件的时候,如何结束本次循环 ,同时,跳过或者结束本次外层循环。后来,在网上就查阅了一些资料,自己整理了下。 1.for…else… 搭配break 结束双重循环 代码如下: for i in range(1,...
  • 跳出一层:for(int i=0;...//跳出a循环,回到i循环 }}跳出两层:labe:for(int i=0;i for(int a=0;a break labe;//跳出整个循环 }} 跳出单层并且不执行外层的代码 boolean flag = false; for(int i=0;i
  • ++i与i++的区别是: ++i 是先执行 i=i+1 再使用 i 的值,而 i++ 是先使用 i 的值再执行 i=i+1(即++i是先加后用 i++是先用后加) 在这样的循环体for (int i=0; i&lt;10; i++){}for (int i=0; i&lt;10; ++...
  • 例如在循环内try catch: for(int i=0;i();i++){ try{ method(q.get(i)[0]); }catch(Exception e){ System.out.println(e.getMessage()); } finally{ continue; } } 只要不抛出异常,...
  • 递归和循环区别

    千次阅读 2019-05-11 22:26:52
    针对需要重复地多次计算相同的问题,通常可以选择...我们以计算1+2+3+......+n为例,我们可以采用递归和循环两种方式求出结果,对应的代码分别如下: 1、递归方法 int AddByRecursive(int n) { return n<=0 ...
  • 循环内创建对象和循环外创建对象

    千次阅读 2018-06-01 10:15:05
    对于在循环外创建对象比较好还是在循环内创建对象好问题,我认为应该具体情况具体分析:1、如果循环内没有需要将对象添加到集合等一些情况,则应该在循环外创建对象,这样可以节省内存,不用多次创建对象造成内存的...
  • Effective C++ 条款 26 有一段话大概是:当一个赋值成本低于一组构造+析构成本,定义在循环外(即A)比较高效,否则定义在循环内(即B)比较好。 我的问题是:哪些情况 赋值成本低于一组构造+析构成本?   ...
  • shell中使用管道会生成一个子shell,在子shell中使用while、for循环的代码也是在子shell中执行的,所以在循环中的修改的变量只在子shell中有效,当循环结束时,会回到主shell,子shell中修改的变量不会影响主shell中...
  • C语言while循环语句 do while语句 for循环语句

    万次阅读 多人点赞 2019-06-10 14:17:53
    一、循环结构的思想及意义: 知道了循环结构,那么在生活中也一样,我们每天都在重复做着相同的事情,例如:吸气呼气的过程;又如夏天开电扇,电扇一圈一圈的转,这都是在重复。现在大家玩个游戏,一个人A来说一个人...
  • 如何在内循环中使用break来终止外循环 ​ 原理其实很简单,我们可以分别给两个循环命名,然后在break之后添加想要终止的循环名称 代码结果如下: public class Hello { public static void main(String[] args) { ...
  • 写这篇文章的原因是我在问答平台看到的一个问题:C++循环中定义变量在外面定义比影响大吗?问题来自:http://ask.csdn.net/questions/176270 例如: for(int i=0;i;i++) { for(int j=0;j;j++); } ...
  • 循环内定义变量tempReportInfo ``` public ArrayList<ReportInfo> getAllReportInfos() { ArrayList<ReportInfo> reportInfos = new ArrayList(); Cursor cursor = null; synchronized (helper) { if ...

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 1,490,452
精华内容 596,180
关键字:

内循环和外循环的区别