java那种循环更快_关于java:哪个循环具有更好的性能? 为什么?

String s ="";

for(i=0;i<....>

s = some Assignment;

}

要么

for(i=0;i<..>

String s = some Assignment;

}

我不需要在循环外再次使用" s"。

第一个选项可能更好,因为不会每次都初始化新的String。 但是,第二个方法将导致变量的范围仅限于循环本身。

编辑:回应米尔豪斯的回答。 在循环中将String分配给常量是没有意义的吗? 不,这里的"某些分配"是指从要迭代的列表中获得的变化值。

另外,问题不是因为我担心内存管理。 只想知道哪个更好。

遍历指定为文字的字符串集合并不罕见。 例如,表的列标题可能被硬编码为String []。 但是重要的是,在两种情况下都会发生相同的分配,因此不会影响答案。

另一个评论:别忘了,如果您不打算更改s的值,则应将其声明为final。 许多Java程序员经常忘记这一切。

范围最好

使用第二个选项:

for ( ... ) {

String s = ...;

}

范围不影响性能

如果将每个编译器的代码反汇编(使用JDK的javap工具),您会发现在两种情况下,该循环都编译为完全相同的JVM指令。还要注意,Brian R. Bondy的"选项3"与选项1相同。使用更紧密的示波器时,不会在堆栈中添加或删除任何多余的东西,并且两种情况下在堆栈上都使用相同的数据。

避免过早初始化

两种情况之间的唯一区别是,在第一个示例中,变量s不必要地初始化。这是与变量声明的位置不同的问题。这会增加两个浪费的指令(以加载字符串常量并将其存储在堆栈框架插槽中)。一个好的静态分析工具会警告您,您永远不要读取分配给s的值,而好的JIT编译器可能会在运行时忽略它。

您可以简单地通过使用空声明(即String s;)来解决此问题,但这被认为是不好的做法,并且还会在下面讨论其他副作用。

通常,将像null这样的虚假值分配给变量只是为了掩盖编译器错误,该错误是在未初始化的情况下读取变量。该错误可以作为变量范围过大的暗示,并且在需要接收有效值之前就已声明了该错误。空声明会迫使您考虑每个代码路径。请勿通过分配虚假值来忽略此有价值的警告。

节省堆叠插槽

如前所述,尽管两种情况下的JVM指令都是相同的,但有一个细微的副作用,使它在JVM级别上最好使用尽可能有限的范围。这在该方法的"局部变量表"中可见。请考虑一下,如果您有多个循环,并且在不必要的大范围内声明了变量,会发生什么情况:

void x(String[] strings, Integer[] integers) {

String s;

for (int i = 0; i < strings.length; ++i) {

s = strings[0];

...

}

Integer n;

for (int i = 0; i < integers.length; ++i) {

n = integers[i];

...

}

}

变量s和n可以在它们各自的循环中声明,但是由于没有声明,因此编译器在堆栈帧中使用两个"槽"。如果在循环内声明它们,则编译器可以重用同一插槽,从而使堆栈帧更小。

真正重要的是

但是,这些问题大多数都不重要。一个好的JIT编译器将看到不可能读取您浪费分配的初始值,并且无法优化分配。在此处保存插槽,否则将无法建立或破坏您的应用程序。

重要的是使代码易于阅读且易于维护,在这方面,使用有限的范围显然更好。变量的范围越小,就越容易理解其用法以及对代码进行的更改将产生何种影响。

