Skip to content

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.

Code
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.

Code
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.

Code
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.

CheckResultWhat happens
score >= 9085 >= 90 is falseskip this block
score >= 8085 >= 80 is trueprint Grade: B
score >= 70never evaluated—
elsenever reached—
One value walked through the ladder. The chain ends at the first check that passes.

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.

Code
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.

Code
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:

Code
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.

Code
// 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.

Code
// 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:

Code
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 to x and then tests the value it just assigned.
  • if (x == 10) — CORRECT. This asks "is x equal 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:

Code
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():

Code
if (stringA.equals(stringB)) // Correct way to compare text in Java

The 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

Code
Integer a = 127, b = 127;System.out.println(a == b);  // true Integer x = 128, y = 128;System.out.println(x == y);  // false

Same 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:

ValuePythonJavaScript
0falsyfalsy
"" (empty string)falsyfalsy
None / null / undefinedfalsyfalsy
NaN—falsy
[] (empty list or array)falsytruthy
{} (empty dict or object)falsytruthy
"0" (the string zero)truthytruthy
The same two values, opposite answers. An empty container is false in Python and true in JavaScript.

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.

Code
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.

Code
if (isValid == true)   // redundantif (isValid)           // say what you mean if (isValid == false)  // redundantif (!isValid)          // better

The same idea applies to returns:

Code
// 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:

Code
int max;if (a > b) {    max = a;} else {    max = b;}

Ternary equivalent:

Code
int max = (a > b) ? a : b;

Because ternaries are so compact, it is possible to nest them:

Code
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):

Code
void processOrder(Order order) {    if (order != null) {        if (order.isPaid) {            // 50 lines of processing code        }    }}

Good (Guard clauses):

Code
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 elif instead of else 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.

Code
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 else case when a fallback is strictly required by the business logic.
  • Off-by-one at the boundary. if (score > 90) and if (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, where left < right and left <= right produce different algorithms.

Note

These two lines shipped in Apple's TLS code in 2014, as CVE-2014-1266:

Code
if ((err = SSLHashSHA1.update(&hashCtx, &signedParams)) != 0)    goto fail;    goto fail;   // not conditional, and always runs

The 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

  1. What is the difference between if (x = 10) and if (x == 10)?
  2. Why is it dangerous to write if (node.value == 5 && node != null)?
  3. What is a "Guard Clause" and why does it make code cleaner?
  4. In Java, what does new String("hello") == new String("hello") evaluate to, and what should you write instead?
  5. In Python, what does if 0: evaluate to, and why?

6. What does this print?

Code
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?

Code
values = []if values:    print("truthy")else:    print("falsy")
Show answer
  1. = 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 unless x is already a boolean.
  2. Because && evaluates left to right. node.value is read before node != null is ever reached, so a null node throws a NullPointerException. The guard has to come first.
  3. 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 if blocks.
  4. false. == compares references, and new forces two separate objects. Write a.equals(b). Note that "hello" == "hello" written with plain literals is true, 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.
  5. false. Python treats 0 as falsy, along with "", None, and empty collections. Everything else is truthy.
  6. A, B and C, on three lines. Without braces only the first statement belongs to the if, so B and C print no matter what x is. The indentation is a lie the compiler never reads.
  7. falsy. An empty list is falsy in Python. The same test in JavaScript prints truthy, 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, and else create 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

Code
Operators     ↓Conditions     ↓Loops     ↓Control Flow     ↓Functions

Conditions 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.