Conditions
Why Do We Need Conditions?
Code normally executes linearly, line by line, from top to bottom. But a linear program is rigid. It cannot react to user input, it cannot handle errors, and it cannot adapt to different data.
If you are building a login system, the program must do one thing if the password is correct, and a completely different thing if it is wrong. To make code dynamic, we need the ability to fork the execution path. Conditions are the track switches of programming: they let a program choose different paths based on what is true.
What Is a Condition?
Definition
A condition is simply a question the computer can answer with yes or no.
Based on that answer, the program decides whether or not to execute a specific block of code.
The question itself is a Boolean expression: any combination of variables and operators that boils down to a single Boolean value.
score >= 50(Evaluates to true or false)userRole == "admin"(Evaluates to true or false)isLoggedIn(Already a Boolean variable, evaluates to true or false)
The if Statement
The if statement is the most fundamental decision-making structure. It checks a condition; if the condition is true, the code inside its block runs. If false, the computer skips the block entirely.
if (score >= 50) { System.out.println("You passed!");}The if–else Statement
Often, you need a fallback. The else block guarantees that exactly one path will execute. If the if condition is false, the else block catches the execution.
if (score >= 50) { System.out.println("You passed!");} else { System.out.println("You failed. Try again.");}The else-if Ladder
When you have more than two mutually exclusive outcomes, you chain conditions using else if. The computer checks them from top to bottom and executes the first one that is true, skipping the rest.
if (score >= 90) { System.out.println("Grade: A");} else if (score >= 80) { System.out.println("Grade: B");} else if (score >= 70) { System.out.println("Grade: C");} else { System.out.println("Grade: F");}Let's run it with score = 85.
| Check | Result | What happens |
|---|---|---|
| score >= 90 | 85 >= 90 is false | skip this block |
| score >= 80 | 85 >= 80 is true | print Grade: B |
| score >= 70 | never evaluated | — |
| else | never reached | — |
Notice that the third and fourth checks never run at all. Once a branch matches, the entire rest of the ladder is skipped. That is not an optimization: it is the defining behavior of else if.
A useful consequence: on the second branch, score >= 80 really means "80 or above, and below 90", because anything 90 or above was already caught and consumed by the branch above it. You never have to write the upper bound. The ladder has already narrowed the range for you.
Now watch what happens if the order is wrong.
if (score >= 70) { System.out.println("Grade: C");} else if (score >= 80) { System.out.println("Grade: B");} else if (score >= 90) { System.out.println("Grade: A");}Run this with score = 95. The first check, 95 >= 70, is true. The student gets a C. The A and B branches are unreachable, not just for 95, but for every score above 70.
This code has no syntax error. It compiles. It runs. It is simply wrong, and it will pass a test suite that only checks failing grades. In a ladder of overlapping ranges, order is the logic. Go from the most restrictive condition to the least.
When every branch compares the same variable against a fixed value, there is a cleaner form: see Switch Statements.
Nested Conditions
You can place an if statement inside another if statement. This is called nesting.
if (hasTicket) { if (age >= 18) { System.out.println("Welcome to the 18+ section."); } else { System.out.println("Welcome to the general section."); }}Watch out
Deeply nested conditions (the "arrow anti-pattern," where code indentations look like >) can become difficult to read and maintain. When nesting starts to make the logic hard to follow, consider combining conditions, extracting a function, or using guard clauses.
The dangling else. Nesting without braces has a trap of its own:
if (hasTicket) if (age >= 18) System.out.println("18+ section");else System.out.println("No ticket");Which if does that else belong to? The indentation says the outer one. The compiler says the inner one: an else binds to the nearest unmatched if. A person without a ticket prints nothing at all. Braces make the answer visible instead of a rule you have to remember.
Combining Conditions
Instead of nesting, you can often evaluate multiple constraints in a single line using logical operators. This flattens your code and makes it much easier to read.
// Better than nestingif (hasTicket && age >= 18) { System.out.println("Welcome to the 18+ section.");}The three you will reach for constantly, covered in full in Operators:
&&(AND): both sides must be true.||(OR): at least one side must be true.!(NOT): reverses the condition.if (!isRaining)means "if it is not raining."
Parentheses Make the Grouping Explicit
When mixing && and ||, the order of operations can cause unpredictable behavior. Always use parentheses to group your logic explicitly.
// Confusing: do they need to be VIP AND over 18? Or just VIP?if (age > 18 && hasTicket || isVIP) // Safe and explicitif ((age > 18 && hasTicket) || isVIP)Short-Circuit Evaluation
&& and || evaluate from left to right and stop the moment the answer is settled. Operators covers why. What matters here is what that lets a condition do.
In Data Structures, you will constantly check that an object exists before reading anything out of it:
if (node != null && node.value == target) { // We found the target!}If node is null, the left side is false and && never evaluates the right side at all. Swap the order to if (node.value == target && node != null) and the program throws a NullPointerException immediately, trying to read a field of nothing.
Comparing Things Correctly
The single most common typo in programming happens right here:
if (x = 10)— WRONG. This assigns 10 toxand then tests the value it just assigned.if (x == 10)— CORRECT. This asks "isxequal to 10?"
What happens next depends on the language. In C and C++ the first line compiles, because the assigned value is converted to a Boolean, so if (x = 10) is always true and if (x = 0) is always false. In Java it does not compile at all unless x is already a boolean, which is precisely why Java made that choice. Operators covers the mechanics of = against ==.
Comparing Floats
Never compare floats with ==. Floating-point numbers are approximations: 0.1 + 0.2 is 0.30000000000000004, not 0.3. Compare the difference against a tolerance instead:
if (Math.abs(a - b) < 1e-9)This fixed tolerance works when your numbers are in a predictable range, which covers nearly all DSA problems, where the problem statement usually tells you the precision required. It is not a universal rule: for very large values the tolerance has to scale with the magnitude of the numbers. Comparing floating-point values correctly in general is genuinely hard, and it is a topic for a later chapter.
Comparing Strings in Java
Writing if (stringA == stringB) checks whether they are the exact same object, not whether they contain the same text. You must use .equals():
if (stringA.equals(stringB)) // Correct way to compare text in JavaThe same trap exists in C with char*, where == compares addresses, so use strcmp there. It does not apply to C++ std::string, which overloads == to compare contents.
Comparing Boxed Integers in Java
Integer a = 127, b = 127;System.out.println(a == b); // true Integer x = 128, y = 128;System.out.println(x == y); // falseSame code, different numbers, different answer. Java caches boxed Integer objects from -128 to 127 and reuses them, so == accidentally appears to work for small values. Above 127 you get separate objects and it stops working. Use .equals(), or unbox to int. This is what makes == on objects dangerous: it fails intermittently, which is worse than failing always.
Truthy and Falsy Values
Languages split into two camps here.
Java requires a real boolean. if (5) does not compile. The condition must already be of type boolean.
C, C++, Python and JavaScript accept other values and decide for themselves whether the value counts as true. C in its original form had no boolean type at all: a condition was just a number, and zero meant false. (C99 added _Bool and <stdbool.h>, and C23 made bool a keyword.) C++ does have a real bool, but any expression in a condition is contextually converted to it, which is why if (ptr) and if (count) are everyday C++.
Which values count as false is then each language's own decision, and Python and JavaScript disagree:
| Value | Python | JavaScript |
|---|---|---|
| 0 | falsy | falsy |
| "" (empty string) | falsy | falsy |
| None / null / undefined | falsy | falsy |
| NaN | — | falsy |
| [] (empty list or array) | falsy | truthy |
| {} (empty dict or object) | falsy | truthy |
| "0" (the string zero) | truthy | truthy |
In Python, "empty" means false. In JavaScript the false values are false, 0, -0, 0n, "", null, undefined and NaN; everything else is truthy, because an empty array is still an object and every object is truthy.
In Python this is customizable: a class that defines __bool__ or __len__ decides its own truthiness.
name = ""if name: print("We have a name!")else: print("Name is empty.") # This will print, because "" is falsy.Boolean Zen
A condition already evaluates to true or false. Comparing it to true adds a step that does nothing.
if (isValid == true) // redundantif (isValid) // say what you mean if (isValid == false) // redundantif (!isValid) // betterThe same idea applies to returns:
// Verboseif (n % 2 != 0) { return true; } else { return false; } // The condition IS the answerreturn n % 2 != 0;There is a second reason to write conditions this way. if (isValid = true) does compile in Java, because isValid is already a boolean. It is the one case where Java's protection against = in a condition fails.
Flipping a stored boolean is the same idea in one line: isLightOn = !isLightOn;
The Conditional / Ternary Operator
The ternary operator (? :) is a one-line shorthand for a simple if-else statement.
Standard:
int max;if (a > b) { max = a;} else { max = b;}Ternary equivalent:
int max = (a > b) ? a : b;Because ternaries are so compact, it is possible to nest them:
String result = (score >= 90) ? "A" : (score >= 80) ? "B" : "C";Avoid deeply nested ternaries. If a ternary contains multiple conditions or becomes difficult to read, use if-else instead.
Guard Conditions and Early Returns
In a function, instead of wrapping all your logic inside a massive if statement, use a guard clause. Check for invalid conditions immediately at the top and exit the function. This acts like a bouncer at a club ("fail fast") and keeps your core code un-nested.
Bad (Deeply nested):
void processOrder(Order order) { if (order != null) { if (order.isPaid) { // 50 lines of processing code } }}Good (Guard clauses):
void processOrder(Order order) { if (order == null) return; // Guard if (!order.isPaid) return; // Guard // 50 lines of processing code un-nested and clean}Conditions in DSA
In algorithms, conditional logic is everything:
- Array Bounds Checking:
if (index >= 0 && index < array.length)guards every read. Both halves are needed, and&&checks them in the order you wrote them. - Recursion Base Cases:
if (node == null) return;is the condition that tells a recursive function to stop calling itself. Without one it never stops.
Language Differences
- Python: Uses
elifinstead ofelse if. Also strictly requires a colon:and indentation instead of curly braces{}. - JavaScript: Has
==(loose equality) and===(strict equality). You should exclusively use===in JS to avoid bizarre type-coercion bugs. - Java/C/C++: Require parentheses around the condition:
if (x > 5). Python does not:if x > 5:.
Common Mistakes
Watch out
The Single-Line Trap (Missing Braces): In C++/Java, if you omit the {} brackets, the if statement only applies to the very next line.
if (isSecure) enableEncryption(); sendData(); // BUG: This line runs regardless of the condition!Always use braces. They make the intended scope explicit and reduce the risk of accidentally changing which statements belong to the condition.
- Using
=instead of==. - Comparing Strings with
==in Java/C. - Forgetting the
elsecase when a fallback is strictly required by the business logic. - Off-by-one at the boundary.
if (score > 90)andif (score >= 90)differ for exactly one input: 90. That one value is where conditional bugs hide, because it is also the value nobody tests. When you write a comparison, deliberately ask what happens at the boundary, not just above and below it. This becomes critical in Binary Search, whereleft < rightandleft <= rightproduce different algorithms.
Note
These two lines shipped in Apple's TLS code in 2014, as CVE-2014-1266:
if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0) goto fail; goto fail; // not conditional, and always runsThe second goto fail is indented as though it belongs to the if, but with no braces it is not part of the condition at all. The code jumped past the remaining signature checks and returned success, so affected iOS and macOS devices accepted forged certificates. Two braces would have prevented it.
Quick Check
- What is the difference between
if (x = 10)andif (x == 10)? - Why is it dangerous to write
if (node.value == 5 && node != null)? - What is a "Guard Clause" and why does it make code cleaner?
- In Java, what does
new String("hello") == new String("hello")evaluate to, and what should you write instead? - In Python, what does
if 0:evaluate to, and why?
6. What does this print?
int x = 5;if (x > 3) System.out.println("A"); System.out.println("B");System.out.println("C");7. What does this print, and what would the same test print in JavaScript?
values = []if values: print("truthy")else: print("falsy")Show answerHide answer
=assigns and then tests the value it assigned;==compares two values and tests the answer. In C and C++ the first one compiles and is always true. In Java it does not compile at all unlessxis already aboolean.- Because
&&evaluates left to right.node.valueis read beforenode != nullis ever reached, so a nullnodethrows aNullPointerException. The guard has to come first. - A check at the top of a function that catches an invalid case and returns immediately. It keeps the real work at one level of indentation instead of burying it inside a pyramid of nested
ifblocks. false.==compares references, andnewforces two separate objects. Writea.equals(b). Note that"hello" == "hello"written with plain literals istrue, because the compiler puts identical literals in one shared object. That is exactly what makes==on strings dangerous: it can appear to work right up until the strings come from user input or a file.false. Python treats0as falsy, along with"",None, and empty collections. Everything else is truthy.A,BandC, on three lines. Without braces only the first statement belongs to theif, soBandCprint no matter whatxis. The indentation is a lie the compiler never reads.falsy. An empty list is falsy in Python. The same test in JavaScript printstruthy, because an empty array is still an object and every object is truthy.
Quick Recap
- Conditions allow code to fork into different execution paths based on Boolean expressions.
if,else if, andelsecreate decision chains.- Use logical operators to combine conditions, and always use parentheses for clarity.
- Short-circuit evaluation (
&&) safely prevents crashes when checking null objects. - Guard clauses use early returns to flatten code and prevent deep nesting.
- Never compare floating-point numbers with
==, and use.equals()for Strings in strict languages like Java.
Where This Leads
Operators ↓Conditions ↓Loops ↓Control Flow ↓FunctionsConditions allow your code to make decisions, but what if you need to make the same decision thousands of times? What if you need to search through 10,000 users to apply that condition? To repeat actions efficiently, we must move to the next fundamental building block: Loops.
Key takeaway
Conditions transform your program from a rigid, single-track list of instructions into a dynamic system capable of reacting, validating, and making complex logical decisions on the fly.