In Java it is possible to declare a variable in the initialization part of a for-loop:
for ( int i=0; i < 10; i++) {
// do something (with i)
}
But with the while statement this seems not to be possible.
Quite often I see code like this, when the conditional for the while loop needs to be updated after every iteration:
List<Object> processables = retrieveProcessableItems(..); // initial fetch
while (processables.size() > 0) {
// process items
processables = retrieveProcessableItems(..); // update
}
Here on stackoverflow I found at least a solution to prevent the duplicate code of fetching the processable items:
List<Object> processables;
while ((processables = retrieveProcessableItems(..)).size() > 0) {
// process items
}
But the variable still has to be declared outside the while-loop.
So, as I want to keep my variable scopes clean, is it possible to declare a variable within the while conditional, or is there some other solution for such cases?
You can write a while loop using a for loop:
while (condition) { ... }
is the same as
for (; condition; ) { ... }
since all three bits in the brackets of the basic for statement declaration are optional:
BasicForStatement:
for ( [ForInit] ; [Expression] ; [ForUpdate] ) Statement
Similarly, you can just rewrite your while loop as a for loop:
for (List<Object> processables;
(processables = retrieveProcessableItems(..)).size() > 0;) {
// ... Process items.
}
Note that some static analysis tools (e.g. eclipse 4.5) might demand that an initial value is assigned to processables, e.g. List<Object> processables = null. This is incorrect, according to JLS; my version of javac does not complain if the variable is left initially unassigned.
No it's not possible.
It doesn't really make too much sense either: unlike a for loop where you can set up the initial state of the "looping variable", in a while loop you test the value of an existing variable, akin to the conditional check of the for loop.
Of course, if you're concerned about variables "leaking" into other parts of your code, you could enclose the whole thing in an extra scope block:
{
/*declare variable here*/
while(...){...}
}
Alternatively, convert the while loop into a for loop.
Make a do/while:
String value;
do {
value = getValue();
...your processing
} while (value != null && !value.isEmpty());
Related
have a list of bean and I want to manipulate by its index and I tried below way, is there any other way of doing this which is easier and generic?
List<UserBean> resultBean = query.setFirstResult(offset).setMaxResults(limit).getResultList();
for (int i = 0; i < resultBean.size(); i++) {
resultBean.get(i).setChabi(encode(decyptChabi(resultBean.get(i).getChabi())));
}
As you can see from the Java Language Specification (JLS), 14.14, there are two kinds of for loops. The basic for loop, which uses an index, and the enhanced for loop, which doesn't.
You used the basic for loop but violated the DRY (Don't Repeat Yourself) principle in that you're calling resultBean.get(i) twice. To cure that, you can introduce a variable which makes the code much more readable:
for (int i = 0; i < resultBean.size(); i++) {
UserBean user = resultBean.get(i);
user.setChabi(encode(decryptChabi(user.getChabi())));
}
In your example, you don't even need the index variable, so you can replace the basic for loop with an enhanced for loop which would be even more concise:
for (UserBean user : resultBean) {
user.setChabi(encode(decryptChabi(user.getChabi())));
}
Whatever you do, prefer code that is easy to read over code that is fast/easy to write.
Why do I get this vexing "Variable not initialized" compiler error, when this variable will have been initialized in any case ?
File[] files;
final boolean doItFirst = evaluateOnce();
if (doItFirst)
files = listFiles();
changeDirectory();
if (!doItFirst)
files = listFiles();
// next line is flagged error by the java 7 compiler, although files as in any case been initialized
if (files != null && files.length > 0 ) {
// ...
}
The technical reason is to do with the JLS rules on definite assignment for if statements; see JLS 16.2.7.
These rules (and the others) state that v is not definitely assigned in
if (c) {
v = 1;
}
if (!c) {
v = 2;
}
but it is definitely assigned in:
if (c) {
v = 1;
} else {
v = 2;
}
The bottom line is that Java decides whether a variable "may not be initialized" based on the application of some simple rules, not on logical deduction.
(The language is specified this way to avoid the need for the compiler to implement potentially complex and expensive logical inference. This also avoids hypothetical portability problems if one Java compiler did a better job of inference than another one.)
For what it is worth, I would code the relevant part of your example as:
boolean doItFirst = evaluateOnce();
if (doItFirst) { // or get rid of the temporary variable
files = listFiles();
changeDirectory();
} else {
changeDirectory();
files = listFiles();
}
IMO, this is better than pre-initializing files to a dummy value because it is robust against changes to the code that accidentally cause the actual initialization to be removed.
The compiler can tell that those two if statements each tries to assign to files, but it does not recognise that the two conditions are complementary. Regard it as a compiler limitation if you want.
Here's a suggestion:
if (!doItFirst) {
changeDirectory();
}
files = listFiles();
if (doItFirst) {
changeDirectory();
}
This way the sequence of method calls is the same, but your files initialisation is out of the if statements.
The compiler doesn't know that if (doItFirst) and if (!doItFirst) are complementary (as far as the compiler can tell, the value of doItFirst could have changed after the first condition and before the second). Therefore it can't be sure your variable will be initialized in all scenarios.
If you use an if-else condition, you would avoid this problem.
For example:
if (doItFirst) {
files = listFiles();
} else {
changeDirectory();
files = listFiles();
}
I wasn't sure where to put changeDirectory(), since I don't know how it affects the listFiles() calls. If you want to call changeDirectory() in both cases, you might have to call it in both the if and else clauses (or put it before or after the conditions).
You have to initialise your array :
File[] files = null;
Yes it is logic that you think that no need to initialise your array, but this is not correct, you are using files = listFiles(); just in if block, without else, for that the compilator ask you to initialise you array.
if (doItFirst) {//if the condition is correct
files = listFiles();//then your array will be desalinized
}//else your array is not initialise <<--------here is the problem
In the conventional loop, we could have as below, making single nested layer.
for (int i=0; listObject != null && i < listObject.size(); i++) {
// Do whatever we want
}
However, using the below style for each loop, I'll need a double nested code: -
if (listObject != null) {
for (Object object: listObject) {
// Do whatever we want
}
}
Is it possible to embed the listObject != null condition into the for-loop statement to make it single nested code?
Your second example is clear, easily understood code. There is nothing wrong with nesting a for loop in an if block. It's even more clear than your first example.
But if you insist on combining them, you can use the ternary operator to supply an empty list if listObject is null. Using Collections.emptyList means no iterations will take place and no NullPointerException will be thrown.
for (Object object : listObject == null ? Collections.emptyList() : listObject)
I don't think I would use code like this when a clear example such as your second example already exists, but this code does provide a way to get two colons inside a for loop.
To make it concise on, while having a single nested loop, I decided to make it into function as below
void checkCondition(List<Object> listObject) {
if (listObject == null) return;
for (Object object: listObject) {
// Do whatever
}
}
if have the following problem:
I have a List which i am going through using the enhanced for loop. Every time i want to remove sth, out of the list, i get a ConcurrentModificationException. I already found out why this exception is thrown, but i don`t know how i can modify my code, so that its working. This is my code:
for(Subject s : SerData.schedule)
{
//Checking of the class is already existing
for(Classes c : s.classes)
{
if(c.day == day &c.which_class == which_class)
{
int index = getclassesindex(s.classes, new Classes(day, which_class));
synchronized (s) {
s.classes.remove(index);
}
}
}
//More code....
}
I also tried out this implementation.
for(Subject s : SerData.schedule)
{
//Checking of the class is already existing
Iterator<Classes> x = s.classes.iterator();
while(x.hasNext())
{
Classes c = x.next();
if(c.day == day &c.which_class == which_class)
{
int index = getclassesindex(s.classes, new Classes(day, which_class));
synchronized (s) {
s.classes.remove(index);
}
}
}
//More code....
}
not working either...
Is there a common used, standard solution? (Hopefully sth. that is not obvious :D )
The main reason this issue occurs is because of the semantic meaning of your for-each loop.
When you use for-each loops, the data structure that is being traversed cannot be modified.
Essentially anything of this form will throw this exception:
for( Object o : objCollection )
{
// ...
if ( satisfiesSomeProperty ( o ) )
objList.remove(o); // This is an error!!
// ...
}
As a side note, you can't add or replace elements in the collection either.
There are a few ways to perform this operation.
One way is to use an iterator and call the remove() method when the object is to be removed.
Iterator <Object> objItr = objCollection.iterator();
while(objItr.hasNext())
{
Object o = objItr.next();
// ...
if ( satifiesSomeProperty ( o ) )
objItr.remove(); // This is okay
// ...
}
This option has the property that removal of the object is done in time proportional to the iterator's remove method.
The next option is to store the objects you want to remove, and then remove them after traversing the list. This may be useful in situations where removal during iteration may produce inconsistent results.
Collection <Object> objsToRemove = // ...
for( Object o : objCollection )
{
// ...
if ( satisfiesSomeProperty ( o ) )
objsToRemove.add (o);
// ...
}
objCollection.removeAll ( objsToRemove );
These two methods work for general Collection types, but for lists, you could use a standard for loop and walk the list from the end of the list to the front, removing what you please.
for (int i = objList.size() - 1; i >= 0; i--)
{
Object o = objList.get(i);
// ...
if ( satisfiesSomeProperty(o) )
objList.remove(i);
// ...
}
Walking in the normal direction and removing could also be done, but you would have to take care of how incrementation occurs; specifically, you don't want to increment i when you remove, since the next element is shifted down to the same index.
for (int i = 0; i < objList.size(); i++)
{
Object o = objList.get(i);
// ...
if ( satisfiesSomeProperty(o) )
{
objList.remove(i);
i--;
}
//caveat: only works if you don't use `i` later here
// ...
}
Hope this provides a good overview of the concepts and helps!
Using Iterator.remove() should prevent the exception from being thrown.
Hm if I get it right you are iterating over a collection of classes and if a given class matches some criteria you are looking for the its index and try to remove it?
Why not just do:
Iterator<Classes> x = s.classes.iterator();
while(x.hasNext()){
Classes c = x.next();
if(c.day == day && c.which_class == which_class) {
x.remove();
}
}
Add synchronization if need be (but I would prefer a concurrent collection if I were you), preferably change the "==" to equals(), add getters/setters etc. Also the convention in java is to name variables and methods using camelCase (and not separating them with "_").
Actually this is one of the cases when you have to use an iterator.
From the javadoc on ConcurrentModificationException:
"if a thread modifies a collection directly while it is iterating over the collection with a fail-fast iterator, the iterator will throw this exception."
So within your
for (Classes c : s.classes)
you are executing
s.classes.remove(index)
and the iterator is doing just what its contract says. Declare the index(es) in a scope outside the loop and remove your target after the loop is done.
Iterator<Classes> classesIterator = s.classes.iterator();
while (classesIterator.hasNext()) {
Classes c = classesIterator.next();
if (c.day == day && c.which_class == which_class) {
classesIterator.remove();
}
}
There is no general solution for Collection subclasses in general - most iterators will become invalid if the collection is modified, unless the modification happens through the iterator itself via Iterator.remove().
There is a potential solution when it comes to List implementations: the List interface has index-based add/get/set/remove operations. Rather than use an Iterator instance, you can iterate through the list explicitly with a counter-based loop, much like with arrays. You should take care, however, to update the loop counter appropriately when inserting or deleting elements.
Your for-each iterator is fail-fast and this is why remove operation fails as it would change the collection while traversing it.
What implementation of List interface are you using?
Noticed synchronisation on Subject, are you using this code concurrently?
If concurrency is the case, then I would recommend using CopyOnWriteArrayList. It doesn't need synchronisation and its for-each iterator doesn't throw ConcurrentModificationException.
A while back, I was working on a program that hashed values into a hashtable (I don't remember the specifics, and the specifics themselves are irrelevant to the question at hand). Anyway, I had the following code as part of a "recordInput" method:
tempElement = new hashElement(someInt);
while(in.hasNext() == true)
{
int firstVal = in.nextInt();
if (firstVal == -911)
{
break;
}
tempElement.setKeyValue(firstVal, 0);
for(int i = 1; i<numKeyValues;i++)
{
tempElement.setKeyValue(in.nextInt(), i);
}
elementArray[placeValue] = tempElement;
placeValue++;
} // close while loop
} // close method
This part of the code was giving me a very nasty bug -- no matter how I finagled it, no matter what input I gave the program, it would always produce an array full of only a single value -- the last one.
The problem, as I later determined it, was that because I had not created the tempElement variable within the loop, and because values were not being assigned to elementArray[] until after the loop had ended -- every term was defined rather as "tempElement" -- when the loop terminated, every slot in the array was filled with the last value tempElement had taken.
I was able to fix this bug by moving the declaration of tempElement within the while loop. My question to you, Stackoverflow, is whether there is another (read: better) way to avoid this bug while keeping the variable declaration of tempElement outside the while loop.
Why would you want to keep the variable declaration outside the while loop? Anyway, you can, as long as you assign it to a new hashElement each time:
hashElement tempElement;
while (/*...*/) {
tempElement = new hashElement();
//...
It's certainly not "better" though. Scope your variables as narrowly as possible, in general.
This is not about declaration of the variable, but about the objects you create. Arrays in java only hold references to objects, so if you actually want to have distinct objects in the array, you need to create them with new somewhere in the loop.
tempElement = new WhateverClass();
Element tempElement;
while(condition){
tempElement = new HashElement();
//do more stuff
elementArray[index] = tempElement;
}