Free for students · Ad-free · WCAG 2.1 AA Compliant · Accessibility

Comparing Boolean Expressions

Lesson ~10 min read 8 MCQs

In simple terms: In simple terms, comparing Boolean expressions is about checking if two logical statements are the same, and understanding the difference between two objects being identical versus just being equivalent.

Why this matters

Imagine you're building the checkout system for a new online sneaker store based in Dallas. The marketing team wants a special promotion for the launch: "Customers get 15% off, but NOT if they are using a 'new customer' coupon AND it's a holiday weekend."

That logic feels a little tangled, right? As a programmer, you need to translate that business rule into clean, efficient code. Is there a simpler way to write !(isNewCustomerCoupon && isHolidayWeekend)? What if you need to check if the Sneaker object in the cart is the exact same one the customer first clicked on, not just another pair of the same size and model?

[[fig:tangled_logic_flow]]

These are the kinds of questions that come up every single day in software development. In this lesson, we’ll tackle them head-on. We'll learn how to simplify complex logical statements and master the crucial difference between checking if two things are identical versus just being equal.

Concept overview

flowchart TD
    A[Start: Compare obj1 and obj2] --> B{Is obj1 == obj2?};
    B -- Yes --> C[They are IDENTICAL <br> (same object in memory)];
    B -- No --> D{Do you need to check for <br> content EQUIVALENCE?};
    D -- Yes --> E[Use obj1.equals(obj2) <br> (method must be properly defined)];
    D -- No --> F[They are NOT identical];
    E --> G{Is it true?};
    G -- Yes --> H[They are EQUIVALENT];
    G -- No --> I[They are NOT equivalent];
This diagram is a flowchart that guides a user through the process of comparing two objects in Java, named obj1 and obj2. It first asks if the objects are identical using '=='. If they are, the process ends. If not, it asks if content equivalence is needed, leading to the use of the '.equals()' method.

Core explanation

Hello everyone! It’s Saavi. Today we're diving into a topic that might seem small but has huge implications for writing bug-free code: comparing Boolean expressions and objects.

What Makes Two Expressions "Equivalent"?

Let's start with a simple idea. In math, you know that 2 + 4 is equivalent to 6. They look different, but they always produce the same value.

The same concept applies to Boolean logic. Two Boolean expressions are equivalent if they evaluate to the same result (true or false) in all possible situations.

How can we prove this? We can use a truth table. It’s just an organized way to check every single combination of inputs.

For example, let's say we have a Boolean variable isRaining. Is the expression !(!isRaining) equivalent to just isRaining? Let's see:

isRaining !isRaining !(!isRaining)
true false true
false true false

Look at the first and last columns. They match perfectly! So, yes, !(!isRaining) is equivalent to isRaining. It's a clunky way of saying the same thing. Our goal as programmers is to find the simplest, most readable form.

Your Secret Weapon: De Morgan's Law

Now for the superpower that lets you simplify complex logical statements: De Morgan's Law. It sounds fancy, but it's an incredibly useful pattern for dealing with negated ANDs and ORs.

There are two rules to remember:

  1. !(a && b) is equivalent to !a || !b
  2. !(a || b) is equivalent to !a && !b

Let's break this down with an analogy. Imagine the rule for getting into a school dance is: "You must have a ticket AND be a current student." hasTicket && isStudent

Now, what if we want to express the reason someone is denied entry? That would be !(hasTicket && isStudent).

According to De Morgan's Law, this is the same as !hasTicket || !isStudent. Let's translate that to English: "You are denied entry if you do NOT have a ticket, OR you are NOT a current student." It makes perfect sense!

[[fig:demorgans_mistake]]

Comparing Objects: The Twin Analogy

Okay, let's switch gears from pure logic to objects. This is one of the most important and frequently tested concepts in AP CSA.

Imagine you have identical twins, Priya and Sofia.

  • == (the double equals sign) checks for identity. It asks, "Are these two variables pointing to the exact same person?" If you have one person and two nicknames for them, == would be true. But Priya and Sofia are two different people, so Priya == Sofia would be false.
  • .equals() (the method) checks for equivalency. It asks, "Do these two people have the same attributes?" Since Priya and Sofia are identical twins, they have the same eye color, hair color, etc. So, Priya.equals(Sofia) would be true.

In Java, an "object reference" is like a nickname for an object living in your computer's memory.

// Create two different String objects with the same content
String city1 = new String("Chicago");
String city2 = new String("Chicago");

// Create a third reference that points to the *same* object as city1
String city3 = city1;

