在Java中,在循环中多次实例化一个对象是不好的做法吗?
In Java, is it bad practice to instantiate an object multiple times in a loop?
我正在处理一个 Java 项目,一个变量在 while 循环内一遍又一遍地实例化为局部变量。
while ((inputLine = in.readLine()) != null){
Matcher matcher = pattern.matcher(inputLine);
isFound = matcher.find();
if(isFound){
break;
}
}
问题是,局部变量匹配器在 while 循环中每次都在实例化,直到循环终止。
我想知道,这会减慢处理时间吗?
在我看来,您使用 Matcher 对象的方式应该在循环外实例化然后使用。
一般来说,不会。很多时候,你需要做这样的事情。当然,您确实希望避免虚假和过多的堆栈分配,因为它们不是免费的,但是在许多非常好的情况下,您需要在循环中创建对象。
实例化和声明也有区别。在您的示例中, pattern.matcher(inputLine)
是您的对象实例化;您正在单独使用该语句创建一个新的 Matcher。 Matcher matcher = ...
是你的宣言。如果您问是否可以在一个循环中多次声明一个变量,这又取决于上下文。通常,这很好,实际上比在循环外声明变量更可取,因为该 Matcher 的每个实例的范围(大概)应该限于循环的单个迭代。这种范围机制可以帮助您避免以后在应用程序中产生错误。
视情况而定。
在这种特殊情况下,可以这样做。这是因为 inputLine
在循环的每次迭代中都在变化。如果不在每次迭代中创建一个新的 Matcher
,则无法执行此操作。此外,Matcher
的构造函数似乎就是这样做的:
Matcher(Pattern parent, CharSequence text) {
this.parentPattern = parent;
this.text = text;
// Allocate state storage
int parentGroupCount = Math.max(parent.capturingGroupCount, 10);
groups = new int[parentGroupCount * 2];
locals = new int[parent.localCount];
// Put fields into initial states
reset();
}
我不会说很多。
在其他可以避免创建实例的情况下,您应该避免创建实例。一个很好的例子是循环中的字符串连接。
示例:
String s = "";
for(int i = 0 ; i < 100 ; i++) {
s += Integer.toString(i); // string is immutable so this creates a new string every time.
}
您应该改用 StringBuilder
。
Javahere中进行模式匹配的规范模式是:
- 实例化模式
- 对于每个要测试的输入字符串 s,通过 .match(s) 创建一个匹配器 m,正如您所做的那样。
- 像您所做的那样,通过 Matcher 中的 m.matches()/find() 方法测试匹配项。
视情况而定。对于这种特殊情况,其他人已经解决了您的问题。一般来说,在循环中实例化一个对象通常(但并非总是如此)更好,这样它就可以在新生代集合中被清理掉。 Java 的 GC 针对短期对象进行了优化。
变量的作用域需要符合情境的逻辑,而不是一些教条"better"。对象可能比变量长寿,例如在一个循环中:
Result result = null;
for (int ix = 0; ix < limit; ++ix) {
Result candidate = new Result();
if (condition()) {
result = candidate;
break;
}
}
if (result != null) { ...
此处,candidate
变量在循环后超出范围。如果分配了一个非 null
值,result
变量将指向一个比其原始引用寿命更长的对象。所有其他候选人将有资格获得 GC。 (这个例子是人为设计的,但旨在说明这一点。)
例外情况是初始化开销很大,并且在循环内没有重复。如果一个对象的构造是巨大的,并且循环只改变了对象状态的一小部分而没有重复大量的工作,你可以考虑在循环外创建对象。
"massive" 有多大?很难知道。但是对象创建几乎是免费的,年轻代 GC 也几乎是免费的。死对象不会导致年轻 GC 开销。当对象被使用时,GC 开销会增加,因此您希望尽可能缩短生命周期。
生命周期管理是 Java 编程的黑魔法之一。不过,如果您喜欢尽快扔掉物品,那么您基本上不会出错。要偏离此经验法则,您必须衡量不同生命周期策略之间的差异。在某些情况下,将对象放置更长时间会显着提高性能,但如果您研究它们,您会发现这些结果是通过大量实验和准确的指标实现的。
我正在处理一个 Java 项目,一个变量在 while 循环内一遍又一遍地实例化为局部变量。
while ((inputLine = in.readLine()) != null){
Matcher matcher = pattern.matcher(inputLine);
isFound = matcher.find();
if(isFound){
break;
}
}
问题是,局部变量匹配器在 while 循环中每次都在实例化,直到循环终止。
我想知道,这会减慢处理时间吗?
在我看来,您使用 Matcher 对象的方式应该在循环外实例化然后使用。
一般来说,不会。很多时候,你需要做这样的事情。当然,您确实希望避免虚假和过多的堆栈分配,因为它们不是免费的,但是在许多非常好的情况下,您需要在循环中创建对象。
实例化和声明也有区别。在您的示例中, pattern.matcher(inputLine)
是您的对象实例化;您正在单独使用该语句创建一个新的 Matcher。 Matcher matcher = ...
是你的宣言。如果您问是否可以在一个循环中多次声明一个变量,这又取决于上下文。通常,这很好,实际上比在循环外声明变量更可取,因为该 Matcher 的每个实例的范围(大概)应该限于循环的单个迭代。这种范围机制可以帮助您避免以后在应用程序中产生错误。
视情况而定。
在这种特殊情况下,可以这样做。这是因为 inputLine
在循环的每次迭代中都在变化。如果不在每次迭代中创建一个新的 Matcher
,则无法执行此操作。此外,Matcher
的构造函数似乎就是这样做的:
Matcher(Pattern parent, CharSequence text) {
this.parentPattern = parent;
this.text = text;
// Allocate state storage
int parentGroupCount = Math.max(parent.capturingGroupCount, 10);
groups = new int[parentGroupCount * 2];
locals = new int[parent.localCount];
// Put fields into initial states
reset();
}
我不会说很多。
在其他可以避免创建实例的情况下,您应该避免创建实例。一个很好的例子是循环中的字符串连接。
示例:
String s = "";
for(int i = 0 ; i < 100 ; i++) {
s += Integer.toString(i); // string is immutable so this creates a new string every time.
}
您应该改用 StringBuilder
。
Javahere中进行模式匹配的规范模式是:
- 实例化模式
- 对于每个要测试的输入字符串 s,通过 .match(s) 创建一个匹配器 m,正如您所做的那样。
- 像您所做的那样,通过 Matcher 中的 m.matches()/find() 方法测试匹配项。
视情况而定。对于这种特殊情况,其他人已经解决了您的问题。一般来说,在循环中实例化一个对象通常(但并非总是如此)更好,这样它就可以在新生代集合中被清理掉。 Java 的 GC 针对短期对象进行了优化。
变量的作用域需要符合情境的逻辑,而不是一些教条"better"。对象可能比变量长寿,例如在一个循环中:
Result result = null;
for (int ix = 0; ix < limit; ++ix) {
Result candidate = new Result();
if (condition()) {
result = candidate;
break;
}
}
if (result != null) { ...
此处,candidate
变量在循环后超出范围。如果分配了一个非 null
值,result
变量将指向一个比其原始引用寿命更长的对象。所有其他候选人将有资格获得 GC。 (这个例子是人为设计的,但旨在说明这一点。)
例外情况是初始化开销很大,并且在循环内没有重复。如果一个对象的构造是巨大的,并且循环只改变了对象状态的一小部分而没有重复大量的工作,你可以考虑在循环外创建对象。
"massive" 有多大?很难知道。但是对象创建几乎是免费的,年轻代 GC 也几乎是免费的。死对象不会导致年轻 GC 开销。当对象被使用时,GC 开销会增加,因此您希望尽可能缩短生命周期。
生命周期管理是 Java 编程的黑魔法之一。不过,如果您喜欢尽快扔掉物品,那么您基本上不会出错。要偏离此经验法则,您必须衡量不同生命周期策略之间的差异。在某些情况下,将对象放置更长时间会显着提高性能,但如果您研究它们,您会发现这些结果是通过大量实验和准确的指标实现的。