Unit 2 notes · 12 topics, read straight through
Unit 2: Selection and Iteration
Every topic in one read: the big idea, the notes, the Java examples and the exam traps. Practice comes after, on each topic's checkpoint.
2.1 Algorithms with Selection and Repetition
Big ideaEvery algorithm is a mix of doing steps in order, making true or false decisions, and repeating until done.
1. The three building blocks
An algorithm is a step-by-step process that solves a problem or completes a task. Every algorithm, from a recipe to a video game, is built from three building blocks: sequencing, selection and repetition 2.1.A.1.
| Building block | What it does | Everyday example | Java tool (later in Unit 2) |
|---|---|---|---|
| Sequencing | Runs steps one after another, in order | Put on socks, then shoes | Statements in order |
| Selection | Makes a decision, then picks which steps run | If it is raining, take an umbrella | if, if-else |
| Repetition | Repeats steps until a goal is reached | Keep stirring until the sugar dissolves | while, for |
Algorithms get selection through decision making and repetition through looping 2.1.A.2. Here is an everyday algorithm, written in plain language, that uses all three:
- Take the cap off the bottle. (sequencing)
- If the bottle is dirty, rinse it. (selection)
- Hold the bottle under the fountain until it is full. (repetition)
- Put the cap back on. (sequencing)
2. Selection: a true or false decision
Selection happens when the way an algorithm continues depends on a true or false decision 2.1.A.3. The question must have exactly two possible answers. “Is the temperature above 25 degrees?” works. “What is the temperature?” does not, because its answer is a number, not true or false.
| Decision | If true | If false |
|---|---|---|
| Is the temperature above 25? | Wear shorts | Wear jeans |
The same decision in Java (you will learn the if statement in Topic 2.3):
int temperature = 31;
if (temperature > 25) {
System.out.println("Wear shorts");
} else {
System.out.println("Wear jeans");
}
Output:
Wear shorts
Watch out: only one of the two paths runs. The program never prints both lines.
3. Repetition: repeat until the desired outcome
In repetition, the same process runs again and again, and it stops once the result you want has been reached 2.1.A.4. In plain language: “Water the plant one cup at a time until the soil moisture reaches 50.”
int moisture = 20;
int cups = 0;
while (moisture < 50) {
moisture += 15;
cups++;
}
System.out.println("Cups of water: " + cups);
Output:
Cups of water: 2
Trace it. Each row is one check of the decision “is moisture still below 50?”
| Check | moisture < 50? | moisture after the step | cups |
|---|---|---|---|
| 1 | 20 < 50 is true | 35 | 1 |
| 2 | 35 < 50 is true | 50 | 2 |
| 3 | 50 < 50 is false | (stop) | 2 |
Watch out: the repeated step must move toward the desired outcome. If the plant never got any water, the moisture would stay at 20 and the process would never end.
4. Order changes the outcome
Where you place each step of sequencing, selection and repetition helps decide what the algorithm produces, so changing the order can change the result 2.1.A.5. Look at two versions of a game rule. A player has 90 points, earns 15 bonus points, and levels up at 100 points.
Version A checks first, then adds the bonus:
int points = 90;
if (points >= 100) {
System.out.println("Level up!");
}
points += 15;
System.out.println("Points: " + points);
Output:
Points: 105
Version B adds the bonus first, then checks:
int points = 90;
points += 15;
if (points >= 100) {
System.out.println("Level up!");
}
System.out.println("Points: " + points);
Output:
Level up!
Points: 105
Same steps, same final points, different behavior. In Version A the decision saw the old value (90), so the player never leveled up.
5. From written steps to code
Writing the algorithm in plain language first helps you choose the right building blocks. Problem: a student saves 12 dollars each week for headphones that cost 50 dollars. How many weeks does it take, and how much extra money is left?
- Set saved to 0 and weeks to 0.
- Repeat until saved is at least 50: add 12 to saved and add 1 to weeks.
- Print the number of weeks.
- If saved is more than 50, print the extra money.
Each numbered step becomes code:
int saved = 0;
int weeks = 0;
while (saved < 50) {
saved += 12;
weeks++;
}
System.out.println("Weeks: " + weeks);
if (saved > 50) {
System.out.println("Extra: " + (saved - 50));
}
Output:
Weeks: 5
Extra: 10
Notice the translation: “repeat until saved is at least 50” becomes “keep looping while saved is less than 50”. The loop continues while the opposite of the goal is true.
Exam traps
- Same steps, different order: on the exam, trace both versions. A check placed before an update sees the old value; a check placed after sees the new value.
- A repetition needs a step that moves toward the desired outcome, or it never stops.
- Selection is based on one true or false decision, not on a number or a word. "How old are you?" is not a selection; "Are you 16 or older?" is.
2.2 Boolean Expressions
Big ideaA relational operator turns two values into one answer: true or false.
1. Relational operators produce true or false
A relational operator compares two values. An expression that uses one evaluates to a Boolean value, true or false 2.2.A.3. You can print that value or store it in a boolean variable.
| Operator | Meaning | Example | Value |
|---|---|---|---|
== |
equal to | 4 == 4 |
true |
!= |
not equal to | 4 != 4 |
false |
< |
less than | 3 < 8 |
true |
> |
greater than | 3 > 8 |
false |
<= |
less than or equal to | 5 <= 5 |
true |
>= |
greater than or equal to | 2 >= 7 |
false |
int age = 16;
boolean canDrive = age >= 16;
boolean canVote = age >= 18;
System.out.println(canDrive);
System.out.println(canVote);
System.out.println(age != 16);
Output:
true
false
false
Watch out: =<, => and <> are not Java operators. The equals sign always comes second.
2. Comparing numbers with <, >, <= and >=
The operators <, >, <= and >= compare numeric values to find their relationship 2.2.A.2. They work with int and double values, and you can mix the two types: Java widens the int to a double before comparing.
The arithmetic on each side happens first, with the normal Java rules. That makes integer division a classic trap:
int goals = 7;
int games = 2;
System.out.println(goals / games > 3);
System.out.println(goals / games >= 3);
System.out.println((double) goals / games > 3);
Output:
false
true
true
goals / games is 7 / 2, which is 3 in integer division, and 3 > 3 is false. Casting one operand to double gives 3.5, and 3.5 > 3 is true.
Watch out: a range such as “between 0 and 10” cannot be written as 0 < x < 10 in Java. The first comparison gives a boolean, and a boolean cannot be compared with a number.
int x = 5;
boolean inRange = 0 < x < 10;
error: bad operand types for binary operator '<'
You will join two comparisons with && in Topic 2.5.
3. == and != with primitive values
The operators == and != check whether two values are the same 2.2.A.1. With primitive types (int, double, boolean), they compare the actual primitive values.
int lives = 3;
int startLives = 3;
boolean gameOver = false;
System.out.println(lives == startLives);
System.out.println(lives != 0);
System.out.println(gameOver == false);
Output:
true
true
true
Watch out (= versus ==): one equals sign is assignment, two are comparison. Using = where Java expects a Boolean expression is a compile error for int values:
int lives = 3;
if (lives = 0) {
System.out.println("Game over");
}
error: incompatible types: int cannot be converted to boolean
Watch out (double values): round-off error can make two values that should be equal compare as different.
double total = 0.1 + 0.2;
System.out.println(total == 0.3);
System.out.println(total);
Output:
false
0.30000000000000004
4. == and != with reference types
With reference types, == and != compare the object references, not the contents of the objects 2.2.A.1. A reference tells Java where an object is. Two references are == only when they refer to the same object.
String a = new String("goal");
String b = new String("goal");
String c = a;
System.out.println(a == b);
System.out.println(a == c);
System.out.println(a.equals(b));
Output:
false
true
true
a == bis false:newcreated two separate objects, even though both hold the letters “goal”.a == cis true:cholds a copy of the reference ina, so both refer to the same object.a.equals(b)is true:equalscompares the characters.
Watch out: this is the most common String trap on the AP® exam. To ask “do these strings have the same characters?”, use equals, never ==. Topic 2.6 goes deeper into comparing object references.
5. Store the result and reuse it
Because a relational expression evaluates to a Boolean value, you can store the result in a boolean variable with a clear name. This makes later code easier to read.
double temperature = 38.4;
boolean hasFever = temperature >= 38.0;
System.out.println("Fever: " + hasFever);
Output:
Fever: true
Read hasFever = temperature >= 38.0 from right to left: first the comparison gives true, then = stores true in hasFever.
Exam traps
- if (score = 100) is a compile error. Comparison needs ==.
- Integer division inside a comparison: 9 / 2 >= 4.5 is false, because 9 / 2 is 4.
- Two String objects with the same letters can still be != each other. Compare the contents with equals.
- Comparing double values with == can fail because of round-off: 0.1 + 0.2 == 0.3 is false.
2.3 if Statements
Big ideaAn if statement lets the value of a Boolean expression decide which code runs.
1. Selection changes the flow of control
Normally Java runs statements in sequence, top to bottom. Selection statements change that sequential execution 2.3.A.1. An if statement is a type of selection statement: it affects the flow of control by running different segments of code depending on the value of a Boolean expression 2.3.A.2.
boolean isRaining = true;
if (isRaining) {
// body: runs only when the Boolean expression is true
}
// the program continues here in every case
The condition must be a Boolean expression inside parentheses. It can be a relational expression (score >= 70) or a boolean variable (isMember).
2. One-way selection: if
Use a one-way selection (if statement) when some code should run in one situation and be skipped otherwise. If the Boolean expression is true, the body runs; if it is false, the body is skipped 2.3.A.3.
int battery = 15;
System.out.println("Checking battery...");
if (battery < 20) {
System.out.println("Low battery: plug in your phone");
}
System.out.println("Battery: " + battery + "%");
Output:
Checking battery...
Low battery: plug in your phone
Battery: 15%
Trace with two different starting values:
| battery | battery < 20 | Lines printed |
|---|---|---|
| 15 | true | Checking, Low battery, Battery: 15% |
| 80 | false | Checking, Battery: 80% |
The last println is after the if, so it runs in both cases.
3. Two-way selection: if-else
Use a two-way selection (if-else statement) when the program has two different segments of code to choose from: one for a true Boolean expression and one for a false one. The if body runs when the expression is true, and the else body runs when it is false 2.3.A.4. Exactly one of the two bodies runs.
int score = 64;
if (score >= 70) {
System.out.println("Passed");
} else {
System.out.println("Try again");
}
System.out.println("Done");
Output:
Try again
Done
Inside a method, each branch can return a value. Every path through the method must reach a return.
public static String parity(int n) {
if (n % 2 == 0) {
return "even";
} else {
return "odd";
}
}
System.out.println(parity(14));
System.out.println(parity(-3));
Output:
even
odd
4. Two ifs are not an if-else
Two separate if statements are checked one after the other, so both bodies can run. Compare this with the if-else in Section 3.
int temp = 30;
if (temp > 25) {
System.out.println("Hot");
}
if (temp > 10) {
System.out.println("Warm");
}
Output:
Hot
Warm
Separate ifs can also change a value that the next if then checks:
int coins = 8;
if (coins < 10) {
coins += 5;
}
if (coins >= 10) {
System.out.println("Bonus unlocked");
}
System.out.println(coins);
Output:
Bonus unlocked
13
5. Braces and the stray semicolon
Java ignores indentation. Without braces, an if controls only the very next statement.
int speed = 40;
if (speed > 50)
System.out.println("Slow down");
System.out.println("Speeding ticket");
Output:
Speeding ticket
The second println looks like it is inside the if, but it is not, so it runs even though speed > 50 is false.
A semicolon right after the condition is worse: it gives the if an empty body, and the block below always runs.
int speed = 40;
if (speed > 50); {
System.out.println("Slow down");
}
Output:
Slow down
Watch out: an else with no matching if (for example, after a stray semicolon) does not compile:
int speed = 40;
if (speed > 50); {
System.out.println("Slow down");
} else {
System.out.println("OK");
}
error: 'else' without 'if'
Rule for the exam and for your own code: put braces around every if and else body, and never put a semicolon after if (...).
Exam traps
- if (x > 5); with a semicolon: the block below runs every time, because the if controls an empty statement.
- No braces: only the first statement is inside the if, no matter how the code is indented.
- Two ifs in a row can both run; an if-else never runs both bodies.
- Statements after the whole if-else always run, whatever the condition was.
2.4 Nested if Statements
Big ideaNesting and chaining let a program make a decision that depends on earlier decisions.
1. An if inside an if
Nested if statements are if, if-else or if-else-if statements written inside the body of another if, if-else or if-else-if statement 2.4.A.1. Use nesting when a second decision only makes sense after the first one.
A cinema charges by age, but only members get the student price:
int age = 15;
boolean isMember = true;
if (age < 18) {
if (isMember) {
System.out.println("Ticket: 5 dollars");
} else {
System.out.println("Ticket: 8 dollars");
}
} else {
System.out.println("Ticket: 12 dollars");
}
Output:
Ticket: 5 dollars
2. The inner condition waits for the outer one
The Boolean expression of the inner if is evaluated only if the outer Boolean expression is true 2.4.A.2. Trace the cinema code with four inputs:
| age | isMember | age < 18 | isMember checked? | Output |
|---|---|---|---|---|
| 15 | true | true | yes, true | Ticket: 5 dollars |
| 15 | false | true | yes, false | Ticket: 8 dollars |
| 30 | true | false | no | Ticket: 12 dollars |
| 30 | false | false | no | Ticket: 12 dollars |
When the outer test is false, Java jumps straight to the outer else. The value of isMember does not matter.
3. Multiway selection: if-else-if
A multiway selection (if-else-if) is used when there is a series of expressions, each with its own segment of code. Java checks the expressions from top to bottom, runs no more than one segment (the one for the first expression that evaluates to true) and skips the rest. If no expression is true and there is a trailing else, the else body runs 2.4.A.3.
int score = 84;
String grade;
if (score >= 90) {
grade = "A";
} else if (score >= 80) {
grade = "B";
} else if (score >= 70) {
grade = "C";
} else {
grade = "F";
}
System.out.println("Grade: " + grade);
Output:
Grade: B
score >= 70 is also true for 84, but it is never checked, because score >= 80 was true first.
| score | First true expression | grade |
|---|---|---|
| 95 | score >= 90 | A |
| 84 | score >= 80 | B |
| 70 | score >= 70 | C |
| 42 | none, trailing else | F |
4. Order matters in a chain
Because the first true expression wins, the order of the conditions changes the result. This version checks the widest condition first:
int score = 95;
String grade;
if (score >= 70) {
grade = "C";
} else if (score >= 80) {
grade = "B";
} else if (score >= 90) {
grade = "A";
} else {
grade = "F";
}
System.out.println("Grade: " + grade);
Output:
Grade: C
A score of 95 gets a C, because score >= 70 is true first. The B and A branches can never run. Rule: with >=, put the largest boundary first; with <=, put the smallest boundary first.
Without a trailing else, a chain may run nothing at all:
int windSpeed = 12;
if (windSpeed > 60) {
System.out.println("Storm warning");
} else if (windSpeed > 30) {
System.out.println("Strong wind");
}
System.out.println("Report finished");
Output:
Report finished
5. The dangling else
When there are no braces, an else pairs with the nearest earlier if that does not already have an else, no matter how the code is indented. This is called the dangling else.
int x = 5;
int y = 1;
if (x > 3)
if (y > 3)
System.out.println("Both big");
else
System.out.println("x is small");
Output:
x is small
The else is indented under if (x > 3), but it belongs to if (y > 3). Since x > 3 is true and y > 3 is false, the inner else runs and prints a message that is wrong. With braces, the meaning matches the indentation:
int x = 5;
int y = 1;
if (x > 3) {
if (y > 3) {
System.out.println("Both big");
}
} else {
System.out.println("x is small");
}
System.out.println("End");
Output:
EndExam traps
- A wrongly ordered chain such as checking score >= 60 before score >= 90 gives every passing student the lowest grade.
- Dangling else: without braces, the else belongs to the inner if, so it runs when the outer condition is true and the inner one is false.
- In nested code, if the outer condition is false, the inner condition is never evaluated, even if it would be true.
- A chain of separate ifs is not a multiway selection: several bodies can run.
2.5 Compound Boolean Expressions
Big ideaLogical operators combine true and false values, and Java stops evaluating as soon as the answer is known.
1. The three logical operators
Logical operators are used with Boolean expressions 2.5.A.1. Each one produces a Boolean value.
!a(not) is true ifais false, and false otherwise.a && b(and) is true if bothaandbare true, and false otherwise.a || b(or) is true ifais true,bis true, or both are true, and false otherwise.
A truth table lists every combination of inputs:
| a | b | !a | a && b | a || b |
|---|---|---|---|---|
| true | true | false | true | true |
| true | false | false | false | true |
| false | true | true | false | true |
| false | false | true | false | false |
int age = 15;
boolean hasTicket = true;
System.out.println(age >= 13 && hasTicket);
System.out.println(age >= 18 || hasTicket);
System.out.println(!hasTicket);
Output:
true
true
false
An expression that uses logical operators evaluates to a Boolean value, so you can store it, print it or use it as an if condition 2.5.A.1.
2. Order of precedence
When an expression mixes logical operators, Java evaluates ! (not) first, then && (and), then || (or) 2.5.A.1. Arithmetic and relational operators are evaluated before && and ||.
boolean a = true;
boolean b = false;
boolean c = false;
System.out.println(a || b && c);
System.out.println((a || b) && c);
System.out.println(!a || c);
Output:
true
false
false
Step by step for a || b && c: first b && c is false && false, which is false. Then a || false is true || false, which is true. Parentheses change the order: (a || b) && c is true && false, which is false.
Watch out: ! binds very tightly. To negate a comparison, put the comparison in parentheses.
int x = 5;
boolean result = !x > 3;
error: bad operand type int for unary operator '!'
The fix is !(x > 3).
3. Ranges with && and ||
To check whether a value is inside a range, join two comparisons with &&. To check whether it is outside, use ||.
int hour = 22;
boolean openHours = hour >= 9 && hour <= 17;
boolean quietHours = hour < 7 || hour > 21;
System.out.println("Open: " + openHours);
System.out.println("Quiet: " + quietHours);
Output:
Open: false
Quiet: true
Watch out: hour < 7 && hour > 21 can never be true, because no number is both below 7 and above 21. This is a logic error: the code compiles, but the condition is always false.
4. Short-circuit evaluation
Short-circuit evaluation: when the first Boolean expression alone already settles the result of && or ||, Java does not evaluate the second Boolean expression at all 2.5.A.2.
false && anythingis always false, so Java skipsanything.true || anythingis always true, so Java skipsanything.
Programmers use this on purpose as a safety check. Here the division only happens when it is safe:
int games = 0;
int points = 40;
if (games != 0 && points / games > 10) {
System.out.println("High scorer");
} else {
System.out.println("Not enough data");
}
Output:
Not enough data
games != 0 is false, so points / games is never evaluated. Swap the two sides and the division runs first:
int games = 0;
int points = 40;
if (points / games > 10 && games != 0) {
System.out.println("High scorer");
}
Output:
Exception in thread "main" java.lang.ArithmeticException: / by zero
The same idea protects a method call on a reference that might be null:
String nickname = null;
if (nickname != null && nickname.length() > 3) {
System.out.println("Long nickname");
} else {
System.out.println("No long nickname");
}
Output:
No long nickname
5. Short-circuit and side effects
If the second expression calls a method that does something visible, such as printing, short-circuit evaluation means that call may not happen. Exam questions use this to test whether you really know which side runs.
public class ShortCircuitDemo {
public static boolean isLong(String word) {
System.out.println("Checking " + word);
return word.length() > 5;
}
public static void main(String[] args) {
boolean first = 5 > 2 || isLong("banana");
System.out.println("first: " + first);
boolean second = 5 < 2 || isLong("kiwi");
System.out.println("second: " + second);
}
}
Output:
first: true
Checking kiwi
second: false
In first, the left side is true, so isLong("banana") is never called and nothing extra is printed. In second, the left side is false, so Java must evaluate the right side: isLong("kiwi") runs, prints its line and returns false.
Exam traps
- count != 0 && total / count > 5 is safe when count is 0. Swap the two sides and the division runs first, throwing an ArithmeticException.
- x < 0 && x > 10 is never true. To test outside a range, use ||: x < 0 || x > 10.
- true || false && false is true, not false: && is done before ||.
- !x > 3 does not compile when x is an int. Write !(x > 3).
- Short-circuit also skips anything the second expression would do, such as a method call that prints.
2.6 Comparing Boolean Expressions
Big ideaEquivalent expressions always agree, and == on objects asks about identity, not contents.
1. Equivalent expressions and truth tables
Two Boolean expressions are equivalent if they evaluate to the same value in all cases. A truth table can prove they are equivalent 2.6.A.1: list every combination of true and false for the variables, evaluate both expressions in each row, and compare. With two variables there are 4 rows; with three variables there are 8.
Are !(a && b) and !a || !b equivalent?
| a | b | a && b | !(a && b) | !a | !b | !a || !b |
|---|---|---|---|---|---|---|
| true | true | true | false | false | false | false |
| true | false | false | true | false | true | true |
| false | true | false | true | true | false | true |
| false | false | false | true | true | true | true |
The columns for !(a && b) and !a || !b match in every row, so the expressions are equivalent.
Are !(a && b) and !a && !b equivalent? Row 2 (a true, b false) gives true for the first and false for the second. One row that differs is enough to show two expressions are not equivalent.
boolean a = true;
boolean b = false;
System.out.println(!(a && b));
System.out.println(!a || !b);
System.out.println(!a && !b);
Output:
true
true
false
2. De Morgan’s laws
De Morgan’s laws can be applied to Boolean expressions to create equivalent Boolean expressions 2.6.A.2:
!(a && b)is equivalent to!a || !b!(a || b)is equivalent to!a && !b
To apply a law: move the ! onto each part, and switch && to || (or || to &&). When the parts are comparisons, negate each comparison:
| Comparison | Its negation |
|---|---|
x > 5 |
x <= 5 |
x < 5 |
x >= 5 |
x >= 5 |
x < 5 |
x == 5 |
x != 5 |
Example: !(temp > 30 || isRaining) becomes temp <= 30 && !isRaining. In words: “not (hot or raining)” means “not hot and not raining”.
int temp = 24;
boolean isRaining = false;
boolean original = !(temp > 30 || isRaining);
boolean rewritten = temp <= 30 && !isRaining;
System.out.println(original + " " + rewritten);
Output:
true true
Watch out: a common wrong answer keeps the > and writes temp < 30. For temp equal to 30, !(temp > 30) is true but temp < 30 is false.
3. Two variables, one object
Two different variables can hold references to the same object. Object references can be compared using == and != 2.6.B.1. The comparison is true only when both references point to the same object; it does not look at what is inside the objects.
String teamA = new String("Lions");
String teamB = new String("Lions");
String favorite = teamA;
System.out.println(teamA == favorite);
System.out.println(teamA == teamB);
System.out.println(teamA != teamB);
Output:
true
false
true
| Variable | Refers to |
|---|---|
teamA |
object 1 (“Lions”) |
favorite |
object 1 (same object as teamA) |
teamB |
object 2 (“Lions”, a separate object) |
Variables like teamA and favorite, which refer to the same object, are called aliases.
Watch out: two string literals with the same characters, such as "Lions" and "Lions", may share one object, so == can happen to be true. Never rely on this: compare string contents with equals.
4. Comparing a reference with null
A reference variable can hold null, which means it does not refer to any object. Comparing a reference with null, using == or !=, tells you whether it actually refers to an object 2.6.B.2.
String winner = null;
if (winner == null) {
System.out.println("No winner yet");
}
winner = "Tigers";
if (winner != null) {
System.out.println("Winner: " + winner + " (" + winner.length() + " letters)");
}
Output:
No winner yet
Winner: Tigers (6 letters)
Calling a method such as length() on a null reference throws a NullPointerException. Check for null first, and put that check on the left of && so short-circuit evaluation protects the call: winner != null && winner.length() > 2.
5. equals: equivalency by attributes
Classes often define their own equals method to specify what makes two objects of that class equivalent. The decision is most often based on attributes of the two objects 2.6.B.3.
The String class is the example you use most. Its equals method treats two strings as equivalent when they have the same sequence of characters, even if they are separate objects.
String typed = new String("open sesame");
String password = "open sesame";
System.out.println(typed == password);
System.out.println(typed.equals(password));
System.out.println(typed.equals("Open sesame"));
Output:
false
true
false
Uppercase and lowercase letters are different characters, so the last comparison is false.
Other classes choose their own criteria. A class for library cards might call two cards equal when their card numbers match; a class for points on a grid might compare both coordinates. When an exam question gives you a class with an equals method, read its description to see which attributes it compares. (Writing your own replacement for the equals method is outside the AP® course and exam.)
| Question you are asking | Use |
|---|---|
| Do these two variables refer to the same object? | == |
| Does this variable refer to any object? | == null or != null |
| Are these two objects equivalent (same attribute values)? | equals |
Exam traps
- Negating a comparison: !(score >= 60) is score < 60, not score <= 60.
- !(a && b) is not !a && !b. The operator must flip.
- One test case is not a proof of equivalence. Check every row of the truth table.
- Calling a method on a null reference throws a NullPointerException. Check != null first, on the left of &&.
2.7 while Loops
Big ideaA while loop repeats its body for as long as its condition stays true, and checks the condition before every pass.
1. When you need iteration
Iteration is one kind of repetition. An iteration statement changes the flow of control: it sends the program back to run the same segment of code again, for as long as the Boolean expression that controls the loop stays true. That can mean many times, once, or zero times 2.7.A.1. You need iteration when a task repeats the same steps many times, or when you do not know how many times the steps must repeat until the program is running: “add interest until the savings reach 1000”, “keep halving until the value is 1”.
Without a loop you would copy the same lines again and again, and you could not handle a number of repetitions that changes.
2. How a while loop runs
A while loop is a type of iterative statement. The Boolean expression is evaluated before each iteration of the loop body, including the first. When it is true, the body runs. This continues until the expression is false, and then the iteration ends 2.7.B.1.
int countdown = 3;
while (countdown > 0) {
System.out.println(countdown);
countdown--;
}
System.out.println("Liftoff!");
Output:
3
2
1
Liftoff!
| Check | countdown | countdown > 0 | Printed |
|---|---|---|---|
| 1 | 3 | true | 3 |
| 2 | 2 | true | 2 |
| 3 | 1 | true | 1 |
| 4 | 0 | false | (loop ends) |
The Boolean expression was evaluated 4 times; the body ran 3 times. The variable that controls the loop (countdown) is called the loop control variable. A working loop has three parts: it is initialized before the loop, tested in the condition and updated in the body.
Watch out: the condition is only checked at the top. In the example below, steps passes 10 in the middle of the body, but the rest of the body still runs.
int steps = 8;
while (steps < 10) {
steps += 3;
System.out.println("steps = " + steps);
}
Output:
steps = 11
3. Zero iterations
If the Boolean expression is false the first time it is evaluated, the loop body does not run at all 2.7.A.3.
int lives = 0;
while (lives > 0) {
System.out.println("Playing...");
lives--;
}
System.out.println("Game over");
Output:
Game over
This matters when a loop depends on its starting values (initial conditions). Ask: “What must be true before the loop starts for it to work as intended?”
4. Infinite loops
If the Boolean expression of a loop is true every time it is checked, the loop never stops: this is an infinite loop 2.7.A.2. The code compiles, but the program never moves past the loop. Three common causes:
Forgetting the update:
int count = 1;
while (count <= 5) {
System.out.println(count);
}
Updating in the wrong direction:
int count = 1;
while (count <= 5) {
System.out.println(count);
count--;
}
count moves away from 5 (1, 0, -1, -2…), so count <= 5 stays true. (Strictly, count would overflow after about two billion iterations, but for the AP® exam this loop is an infinite loop.)
Skipping over the stopping value with !=:
int n = 0;
while (n != 10) {
n += 4;
}
n goes 0, 4, 8, 12, 16… and is never exactly 10. Writing n < 10 would stop the loop.
A stray semicolon also causes trouble: while (count <= 5); is a loop with an empty body, so count never changes.
5. Off by one errors
An off by one error happens when the loop runs one time too many or one time too few 2.7.A.4. Goal: print the lap numbers 1 to 5.
int lap = 1;
while (lap < 5) {
System.out.print(lap + " ");
lap++;
}
Output:
1 2 3 4
One time too few: when lap is 5, lap < 5 is false. The fix is lap <= 5:
int lap = 1;
while (lap <= 5) {
System.out.print(lap + " ");
lap++;
}
Output:
1 2 3 4 5
Starting at 0 with lap <= 5 would be one time too many (6 iterations). To check any loop, trace the first iteration and the last iteration.
6. Repeat until a goal is reached
while loops are a natural fit when you do not know the number of iterations in advance. A savings account earns 10 percent each year (integer dollars, rounded down). How many years until 100 dollars grows to at least 150?
int balance = 100;
int years = 0;
while (balance < 150) {
balance = balance + balance / 10;
years++;
}
System.out.println(years + " years, balance " + balance);
Output:
5 years, balance 160
| years at check | balance | balance < 150 |
|---|---|---|
| 0 | 100 | true |
| 1 | 110 | true |
| 2 | 121 | true |
| 3 | 133 | true |
| 4 | 146 | true |
| 5 | 160 | false |
Notice balance / 10 is integer division: 121 / 10 is 12 and 133 / 10 is 13.
You can also end a loop early with a condition in the body. Here a boolean variable stops the search at the first multiple of 7 above 50:
int n = 51;
boolean found = false;
while (!found) {
if (n % 7 == 0) {
found = true;
} else {
n++;
}
}
System.out.println("First multiple of 7 above 50: " + n);
Output:
First multiple of 7 above 50: 56
Watch out: if the n++ came before the if, the search would skip testing 51. The order of statements in the body decides whether an early exit happens too early, too late or not at all.
Exam traps
- A semicolon after while (...) makes an empty body: the loop either does nothing or never ends.
- Forgetting the update (count++) inside the body gives an infinite loop.
- while (n != 10) with n += 4 starting at 0 skips over 10 and never stops. Prefer < or > in loop conditions.
- Counting how many times a body runs: starting at 1 with <= n gives n times; starting at 0 with <= n gives n + 1 times.
2.8 for Loops
Big ideaA for loop packs the setup, the test and the step of a counting loop into one header line.
1. The three parts of the header
A for loop is a type of iterative statement. Its header has three parts, separated by semicolons: the initialization, the Boolean expression and the update 2.8.A.1.
for (int lap = 1; lap <= 4; lap++) {
System.out.println("Lap " + lap);
}
Output:
Lap 1
Lap 2
Lap 3
Lap 4
| Part | In the example | Job |
|---|---|---|
| Initialization | int lap = 1 |
Create and set the loop control variable |
| Boolean expression | lap <= 4 |
Decide whether to run the body again |
| Update | lap++ |
Change the loop control variable after each iteration |
2. The exact order of execution
The initialization statement runs only once, before the Boolean expression is evaluated for the first time. The variable it initializes is called the loop control variable. The Boolean expression is evaluated right after the initialization and then after each update, until it is false. In each iteration, the update runs after the entire loop body and before the Boolean expression is evaluated again 2.8.A.2.
for (int i = 0; i < 3; i++) {
System.out.println("body sees i = " + i);
}
Output:
body sees i = 0
body sees i = 1
body sees i = 2
| Step | Action | i | Result |
|---|---|---|---|
| 1 | initialization | 0 | |
| 2 | check i < 3 |
0 | true |
| 3 | body | 0 | prints 0 |
| 4 | update i++ |
1 | |
| 5 | check i < 3 |
1 | true |
| 6 | body | 1 | prints 1 |
| 7 | update i++ |
2 | |
| 8 | check i < 3 |
2 | true |
| 9 | body | 2 | prints 2 |
| 10 | update i++ |
3 | |
| 11 | check i < 3 |
3 | false, loop ends |
Count from the table: the initialization ran 1 time, the Boolean expression 4 times, the body 3 times and the update 3 times.
A loop control variable declared in the header exists only inside the loop:
for (int i = 0; i < 3; i++) {
System.out.println(i);
}
System.out.println(i);
error: cannot find symbol
3. Counting iterations and other updates
You can count iterations without tracing every step:
| Header | Values of the loop control variable | Iterations |
|---|---|---|
for (int i = 0; i < 10; i++) |
0 to 9 | 10 |
for (int i = 1; i <= 10; i++) |
1 to 10 | 10 |
for (int i = 1; i < 10; i++) |
1 to 9 | 9 |
for (int i = 0; i <= 10; i += 2) |
0, 2, 4, 6, 8, 10 | 6 |
for (int i = 10; i > 0; i--) |
10 down to 1 | 10 |
for (int i = 1; i < 100; i *= 3) |
1, 3, 9, 27, 81 | 5 |
The update does not have to be i++. This loop counts down by 5:
for (int seconds = 20; seconds >= 0; seconds -= 5) {
System.out.print(seconds + " ");
}
Output:
20 15 10 5 0
A for loop is a good fit for an accumulator, such as the sum of the even numbers from 2 to 10:
int sum = 0;
for (int n = 2; n <= 10; n += 2) {
sum += n;
}
System.out.println("Sum: " + sum);
Output:
Sum: 30
4. for and while are interchangeable
A for loop can be rewritten as an equivalent while loop, and the other way around 2.8.A.3. Move the initialization above the loop, keep the Boolean expression, and make the update the last statement of the body.
for (int day = 1; day <= 3; day++) {
System.out.println("Day " + day);
}
int day = 1;
while (day <= 3) {
System.out.println("Day " + day);
day++;
}
Both print the same three lines:
Output:
Day 1
Day 2
Day 3
One difference: in the while version, day is declared before the loop, so it still exists after the loop, with the value 4.
Going the other way, a while loop that halves a value becomes a for loop:
int steps = 0;
for (int size = 64; size > 1; size /= 2) {
steps++;
}
System.out.println(steps);
Output:
6
5. Common for loop errors
A semicolon after the header gives the loop an empty body. The empty body runs 5 times, then the block runs once:
int total = 0;
for (int i = 0; i < 5; i++); {
total++;
}
System.out.println(total);
Output:
1
Changing the loop control variable in the body as well as in the header skips values:
for (int i = 0; i < 10; i++) {
System.out.print(i + " ");
i++;
}
Output:
0 2 4 6 8
Each iteration adds 2 to i: once in the body and once in the update. Keep all changes to the loop control variable in the header.
Exam traps
- for (int i = 0; i < 5; i++); with a semicolon runs an empty body 5 times, then the block below runs once.
- Off by one: for (int i = 1; i < 10; i++) runs 9 times, not 10.
- Changing the loop control variable inside the body as well as in the update skips values.
- After for (int k = 0; k < 3; k++) ends, k does not exist. If k is declared before the loop, its value after the loop is 3, the first value that failed the check.
2.9 Implementing Selection and Iteration Algorithms
Big ideaA handful of loop patterns (test, count, track the best, add up) solve most exam problems.
1. Evenly divisible: use the remainder
There are standard algorithms to identify if an integer is or is not evenly divisible by another integer 2.9.A.1. The test is the remainder operator: a % b == 0 means a is evenly divisible by b.
int year = 2028;
if (year % 4 == 0) {
System.out.println(year + " is divisible by 4");
}
for (int n = 1; n <= 15; n++) {
if (n % 3 == 0 && n % 5 == 0) {
System.out.print("both ");
} else if (n % 3 == 0) {
System.out.print("three ");
} else if (n % 5 == 0) {
System.out.print("five ");
}
}
System.out.println();
Output:
2028 is divisible by 4
three five three three five three both
The test for “divisible by both” comes first. If n % 3 == 0 came first, 15 would print “three” and never reach “both”.
Watch out: to test for odd numbers, write n % 2 != 0. In Java, -7 % 2 is -1, so n % 2 == 1 is false for negative odd numbers.
System.out.println(-7 % 2);
System.out.println(-7 % 2 != 0);
Output:
-1
true
2. The individual digits of an integer
There are standard algorithms to identify the individual digits in an integer 2.9.A.1. n % 10 is the last digit, and n / 10 (integer division) removes the last digit. Repeat while n > 0.
int n = 4072;
int digitSum = 0;
int digitCount = 0;
while (n > 0) {
int digit = n % 10;
System.out.println("digit: " + digit);
digitSum += digit;
digitCount++;
n = n / 10;
}
System.out.println("sum " + digitSum + ", count " + digitCount);
Output:
digit: 2
digit: 7
digit: 0
digit: 4
sum 13, count 4
| n at the check | n > 0 | digit = n % 10 | digitSum | n / 10 |
|---|---|---|---|---|
| 4072 | true | 2 | 2 | 407 |
| 407 | true | 7 | 9 | 40 |
| 40 | true | 0 | 9 | 4 |
| 4 | true | 4 | 13 | 0 |
| 0 | false |
Notes:
- The digits come out from right to left (ones digit first).
- The loop destroys
n. Copy it first if you need the original value later. - If
nstarts at 0, the loop runs zero times anddigitCountstays 0. If the number 0 should count as one digit, handle it with anifbefore the loop.
The same pattern can build a number with the digits reversed:
int n = 1234;
int reversed = 0;
while (n > 0) {
reversed = reversed * 10 + n % 10;
n /= 10;
}
System.out.println(reversed);
Output:
4321
3. Frequency: how often a criterion is met
There are standard algorithms to determine the frequency with which a specific criterion is met 2.9.A.1. Pattern: set a counter to 0 before the loop, test each value with an if, add 1 when the test passes, use the counter after the loop.
How many numbers from 1 to 50 are divisible by 6 or by 8?
int count = 0;
for (int n = 1; n <= 50; n++) {
if (n % 6 == 0 || n % 8 == 0) {
count++;
}
}
System.out.println(count);
Output:
12
There are 8 multiples of 6 and 6 multiples of 8 up to 50, but 24 and 48 are both, and || counts them only once: 8 + 6 - 2 = 12.
Watch out: writing int count = 0; inside the loop resets the counter every iteration, and the code would not compile here anyway, because count would not exist after the loop.
4. Minimum and maximum
There are standard algorithms to determine a minimum or maximum value 2.9.A.1. Pattern: keep the best value so far, and replace it whenever a new value beats it.
A shop sells a game at a price from 1 to 10 dollars. At price p, it expects to earn p * (100 - 8 * p) dollars. Which price earns the most?
int bestPrice = 1;
int bestIncome = 1 * (100 - 8 * 1);
for (int p = 2; p <= 10; p++) {
int income = p * (100 - 8 * p);
if (income > bestIncome) {
bestIncome = income;
bestPrice = p;
}
}
System.out.println("Best price: " + bestPrice + ", income: " + bestIncome);
Output:
Best price: 6, income: 312
The minimum and maximum of the digits of a number use the same idea. Here the starting values come from the Java Quick Reference: Integer.MAX_VALUE is larger than any digit, and Integer.MIN_VALUE is smaller than any digit, so the first digit always replaces them.
int n = 58291;
int smallest = Integer.MAX_VALUE;
int largest = Integer.MIN_VALUE;
while (n > 0) {
int d = n % 10;
if (d < smallest) {
smallest = d;
}
if (d > largest) {
largest = d;
}
n /= 10;
}
System.out.println("min " + smallest + ", max " + largest);
Output:
min 1, max 9
Watch out: starting smallest at 0 would give a minimum of 0 for 58291, a digit it does not contain. Use two separate if statements (not if-else) when one value can update both the minimum and the maximum, as the first digit does here.
5. Sum and average
There are standard algorithms to compute a sum or average 2.9.A.1. Pattern: set the sum to 0 before the loop, add each value inside the loop, divide by the count after the loop. Cast to double to avoid integer division.
Average of the multiples of 7 between 1 and 40:
int sum = 0;
int count = 0;
for (int n = 1; n <= 40; n++) {
if (n % 7 == 0) {
sum += n;
count++;
}
}
System.out.println("sum " + sum + ", count " + count);
if (count != 0) {
System.out.println(sum / count);
double average = (double) sum / count;
System.out.println(average);
}
Output:
sum 105, count 5
21
21.0
The multiples are 7, 14, 21, 28 and 35. Here the integer and the double averages agree, but try the digits of 4825: the sum is 19 and the count is 4, so 19 / 4 is 4 while (double) 19 / 4 is 4.75.
int sum = 19;
int count = 4;
System.out.println(sum / count);
System.out.println((double) sum / count);
System.out.println((double) (sum / count));
Output:
4
4.75
4.0
Watch out: (double) (sum / count) casts too late: the integer division has already thrown away the 0.75.
Exam traps
- Resetting the counter or sum inside the loop instead of before it.
- n % 2 == 1 misses negative odd numbers: -3 % 2 is -1. Use n % 2 != 0.
- Initializing a maximum to 0 or a minimum to 0 gives a wrong answer for some inputs.
- Using > versus >= when tracking a maximum changes which position is kept when there is a tie.
- The digit loop while (n > 0) runs zero times when n is 0, so the number 0 is counted as having 0 digits.
2.10 Implementing String Algorithms
Big ideaMost string problems are a loop over substrings plus a test, a counter, or a string you build up.
1. Traversing a string
String algorithms work by string traversal: a loop visits each position, and substring takes out the part to test. A single character at index i is str.substring(i, i + 1). Indexes run from 0 to length() - 1.
String team = "Hawks";
for (int i = 0; i < team.length(); i++) {
System.out.println(i + ": " + team.substring(i, i + 1));
}
Output:
0: H
1: a
2: w
3: k
4: s
Watch out (off by one): with i <= team.length(), the last pass asks for substring(5, 6) on a 5-letter string:
String team = "Hawks";
for (int i = 0; i <= team.length(); i++) {
System.out.println(team.substring(i, i + 1));
}
Output:
H
a
w
k
s
Exception in thread "main" java.lang.StringIndexOutOfBoundsException: Range [5, 6) out of bounds for length 5
The five letters print, then the program crashes on the sixth pass. (The exact wording of the message depends on the Java version.)
2. Does a substring have a property?
There are standard string algorithms to find if one or more substrings have a particular property 2.10.A.1. Two kinds of question:
- “Is there any substring with the property?” Return
trueas soon as you find one; returnfalseonly after the loop. - “Do all substrings have the property?” Return
falseas soon as one fails; returntrueonly after the loop.
Does a word contain a double letter (the same letter twice in a row)? Each test compares the letter at i with the letter at i + 1, so the loop must stop before the last index.
public static boolean hasDoubleLetter(String word) {
for (int i = 0; i < word.length() - 1; i++) {
String here = word.substring(i, i + 1);
String next = word.substring(i + 1, i + 2);
if (here.equals(next)) {
return true;
}
}
return false;
}
System.out.println(hasDoubleLetter("balloon"));
System.out.println(hasDoubleLetter("planet"));
System.out.println(hasDoubleLetter(""));
Output:
true
false
false
Watch out: putting else { return false; } inside the loop would make the method stop after checking only the first pair, so "balloon" would return false.
Is every character a digit? "0123456789".indexOf(ch) is -1 when ch is not a digit.
public static boolean allDigits(String code) {
for (int i = 0; i < code.length(); i++) {
String ch = code.substring(i, i + 1);
if ("0123456789".indexOf(ch) == -1) {
return false;
}
}
return true;
}
System.out.println(allDigits("2026"));
System.out.println(allDigits("20x6"));
Output:
true
false
3. Counting substrings that meet a criterion
There are standard string algorithms to determine the number of substrings that meet specific criteria 2.10.A.1. Count the vowels in a sentence:
String sentence = "Game over";
int vowels = 0;
for (int i = 0; i < sentence.length(); i++) {
String ch = sentence.substring(i, i + 1);
if ("aeiouAEIOU".indexOf(ch) >= 0) {
vowels++;
}
}
System.out.println("Vowels: " + vowels);
Output:
Vowels: 4
To count a pattern longer than one letter, use a window of that length. How many times does "an" appear in "banana"? The window substring(i, i + 2) needs i + 2 <= length(), so the loop condition is i <= word.length() - 2.
String word = "banana";
int count = 0;
for (int i = 0; i <= word.length() - 2; i++) {
if (word.substring(i, i + 2).equals("an")) {
count++;
}
}
System.out.println(count);
Output:
2
| i | substring(i, i + 2) | equals “an”? | count |
|---|---|---|---|
| 0 | ba | no | 0 |
| 1 | an | yes | 1 |
| 2 | na | no | 1 |
| 3 | an | yes | 2 |
| 4 | na | no | 2 |
Watch out (String comparison with ==): this version compiles and runs, but == compares references, and every call to substring here returns a new String object.
String word = "banana";
int count = 0;
for (int i = 0; i < word.length(); i++) {
if (word.substring(i, i + 1) == "a") {
count++;
}
}
System.out.println(count);
Output:
0
4. Reversing a string
There are standard string algorithms to create a new string with the characters reversed 2.10.A.1. Strings are immutable, so you build a new string. Adding each character to the front reverses the order:
String word = "code";
String reversed = "";
for (int i = 0; i < word.length(); i++) {
reversed = word.substring(i, i + 1) + reversed;
}
System.out.println(reversed);
Output:
edoc
| i | letter | reversed after this iteration |
|---|---|---|
| 0 | c | c |
| 1 | o | oc |
| 2 | d | doc |
| 3 | e | edoc |
The other way: traverse from the last index down to 0 and add each character to the end. Start at length() - 1, not length().
String word = "code";
String reversed = "";
for (int i = word.length() - 1; i >= 0; i--) {
reversed += word.substring(i, i + 1);
}
System.out.println(reversed);
Output:
edoc
5. Combining the algorithms
Original algorithms often combine the standard ones. A palindrome reads the same forward and backward: reverse the word, then compare with equals.
public static boolean isPalindrome(String word) {
String reversed = "";
for (int i = 0; i < word.length(); i++) {
reversed = word.substring(i, i + 1) + reversed;
}
return reversed.equals(word);
}
System.out.println(isPalindrome("level"));
System.out.println(isPalindrome("lever"));
Output:
true
false
Another combination: remove every space from a string by building a new one that skips spaces.
String phrase = "a b c";
String result = "";
for (int i = 0; i < phrase.length(); i++) {
String ch = phrase.substring(i, i + 1);
if (!ch.equals(" ")) {
result += ch;
}
}
System.out.println(result + " (" + result.length() + " letters)");
Output:
abc (3 letters)Exam traps
- Comparing substrings with == compiles but gives false, so counts come out as 0.
- i <= str.length() in the loop condition throws a StringIndexOutOfBoundsException on the last pass.
- Comparing each letter with the next one: stop at length() - 1, not length().
- Returning false inside the loop on the first mismatch is correct only for an "every" check, never for an "any" check.
2.11 Nested Iteration
Big ideaIn nested loops, the inner loop finishes a complete run for every single step of the outer loop.
1. A loop inside a loop
A nested iteration statement is a loop placed inside the body of another loop. The order is strict: once the inner loop starts, it finishes every one of its iterations, and only then can the outer loop go on to its next iteration 2.11.A.1.
for (int round = 1; round <= 2; round++) {
for (int player = 1; player <= 3; player++) {
System.out.println("Round " + round + ", player " + player);
}
System.out.println("End of round " + round);
}
Output:
Round 1, player 1
Round 1, player 2
Round 1, player 3
End of round 1
Round 2, player 1
Round 2, player 2
Round 2, player 3
End of round 2
The inner loop ran all 3 players before the outer loop moved from round 1 to round 2, and it started again at player 1 in round 2. The inner body ran 2 times 3 = 6 times.
2. Rows and columns
Think of the outer loop as rows and the inner loop as columns. print stays on the same line; println after the inner loop ends the row.
for (int row = 1; row <= 3; row++) {
for (int col = 1; col <= 4; col++) {
System.out.print(row * col + " ");
}
System.out.println();
}
Output:
1 2 3 4
2 4 6 8
3 6 9 12
Watch out: a per-row result must be reset inside the outer loop. This code totals each row of the table above:
for (int row = 1; row <= 3; row++) {
int rowTotal = 0;
for (int col = 1; col <= 4; col++) {
rowTotal += row * col;
}
System.out.println("Row " + row + " total: " + rowTotal);
}
Output:
Row 1 total: 10
Row 2 total: 20
Row 3 total: 30
If int rowTotal = 0; were moved above the outer loop, the totals would pile up: 10, 30, 60.
3. Inner bounds that depend on the outer loop
When the inner loop’s Boolean expression uses the outer loop control variable, each row has a different length. A triangle:
for (int i = 1; i <= 4; i++) {
for (int j = 1; j <= i; j++) {
System.out.print("*");
}
System.out.println();
}
Output:
*
**
***
****
| i | inner loop values of j | stars printed |
|---|---|---|
| 1 | 1 | 1 |
| 2 | 1, 2 | 2 |
| 3 | 1, 2, 3 | 3 |
| 4 | 1, 2, 3, 4 | 4 |
The inner body ran 1 + 2 + 3 + 4 = 10 times. Changing the inner loop to start at j = i and run while j <= 4 would print the rows in the other order: 4, 3, 2, 1 stars.
A number pattern built the same way:
for (int i = 1; i <= 3; i++) {
for (int j = i; j <= 3; j++) {
System.out.print(j);
}
System.out.println();
}
Output:
123
23
3
4. Checking pairs in a string
Nested loops can compare every character with every later character. Does a code contain any repeated letter? The inner loop starts at j = i + 1, so each pair is checked once and no letter is compared with itself.
String code = "PLANET";
boolean repeat = false;
for (int i = 0; i < code.length(); i++) {
for (int j = i + 1; j < code.length(); j++) {
if (code.substring(i, i + 1).equals(code.substring(j, j + 1))) {
repeat = true;
}
}
}
System.out.println("Repeat: " + repeat);
Output:
Repeat: false
With "LETTER" instead, the code prints Repeat: true (E and T both repeat). For a 6-letter string the inner body runs 5 + 4 + 3 + 2 + 1 = 15 times.
A while loop can be nested in a for loop too. Count the digits of each number from 8 to 12:
for (int n = 8; n <= 12; n++) {
int temp = n;
int digits = 0;
while (temp > 0) {
digits++;
temp /= 10;
}
System.out.print(digits + " ");
}
System.out.println();
Output:
1 1 2 2 2
The copy temp matters: dividing n itself would change the outer loop control variable and break the outer loop.
5. Common nested loop errors
Using the same name for both loop control variables does not compile when both are declared in their headers:
for (int i = 0; i < 3; i++) {
for (int i = 0; i < 2; i++) {
System.out.print(i);
}
}
error: variable i is already defined in method main(String[])
Using the outer variable in the inner header by mistake (j < i when you meant j < 3) changes how many times the inner body runs. The first row can even run zero times:
int count = 0;
for (int i = 0; i < 3; i++) {
for (int j = 0; j < i; j++) {
count++;
}
}
System.out.println(count);
Output:
3
With j < 3 the count would be 9. Here the inner loop runs 0, 1 and 2 times. To check nested loops, write a small table with one row per outer iteration and list what the inner loop does in that row.
Exam traps
- println inside the inner loop prints one item per line; println after the inner loop ends each row.
- A row total declared before the outer loop keeps growing instead of starting again for each row.
- Reusing i for both loops does not compile when both are declared in their headers.
- When the inner loop starts at j = i, the rows get shorter; trace the first and last rows.
2.12 Informal Run-Time Analysis
Big ideaCount how many times the loop body runs, and you can compare the speed of two algorithms without running them.
1. What a statement execution count is
A statement execution count tells you how many times a statement is executed by the program. You usually calculate it informally, by tracing and analyzing the iterative statements 2.12.A.1. A counter variable can check your count, but on the exam you work it out by reasoning.
int total = 0;
for (int i = 0; i < 5; i++) {
total += i;
}
System.out.println(total);
Output:
10
| Statement | Executions | Why |
|---|---|---|
int total = 0; |
1 | before the loop |
int i = 0 |
1 | initialization runs once |
i < 5 |
6 | true for i = 0 to 4, false for i = 5 |
total += i; |
5 | once per iteration |
i++ |
5 | after each iteration |
System.out.println(total); |
1 | after the loop |
2. Counting a single loop quickly
Find the number of iterations, then the body count follows. Trace the first value, the last value that passes the check, and the step.
| Loop header | Values | Body executions |
|---|---|---|
for (int i = 0; i < n; i++) |
0 to n - 1 | n |
for (int i = 1; i <= n; i++) |
1 to n | n |
for (int i = 0; i <= n; i++) |
0 to n | n + 1 |
for (int i = 2; i < n; i++) |
2 to n - 1 | n - 2 |
for (int i = 0; i < n; i += 2) |
0, 2, 4, … | n / 2, rounded up |
A statement inside an if runs only when the condition is true:
int printed = 0;
for (int k = 1; k <= 20; k++) {
if (k % 3 == 0) {
System.out.print(k + " ");
printed++;
}
}
System.out.println();
System.out.println("print ran " + printed + " times");
Output:
3 6 9 12 15 18
print ran 6 times
The loop body (the if test) ran 20 times, but the print ran only 6 times: once for each multiple of 3 from 1 to 20.
3. Loops that double or halve
When the loop control variable doubles or halves, the count grows very slowly as the bound grows.
int n = 100;
int count = 0;
for (int i = 1; i < n; i *= 2) {
count++;
}
System.out.println(count);
Output:
7
The values of i are 1, 2, 4, 8, 16, 32, 64, so the body runs 7 times. If n were 1000, the body would run only 10 times. A while loop that halves its variable behaves the same way:
int size = 1000;
int halvings = 0;
while (size > 1) {
size /= 2;
halvings++;
}
System.out.println(halvings);
Output:
9
size goes 1000, 500, 250, 125, 62, 31, 15, 7, 3, 1 (integer division), which is 9 halvings.
4. Nested loops: multiply or add up
Sequential loops add their counts; nested loops multiply them when the bounds are fixed.
int n = 4;
int a = 0;
int b = 0;
for (int i = 0; i < n; i++) {
a++;
}
for (int j = 0; j < n; j++) {
a++;
}
for (int i = 0; i < n; i++) {
for (int j = 0; j < n; j++) {
b++;
}
}
System.out.println("sequential: " + a + ", nested: " + b);
Output:
sequential: 8, nested: 16
When the inner bound depends on the outer variable, add the inner count for each outer iteration:
int n = 5;
int count = 0;
for (int i = 0; i < n; i++) {
for (int j = 0; j < i; j++) {
count++;
}
}
System.out.println(count);
Output:
10
| i | inner iterations (j < i) |
|---|---|
| 0 | 0 |
| 1 | 1 |
| 2 | 2 |
| 3 | 3 |
| 4 | 4 |
| total | 0 + 1 + 2 + 3 + 4 = 10 |
In general this shape runs n(n - 1) / 2 times. A statement placed after the inner loop but inside the outer loop runs only n times.
5. Comparing run times informally
Two code segments can produce the same result with very different execution counts. Comparing the counts is an informal run-time comparison 2.12.A.1. Both segments below count the divisors of 36.
Segment A tests every number from 1 to 36:
int n = 36;
int divisors = 0;
int checks = 0;
for (int d = 1; d <= n; d++) {
checks++;
if (n % d == 0) {
divisors++;
}
}
System.out.println(divisors + " divisors, " + checks + " checks");
Output:
9 divisors, 36 checks
Segment B only tests up to the square root, because divisors come in pairs (2 and 18, 3 and 12…):
int n = 36;
int divisors = 0;
int checks = 0;
for (int d = 1; d * d <= n; d++) {
checks++;
if (n % d == 0) {
if (d * d == n) {
divisors++;
} else {
divisors += 2;
}
}
}
System.out.println(divisors + " divisors, " + checks + " checks");
Output:
9 divisors, 6 checks
Same answer, 6 checks instead of 36. For n = 1,000,000, Segment A would make 1,000,000 checks and Segment B only 1,000. The segment with the smaller statement execution count for large inputs has the better run time.
Exam traps
- for (int i = 0; i <= n; i++) runs n + 1 times, not n.
- for (int i = 1; i < n; i++) runs n - 1 times.
- In nested loops, a statement placed after the inner loop (but inside the outer loop) runs only as many times as the outer loop.
- A statement inside an if in a loop can run fewer times than the loop body. Count how often the condition is true.
Notes done
Now check yourself
Close the notes and take each topic's checkpoint. Retrieval practice works better for memory than reading again.