How to break out of nested loops - java

I'm currently making a program where nested loops are needed to search through an array to find a spot for good input within the array. Here is an example:
public void placePiece(int column) {
boolean goodInput = false;
while(!goodInput) {
for(int x = 5; x >= 0; x--) {
if(boardStatus[x][column] == 0) {
setRow(x);
boardStatus[x][column] = 1;
goodInput = true;
break;
}else if(boardStatus[0][column] == 1) {
goodInput = false;
break;
}else{
}
}
}
}
The method takes a parameter which is the column in which the piece should be located (received by a mouse listener). If the column in the 2D array is already filled to the top, the program gets stuck in an endless loop within the "else if", and I'm unsure how I would break out of this loop. How could I break out of this loop if there is bad input so that the user can try to give another column as input.

An easy way is to use a labeled break statement.
myLabelName:
for(outer loop)
for(inner loop)
if(condition)
break myLabelName;
This is useful when you'd rather not waste time iterating over other objects/items when you've found what you needed.
To expand, when you use just break; (without a label) it will exit the parent loop.
Ex:
myLabelName:
for(outer loop){
for(inner loop){
if(condition){
break myLabelName; //breaks outer loop
}
else if(other condition){
break; //breaks parent (inner/nested) loop
}
}
}

When you are in elseif loop your are again assigning the goodInput = false; so the while conditions again becomes true and loops continuous forever.
if you want to take input till correct input is received can use do(){}while() loop

Based on the description you provided, I am not sure of the purpose of the else if statement. As I understand you intention, you are checking all of the rows within a certain column to see if that cell has been set or not?
The combination of the while loop and the for loop seems suspect as well. There doesn't seem to be any need for the while loop in this case.
What is supposed to happen if the entire column is filled? Should this method return a status flag?
Can't this be implemented with a single for loop?
for (int x = 5; x >= 0; --x)
{
if(boardStatus[x][column] == 0) {
setRow(x);
boardStatus[x][column] = 1;
break;
}
}
If you want, you can track a flag variable to indicate if you ever found a cell which was empty.

you can use flag or you can use goto statement
flag description
code
bool flag = true;
for (int i = 0; (i < 10) && (flag==true); i++)
{
for(int j = 0; (j < 10) && (flag==true); j++)
{
if (condition)
flag = false;
}
}

Change the parameter AFTER the loop:
public void placePiece(int column) {
boolean goodInput = false;
while(!goodInput) {
for(int x = 5; x >= 0; x--) {
if(boardStatus[x][column] == 0) {
setRow(x);
boardStatus[x][column] = 1;
goodInput = true;
break;
}else if(boardStatus[0][column] == 1) {
goodInput = false;
break;
}else{
}
}
goodInput = true; // <-----
}
}

Related

Java Loops - Break? Continue?