Let's apply our twin analogy:

  • city1 == city2 evaluates to false. Why? Because new creates a brand new object in a new memory location. We have two separate, identical-looking objects. They are twins, not the same person.
  • city1 == city3 evaluates to true. Why? We didn't use the new keyword. We just said, "Make city3 another name for the object city1 is already pointing to." They are two nicknames for the same object.

So how do we check if two String objects have the same characters? We use the .equals() method.

  • city1.equals(city2) evaluates to true. The String class was written with an .equals() method that compares the actual sequence of characters. This is almost always what you want when comparing strings.

Finally, a variable might not be pointing to any object at all. In this case, its value is null. You can, and should, check for this using ==.

String name = null;
if (name == null) {
    System.out.println("No name has been assigned yet.");
}

For any custom objects you create (like a Player, Car, or Book class), you have to decide what "equals" means. Does it mean they have the same player ID? The same VIN? You would write your own .equals() method in that class to define that criteria.

See it in action

python
Line 1
Output
Click Run to see the output.

        
Try these
    © Shrutam.ai

    Worked examples

    Let's walk through a couple of examples to make these concepts concrete.

    Example 1

    Simplifying a Login Condition

    Problem: A website has a security feature. To access a sensitive page, a user must NOT meet the criteria for a "low-security" user. A low-security user is defined as someone who is isGuest OR has !hasTwoFactorAuth. Write the simplified if condition to check for a high-security user who should be granted access.

    The initial condition for denying access is: isGuest || !hasTwoFactorAuth. The condition for granting access is the negation of that: !(isGuest || !hasTwoFactorAuth).

    Solution Walkthrough:

    1. 1
      Identify the pattern
      The expression !(isGuest || !hasTwoFactorAuth) is in the form !(a || b). This is a perfect candidate for De Morgan's Law.
    2. 2
      Apply De Morgan's Law
      The rule is !(a || b) becomes !a && !b.
      • Our a is isGuest.
      • Our b is !hasTwoFactorAuth.
    3. 3
      Substitute and simplify
      • !a becomes !isGuest.
      • !b becomes !(!hasTwoFactorAuth), which simplifies to just hasTwoFactorAuth.
      • The || operator flips to &&.
    4. 4
      Combine the pieces
      The simplified expression is !isGuest && hasTwoFactorAuth.

    Example 2

    Comparing Custom Objects

    Problem: You have a simple Student class. You create two Student objects for a student named Marcus who is in 11th grade. You also create a second reference to the first object. Predict the output of the comparisons.

    // Assume a simple Student class exists
    Student student1 = new Student("Marcus", 11);
    Student student2 = new Student("Marcus", 11);
    Student student3 = student1;
    
    System.out.println(student1 == student2);
    System.out.println(student1 == student3);
    System.out.println(student1.equals(student2));

    Solution Walkthrough:

    1. Analyze student1 == student2:

      • What it does: This compares the memory addresses of student1 and student2.
      • Why it's false: The new keyword was used twice, creating two distinct Student objects in two different memory locations. They are like identical twins—they look the same but are separate entities. So, the output is false.
    2. Analyze student1 == student3:

      • What it does: This compares the memory addresses of student1 and student3.
      • Why it's true: The line Student student3 = student1; does not create a new object. It simply creates a new reference, student3, that points to the exact same object that student1 points to. They are two names for the same thing. So, the output is true.
    3. Analyze student1.equals(student2):

      • What it does: This calls the .equals() method to compare the objects for equivalency.
      • Why it's (probably) false: This is the trickiest one. This is where students often make an assumption. Unless we have specifically written an .equals() method inside the Student class that compares the name and grade, Java's default .equals() method behaves just like ==. It just checks memory addresses. Since we didn't write that method, it will compare the addresses of student1 and student2, find them different, and return false. To make this true, we would need to add a custom .equals() method to our Student class.

    Try it yourself

    Ready to test your understanding? Give these a shot.

    1. 1
      Theme Park Logic
      A new roller coaster in Seattle has a complex rule for the fast-pass lane, written by the legal team. The condition to stop someone is: !(isTallEnough && hasFastPass). Use De Morgan's law to rewrite this as a simpler, positive condition. What does the simplified rule mean in plain English?
    2. 2
      Object Puzzler
      Carlos is writing a program to track inventory. He writes the following code. Without running it, predict the output and be ready to explain why.
      StringBuilder item1 = new StringBuilder("Wrench");
      StringBuilder item2 = new StringBuilder("Wrench");
      StringBuilder item3 = item2;
      
      // Prediction 1: What will this print?
      System.out.println(item1 == item2);
      
      // Prediction 2: What will this print?
      System.out.println(item2 == item3);
      
      // Prediction 3: What will this print?
      System.out.println(item1.equals(item2));

      Hint: StringBuilder is a class like String, but its default .equals() method was not overridden. How does that affect Prediction 3?