我仍然没有完全确信,但是我删除了答案,因为它是错误的,但仅供参考。这是我所拥有的:{//不删除花括号String s; for(i = 0; i 如果可以的话,我可以对这个评论投票两次。 ID也将问题标记为"早期优化"。

很好的答案;如果可以的话,Id也可以多次投票。

我以前没有玩过Javap,所以我检查了一个for循环,该循环获取日期并将String设置为date.toString。我进行的反汇编显示代码不同。您可以看到,在内部循环的每个循环中都设置了s。这是jit编译器可能解决的问题吗?

@Philip T.-我不确定我了解您的描述。如果您希望在循环的每次迭代中为String分配相同的值,并且您在询问是否可以"提升"该计算,是的,它是可能的,但是将取决于JVM。

我的问题是关于"范围不影响性能"部分的。出于好奇,我查看了实现示例的类的javap输出,该示例在循环内外定义了变量。 Javap表明代码是不同的,对于内部初始化版本,在循环内部有一个ldc和astore;对于外部初始化版本,该代码在循环之外。我试图找出是否遗漏了一些东西,因为普遍的共识似乎是代码不应有所不同。我检查了Eclipse和Netbeans中的类。

您是在原始帖子的第一个示例中谈论""到s的初始浪费分配吗?在循环的第一次迭代中被覆盖之前从未读取的值?这就是我在"避免过早初始化"部分中所说的; ldc和astore指令被浪费了,因为在重新分配变量之前永远无法读取该值。循环内的代码是相同的。变量的初始化(分配)是与变量范围分开的问题。

从理论上讲,在循环内部声明字符串是浪费资源的。

但是实际上,您呈现的两个代码片段都将编译为相同的代码(循环外的声明)。

因此,如果您的编译器进行了许多优化,则没有任何区别。

引用只是放在此方法调用的堆栈框架中,对吗?

@托马斯:对!

我认为您误解了1800 INFORMATION的内容。 Java字符串的不变性在这里无关紧要:无论String是否是不变的," some Assignment"都会每次生成一个新的String。

@Thomas:如果someAssignment只是一个硬编码的字符串怎么办?

已更新问题。分配硬编码字符串将是不合逻辑的。

它是哪种理论,它告诉您声明变量会浪费资源?

jrudolph:如果我没记错我的Java,创建一个新的空字符串会创建一个新的垃圾收集对象。

通常,我会选择第二个,因为's'变量的范围仅限于循环。好处:

这对于程序员来说更好,因为您不必担心在函数的后面某处会再次使用'

这对编译器更好,因为变量的范围较小,因此它有可能进行更多分析和优化

这对将来的读者来说更好,因为他们不会奇怪为什么如果以后不再使用's'变量在循环外声明

如果您想加快循环速度,我更喜欢在计数器旁边声明一个max变量,这样就不需要重复查找条件了:

代替

for (int i = 0; i < array.length; i++) {

Object next = array[i];

}

我更喜欢

for (int i = 0, max = array.lenth; i < max; i++) {

Object next = array[i];

}

已经提到了其他任何需要考虑的事情,所以只提到了我的两分钱(请参阅Ericksons帖子)

加的特Greetz

为了增加@Esteban Araya的答案,它们每次都需要在循环中都创建一个新的字符串(作为some Assignment表达式的返回值)。这些字符串都需要以任何一种方式进行垃圾收集。

我当时也这么想

不必要。我们不知道在循环内分配给" s"的内容。也许它是在PermGen空间中分配的字符串常量,永远不会被垃圾回收。我们所知道的是,无论它是什么,在两种情况下都是相同的,所以没关系。

@erickson:我同意它可以是字符串常量,但是在那种情况下,我希望编译器可能会使用常量传播将s移出循环体。我以为它不是一个常数,因为明智的程序员会做同样的事情。

我的意思不是每次迭代都使用相同的字符串常量。请参阅我对OP的评论。

是的,那样就不会有优化

我知道这是一个古老的问题,但是我想我要补充一点点。

我在浏览Java源代码时注意到,某些方法(例如String.contentEquals(在下面重复))使多余的局部变量成为类变量的副本。我相信在某处有一条评论,这意味着访问局部变量比访问类变量要快。

在这种情况下," v1"和" v2"似乎是不必要的,可以将其删除以简化代码,但可以添加它们以提高性能。

public boolean contentEquals(StringBuffer sb) {

synchronized(sb) {

if (count != sb.length())

return false;

char v1[] = value;

char v2[] = sb.getValue();

int i = offset;

int j = 0;

int n = count;

while (n-- != 0) {

if (v1[i++] != v2[j++])

return false;

}

}

return true;

}

在旧的VM上,这可能比现在更有用。我很难相信这对HotSpot有所帮助。

当我使用多个线程(50个以上)时,我发现这是一种处理鬼线程问题的有效方法,无法正确关闭进程....如果我错了,请告诉我原因我错了:

Process one;

BufferedInputStream two;

try{

one = Runtime.getRuntime().exec(command);

two = new BufferedInputStream(one.getInputStream());

}

}catch(e){

e.printstacktrace

}

finally{

//null to ensure they are erased

one = null;

two = null;

//nudge the gc

System.gc();

}

在我看来,我们需要对该问题进行更多说明。

s = some Assignment;

没有指定这是什么类型的任务。如果分配是

s ="" + i +"";

那么需要分配一个新的字符串。

但是如果是

s = some Constant;

s只会指向常量的存储位置,因此第一个版本的存储效率更高。

似乎我有点傻,不必担心对解释的语言恕我直言的for循环进行大量优化。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值