In terms of style or performance, is it better to define variables within loops or outside of them?
For example:
int var;
for (...) {
var = /*something*/;
// Code that uses var
}
or
for (...) {
int var = /*something*/;
// Code that uses var
}
If you have any insight on how variable declarations work internally, and how one of these might perform better than the other (even if it's only slightly), please share. And outside of performance, which style is preferred?
Inside
for(int i = 0; i < array.length; i++) {
final String variable = array[i];
}
Keeps scope of variables limited.
Variable can be final
More readable (maybe)
Outside
String variable;
for(int i = 0; i < array.length; i++) {
variable = array[i];
}
Variable is accessible outside loop.
For Each
for(final String variable : array) {
}
Only allocates once (source needed)
Keeps scope of variable limited.
Looks frickin' awesome.
Performance
The following test was run. It takes approximately 30s to run. The results show that there is no difference in performance between defining the variable inside or outside of the loop. This is most likely due to compiler optimizations. YMMV.
final double MAX = 1e7;
long start = System.nanoTime();
String var1;
for(double i = 0; i < MAX; i++) {
var1 = UUID.randomUUID().toString();
}
System.out.println((System.nanoTime() - start) / 1e9);
start = System.nanoTime();
for(double i = 0; i < MAX; i++) {
final String var2 = UUID.randomUUID().toString();
}
System.out.println((System.nanoTime() - start) / 1e9);
Discussion of Style Preference: https://stackoverflow.com/a/8803806/1669208
You should define for loop initialization variables in for loop header only which limits its scope within the loop. If you are concerned about performance then you should define variables within the loop.
Define variable outside loop only if you are using value of that variable outside loop as well.
Well, depends on how the variable is intended to be used primarily, if you are defining a variable inside a loop you would need to initialize the variable before the first use of the variable and in every run of the loop the variable would be re-initialized to this value.
On the other hand if you want to value of the variable to persist among different runs in the loop then you have to declare it outside. I don't think performance and style could be the primary criteria here.
If you declare the variable inside the for loop you won't be able to access it outside the loop.
for (...) {
int var = /*something*/;
// Code that uses var
}
you cannot access var outside the loop.
int var;
for(...) {
var = /*something*/;
// Code that uses var
}
var can be accessed outside the loop. Any other class code can use the value of var being set in the for loop.
Bottom line it all depends on your requirements.
The only time you should try to move the variable outside the loop, even if it's never used outside the loop is if you can re-use an object, and avoid using new on every iteration of the loop.
StringBuilder builder = new StringBuilder();
for(...) {
builder.append(..);
builder.append(..);
strings[i] = builder.toString();
// reset builder, to be used again
builder.setLength(0);
}
This is much more efficient than making a new StringBuilder every time.
In all other cases, you should prefer to declare variables inside the loop if you can
Related
This question already has answers here:
Issue with Java 8 Lambda for effective final while incrementing counts
(3 answers)
Closed 4 years ago.
I am trying to count the number of iterations using Java lambda expressions, but it's throwing a compile time error:
Local variable count defined in an enclosing scope must be final or effectively final
public static void main(String[] args) {
List<String> names = new ArrayList<String>();
names.add("Ajeet");
names.add("Negan");
names.add("Aditya");
names.add("Steve");
int count = 0;
names.stream().forEach(s->
{
if(s.length()>6)
count++;
});
}
The statement count++; is reassigning the variable count.
This is not allowed in a lambda expression. A local variable referenced in a lambda must be final (or effectively final, which means that it is not explicitly declared final, but would not break if final were added)
The alternative is to use an atomic integer:
AtomicInteger count = new AtomicInteger();
names.stream().forEach(s -> {
if (s.length() > 6)
count.incrementAndGet();
});
Note that using an atomic integer should be used only if you need to modify the local variable. If all this is just for computing the count variable, then the approach in EliotFrish's answer is much better.
You can't access a non-final variable from an outer scope in a lambda. But you don't need to here, you can filter the stream based on your criteria and count that. Like,
long count = names.stream().filter(s -> s.length() > 6).count();
This answer is just here to provide a wider spectrum to overcome this error.
You can define your own consumer which counts:
class CountingConsumer implements Consumer<String>{
int count;
public void accept(String s){
if(s.length() > 6){
count++;
}
}
}
And then pass an instance of this new class to the consumer:
CountingConsumer countingConsumer = new CountingConsumer();
names.forEach(countingConsumer);
Which finally lets you extract the count after the iteration:
System.out.println(countingConsumer.count);
But an even better answer is just to use a normal for loop:
int count = 0;
for(String s : names){
if(s.length() > 6){
count++;
}
}
protected Day[] days= new Day[n];
for(int i=0;i<days.length; i++)
{
days[i]= new Day(5);
}
Above mentioned code works fine for me but modified for loop as mentioned below results in NullPointerException when I try to access the elements of the array. Can anyone explain why does it happens?
protected Day[] days= new Day[n];
for(Day d:days)
{
d= new Day(5);
}
When Java sees the enhanced for loop that you've made, it runs whatever you put inside it and makes a new variable (called d) and gives this variable a value of whatever is inside of your array. When you set d equal to a new Day(5); you are changing the value of the variable d, not the value inside the array. Here is a workaround:
protected D[] days = new Day [n];
for(int i = 0;i<days.length;i++)
days[i] = new Day(5);
This reaches into the actual array to set values. Hope this helps!
Loop variable in enhanced for loop is temporary. Assigning it inside loop body has no effect on the original item. Here is what happens to the loop according to Java Language Specification:
Day[] days = ...
for (int i = 0; i < days.length; i++) {
Day d = days[i];
...
}
When you assign d, it changes the local variable d, not days[i], which isnearly always an error. For that reason, some programming shops adopt a practice of making loop variable of enhanced for loop final:
for(final Day d:days) {
d= new Day(5); // <<== Compile-time error
}
If you want to shorten the code by avoiding the loop, use
Arrays.setAll(days, i -> new Day(5));
Second type of for uses Iterator for iterating by elements. Initializing reference d makes no sence, because this operation doesn't change reference inside your array.
I don't understand why this works and I hope somebody can explain it to me.
Here is an example:
TestObject array[] = new TestObject[10];
for(int i= 0; i <= 10; i++){
TestObject object = new TestObject();
object.setValue(i);
array[i] = object;
System.out.println(array[i].getObject());
}
Why can I create multiple instances of "TestObject" with the same name in the loop?
Usually you can't create instances with the same name:
TestObject o = new TestObject(1);
TestObject o = new TestObject(2);
Well, this will obviously throws an error...
The scope for a for loop is limited to the iteration. So TestObject object is created and destroyed in each iteration.
Every iteration of a loop is a block and, as a block, has its own scope. You can achieve the same result by doing this:
{
int i = 0;
}
{
int i = 1;
}
// etc
This is because 'object' is in visibility scope of current loop iteration, so for next iteration, there can be initialized a new one with the same name (other scope).
it's the scope of object problem. every iteration has its scope let's say they are not the same object at all
In the expression of a while loop, is it possible to initialise a variable, then use that as part of the expression?
It's probably simpler in code:
while (int a = someMethod(), a<b)
It would be possible to just add another method, and so have to following:
private boolean whileLoopTest() {
int a = someMethod();
return a<b;
}
public void originalMethod() {
while (whileLoopTest()) {
//...
but this doesn't seem as neat.
EDIT
I also don't want to directly compare the method to my variable, as it is compared to several variable, and so if would be a long, unreadable mess. A better example of what I want would be:
while (int a = SomeClass.someStaticMethod(), -1<a && a<b)
It's not true in my case, but this would be a equally valid question if someStaticMethod() took a long time to return - I would only want to call it once.
I'm fairly new to StackOverflow, so I'm not sure if giving other situations where this would apply is what I should be doing.
int a;
while((a = someMethod()) < b){
//do something
}
A common use for this is reading from a file:
BufferedReader fileIn = ...
String line;
while((line = fileIn.readLine()) != null){
//do something
}
fileIn.close();
/edit
You can do this for your new scenario:
int a;
while(-1 < (a = SomeClass.staticMethod()) && a < b) {
//do something
}
Once the left hand portion of the && statement is executed, the return value of SomeClass.staticMethod() is stored in a, which carries over the the right hand portion of the statement.
Why not just not assign the value to "a" if you are not using it anyways?
while (someMethod() < b) { doSomething() }
If you actually do need "a" then your alternate solution would not work. The solution then would be either to save it (which I do not consider unneat) or what Kerrek said.
You can use the function directly without using a local variable like this:
while ( someMethod() < b) { /* ... */}
This, if your method returns intended value. (If you are casting it to a local variable, it's supposed to)
EDIT: For your second question.
Your concern is understandable, but if you are assigning that methods value to a local variable inside while loop's boolean expression, in every loop where "While" checks the expression, you are assigning methods' return value to local variable, which means you are calling that method in every iteration. That doesn't change anything from my first answer.
I was optimizing an application and wanted to change my for loops to enhanced loops:
From:
for (int m = 1;m < MAX_BEREN;m++)
{
Wasberen[m] = new Wasbeer();
Wasberen[m].YYY = r.nextInt(SchermY - 28);
}
to:
for (Wasbeer a : Wasberen)
{
if (a!=null)
{
a = new Wasbeer();
a.YYY = r.nextInt(SchermY - 28);
}
}
I get a NullPointerException, because it probably doesnt know how much 'beren' can be in
the array, but I'm not sure how to manage the same as the loop above (MAX_BEREN = 11).
If the array reference ('Wasberen' in this case) in an enhanced for statement is null, then a NullPointerException will result when the statement is executed.
For initializing arrays, you should stick to the syntax you had before.
You can't use the enhanced for-loop in Java to fill an array. (I'm assuming your Wasberen array was already created before - if not, this will get you a NullPointerException in both variants.)
Your code (simplified)
for (Wasbeer a : Wasberen)
{
a = ...;
}
is equivalent to
for (int i = 0; i < Wasberen.length; i++)
{
Wasbeer a = Wasberen[i];
a = ...;
}
This assignment will change the local variable a, but will have no effect on the contents of the array.