I've read through a bunch of threads on using break and continue and I suspect the problem isn't necessarily my use of those, but the layout of my loops. In the following code, I am trying to iterate through the chars in a string that is input by the user to find any - symbols. If found, it will throw an error to the user that a negative number was found and exit. Otherwise, if it does not find a - symbol, it should print out all of the chars in the string.
I used break at the end of my first loop to find the - symbol, but it is not continuing on to the next loop. I tried continue as well but that didn't work. Loops are new to me so I may have this completely wrong, all I know is my first loop is working OK and will throw the error when it finds a - in the string.
strNum1 = JOptionPane.showInputDialog ("Enter Number String");
for (int i = 0; i < strNum1.length(); i++) {
char c = strNum1.charAt(i);
if (c == '-') {
System.out.println("Negative Digit Found - Exiting");
break;
}
}
for (int i = 0; i < strNum1.length(); i++) {
char c = strNum1.charAt(i);
if (c <= 9) {
System.out.println(c);
}
}
The break statement breaks you only from the first loop. In order to skip running the second loop in the event of finding a - character, you can use some boolean variable to indicate whether the second loop should run :
strNum1 = JOptionPane.showInputDialog ("Enter Number String");
boolean isValid = true;
for (int i=0; i<strNum1.length(); i++) {
char c = strNum1.charAt(i);
if (c == '-'){
System.out.println("Negative Digit Found - Exiting");
isValid = false;
break;
}
}
if (isValid) {
for (int i=0; i<strNum1.length(); i++) {
char c = strNum1.charAt(i);
if (c <= '9'){
System.out.println(c);
}
}
}
If you replace the break with a return it will exit the whole method. It sounds like this is probably what you want.
'break;' will stop the loop that it is in from running, where 'continue;' will skip the current 'iteration' in the loop.
for(int x = 0; x < 10; x++)
{
if (x == 5)
break;
// other code
}
// more other code
This will exit the loop once x == 5, and not do the 6th through 10th iterations.
for(int x = 0; x < 10; x++)
{
if (x == 5)
break;
// other code
}
// more other code
This will do every iteration, besides the 6th iteration.
But if you want to skip the '// more other code', then you would need to use a 'return;', provided your code is in a function, and it will skip the rest of the function, which in this case is the '// more other code'.
Use the return statement instead of break if you dont want to execcute the second loop after the first one.
You don't say if the number should be an integer, so I'm assuming yes.
If so, instead of using loops, a better way of validating the input would be:
int num1;
try {
num1 = Integer.parseInt(strNum1);
catch (NumberFormatException e) {
//not a valid number, complain about it
}
if (num1<0) {
//negative number not allowed, complain about it
}
for (int i=0; i<strNum1.length(); i++) {
char c = strNum1.charAt(i);
if (c == '-'){
System.out.println("Negative Digit Found - Exiting");
break;
}
}
can be modified as
if(strNum1.charAt(0) != '-'){
for (int i=0; i<strNum1.length(); i++) {
char c = strNum1.charAt(i);
if (c <= 9){
System.out.println(c);
}
}
}
else {
//negative number found...
}
In This way, unnecessary for loop and break statements can be avoided
All answers are good, but if you want to repeat prompt until you get a valid value,
you will need another loop for that, using labels:
negative: while(true) {
strNum1 = JOptionPane.showInputDialog ("Enter Number String");
for (int i=0; i<strNum1.length(); i++) {
char c = strNum1.charAt(i);
if (c == '-'){
System.out.println("Negative Digit Found - Exiting");
continue negative;
}
break negative;
}
}

What is wrong with this method using an int[][]?

This method is supposed to return true if there is more than one 1 in a column of a 2D array, yet it doesn't work. I can't figure out what's wrong with it so I thought I'd get some expert opinions.
Example:
10010
01001
10100
will return true because there are 2 ones in the first column.
Here is the code
public static boolean isVert(int[][] x) { //checks for more than one 1 in columns
int count = 0;
boolean break2 = false;
boolean check = false; //false means no 2 (or more) queens in same column
for (int i = 0; i < x.length; i++) {
count = 0;
for (int j = 0; j < x[i].length; j++) {
if (x[i][j] == 1) {
count++;
}
if (count > 1) {
break2 = true;
check = true;
break;
}
}
if (break2) {
break;
}
}
return check;
}
You break at the first occurance of 1 in whole array, which is probably not the expected result.
Explanation of how your code works:
loop until counter i is less than length of array (number of rows in array)
loop until counter j is less than length of i-th row (number of columns or elements in array)
check if element on i-th row and j-th column is 1, if true, increase variable count by one
if count is greater than 1 (this means it has to be 2 or greater) set break2 and check to true, break
if break2 is true (which is as count is > 2 and first loop breaks), break this loop too:
this happens in 1st row of your example table
end of loops, return check (which is true because 1st row contains 2 ones)
The problem in your code is that you break when you find your first row that satisfies your condition, but you do not (necessarily) check all the rows in given array.
I have corrected the code for you, hopefully it works (untested)
public static boolean isVert(int[][] x) { //checks for more than one 1 in columns
int count = 0;
for (int i = 0; i < x.length; i++) {
int rowCount = 0;
for (int j = 0; j < x[i].length; j++) {
if (x[i][j] == 1) {
rowCount++;
}
if(rowCount > 1) {
count++;
break;
}
}
}
// returns true if count of lines containing 1 equals length of array,
// if not, returns false
return count == x.length;
}
Start of by improving your naming convention. Your code has many variables named by their contents, instead of named by how they are used. For example:
boolean check = false; // false means no two queens in the same column.
instead of
boolean twoQueensInColumn = false;
and the other example
boolean break2 = false;
instead of the more reasonable
boolean continueLooking = true;
Plus, it is a very good idea to avoid using variables as place holders for loop escaping logic. For example, the two stanzas
...
if (count > 1) {
break2 = true;
check = true;
break;
}
}
if (break2) {
break;
}
are a breeding ground for bugs, requiring a lot of debugging to ensure they work "right now" which will break just as soon as you modify the code. Much better would be
boolean keepLooking = false;
for (int row = 0; keepLooking && (row < board.length); row++) {
int queensInColumn = 0;
for (int column = 0; keepLooking && (column < board[row].length, column++) {
if (board[row][column] != 0) {
queensInColumn++;
}
if (queensInColumn > 1) {
keepLooking = false;
}
}
}
The main difference being the control logic is in the loop "conditional" block, where it belongs.
I would recommend turning your integers to string and using the .contains() method and looping through that. This would make the code easier to understand.

Nested For loop incrementing after termination

I've got 2 for loops, one nested inside of another. They loop through a 2D array of buttons to get the source of each button thats been clicked using the action listener.
When the button is found I pass the position/array indexs of the button to an external method. However when the button is found from the array of buttons the first for loop evaluates its termination condition to FALSE but still increments the value of i. Leading to an off by one error. My code is in the standard action performed method, with "event" being the ActionEvent. buttons[][] is a JButton array defined as an instance variable. It is of size 10 x 10 and is already added to the panel.
int i = 0; //this will loop through the columns in the array
int j = 0; //loop through the rows
boolean locatedSource = false; //allows me to escape both loops
for(i = 0; !(locatedSource) && i < buttons.length; i++) //problem here, when i < buttons.length is FALSE i still gets incremented, leading to an off by one error
{
for(j = 0; !(locatedSource) && j < buttons.length; j++)
{
if(event.getSource() == buttons[i][j])
{
locatedSource = true;
break;
}
}
}
//do stuff with i and j in another method. Leads to array out of bounds error / off by one error
}
I should of mentioned, I'm not looking to solve this problem with the use of labels, they seem to be discouraged.
Explanation of the problem
The increment expression of a for loop is executed after each loop iteration not before. See the following quote from the Oracle Java tutorial:
The for statement provides a compact way to iterate over a range of values. Programmers often refer to it as the "for loop" because of the way in which it repeatedly loops until a particular condition is satisfied. The general form of the for statement can be expressed as follows:
for (initialization; termination;
increment) {
statement(s)
}
When using this version of the for statement, keep in mind that:
The initialization expression initializes the loop; it's executed once, as the loop begins.
When the termination expression evaluates to false, the loop terminates.
The increment expression is invoked after each iteration through the loop; it is perfectly acceptable for this expression to increment or decrement a value.
For loop solution
You can re-write your loop so that the increment is the first statement inside the loop.
for (i = 0; !(locatedSource) && i < buttons.length;) {
i++;
for (j = 0; !(locatedSource) && j < buttons.length;) {
j++;
if (event.getSource() == buttons[i][j]) {
locatedSource = true;
}
}
}
While Loop Version
Given that the loop variables are both initialised outside of the loop and you don't want to use a for-loop increment expression it might be clearer to rewrite the code to use while-loops as follows:
while (!(locatedSource) && i < buttons.length) {
i++;
while (!(locatedSource) && j < buttons.length) {
j++;
if (event.getSource() == buttons[i][j]) {
locatedSource = true;
}
}
}
Three possible solutions:
Explicitely set a "found" index and do not reuse your for loop indices.
Factor the searching out in an own method and return directly from the loop.
Decrement i by 1 after finishing the loops.
Use some boolean flag set it in inner loop and check it in the beginning of outer loop.
Here is code:
boolean found = false;
for (i = 0; i < 10; i++) // problem here, when i < buttons.length is FALSE i still gets
// incremented, leading to an off by one error
{
if (found) {
i--;
break;
}
for (j = 0; j < 5; j++) {
if (i == 5 && j == 3) {
found = true;
break;
}
}
//if (found) {
// break;
//}
}
Your code contains a comment "problem here, when i < buttons.length is FALSE i still gets incremented, leading to an off by one error", which is wrong in the ordering of the events.
First the cycle update block gets executed (like i++) and after that the condition is checked (like `i < buttons.length').
Meaning, that i == buttons.length is the correct state after the cycle ends without triggering the locatedSource condition.

Retrying a for loop?

Is it possible to use a for lop to set a condition of an object. And if that condition does not pass a test (if statement), the condition is changed until it does pass the test? Here is my pseudo code attempt, but i doubt it will work:
for (int i = 0; i < myArray.size(); ++i){
myArray.get(i).position = random Position
if(myArray.get(i).position == allowed position){
//Success!
}else{
//re-attempt to change position until it meets criteria
}
}
Please dont hate, i just started Java. And english is not my native language
Thank you
You could use a while loop:
myArray.get(i).position = random Position
while (myArray.get(i).position != 100) {
myArray.get(i).position = random Position
}
// Success!
Just Modify your Code Little.
for (int i = 0; i < myArray.size(); ++i){
myArray.get(i).position = random Position
if(myArray.get(i).position == allowed position){
break;
//Success!
}else{
//re-attempt to change position until it meets criteria
// countinue
}
}
by writing break, your loop is terminated when you get your desired value.
boolean correctPosition=false;
for (int i = 0; !correctPosition; ++i){
myArray.get(i).position = random Position
if(myArray.get(i).position == 100){
correctPosition=true;
//Success!
}else{
//re-attempt to change position until it meets criteria
}
}

Dead code in double for loop

I recently started coding in Java, and I have met this dead code problem. I have been looking at other questions (and answers) at Stack Overflow but I haven't found a solution yet. Hopefully you can help. The problem occurs at t++
public static boolean constraint2(int [][] xmatrix, int [][] ymatrix){
for(int l = 0; l < xmatrix.length; l++){
for(int t = 0; t < xmatrix[0].length; t++){ // DEAD CODE at "t++"
if(b[t]*xmatrix[l][t] > ymatrix[l][t]){
return false;
}
else{
return true;
}
}
}
return false;
}
It means that this statement will never execute. First iteration of this loop will exit method and break the loop. So this code is equivalent to:
for(int l = 0; l < xmatrix.length; l++){
if(xmatrix[0].length>0) {
if(b[0]*xmatrix[l][0] > ymatrix[l][0]){
return false;
}
else{
return true;
}
}
}
and t++ doesn't really make sense.
"Dead code" is usually just a warning and wouldn't stop you from compiling your app.
Also, probably you meant t < xmatrix[l].length in loop condition.
UPDATE: You didn't mention it in your question body, but as far as I understand from your comment to another answer what you need is to check that constraint holds for each element in the matrix. To implement it all you need is to check if constraint fails:
public static boolean constraint2(int [][] xmatrix, int [][] ymatrix){
for(int l = 0; l < xmatrix.length; l++){
for(int t = 0; t < xmatrix[l].length; t++){
if(b[t]*xmatrix[l][t] > ymatrix[l][t]) {
//constraint failed
return false;
}
}
}
//constraint holds for all elements
return true;
}
The code returns boolean value in your for loop and returns to the calling function. So it is obvious that code will not execute further after first iteration.
for(int t = 0; t < xmatrix[0].length; t++){ //This is your for loop
if(b[t]*xmatrix[l][t] > ymatrix[l][t]){
return false; // In first iteration, this loop either return false
} // or
else{ //true as per the condition
return true; // And return to the calling function by breaking the loop.
}
}
Inside the most inner for loop---- for both if and else condition checking you are returning from the function after the first iteration. So the t++ is not executing. It's nothing with Java. I think there is some problem in problem solving logic. You have to stop from returning either the if condition is true or the false condition.

Categories