search
  • Sign In
  • Sign Up
Password reset successful

Follow the proiects vou are interested in andi aet the latestnews about them taster

Table of Contents
Understand the equals method and its role in collections
Problems caused by inappropriate equals implementation
Correctly override the equals method
The necessity of hashCode method
Best practices for using Random objects
Home Java javaTutorial Overriding the equals method in Java: Avoiding hidden pitfalls in collection operations

Overriding the equals method in Java: Avoiding hidden pitfalls in collection operations

Dec 01, 2025 pm 01:45 PM

Overriding the equals method in Java: Avoiding hidden pitfalls in collection operations

In Java development, correctly overriding the equals method is crucial for customizing the behavior of objects, especially when interacting with collection classes such as LinkedList. This article will delve into how improper equals implementation can lead to unexpected errors in collection operations such as remove, such as removing the wrong object or causing data inconsistency. We will provide examples of correct equals overriding and emphasize its impact on collection behavior. We will also discuss the necessity of hashCode and best practices for using Random objects to ensure the robustness and correctness of the code.

Understand the equals method and its role in collections

The equals method is a core method in the Object class, used to determine whether two objects are "equal". When we create a custom class and want to determine equality based on specific properties of the object instead of memory address, we need to override the equals method. For example, in a poker game, we may need to determine whether two cards are the same.

Many collection classes in Java, such as ArrayList, LinkedList, HashSet, etc., will internally call the equals method of the object to determine the equality of the objects when performing operations such as search, addition, and deletion. For example, the LinkedList's remove(Object o) method iterates through the elements in the list and uses o.equals(element) to find the element to remove. If the equals method is implemented incorrectly, it will cause the collection to behave abnormally.

Problems caused by inappropriate equals implementation

Consider the Card class in a poker game. If we only judge whether two cards are equal based on the number of the card (cardNum, which represents the size of the card, such as A, 2, K) and ignore the suit, then problems will arise.

The following is an example of an inappropriate equals method:

 public class Card {
    private int cardNum; // card number (for example 1-13)
    private String suit; // suit (such as "Hearts", "Spades")

    // Constructors and other methods omit @Override
    public boolean equals(Object obj) {
        if (!(obj instanceof Card)) {
            return false;
        } else {
            Card otherCard = (Card) obj;
            // Error: only compare card numbers, ignore suits return otherCard.cardNum == this.cardNum;
        }
    }

    // Other methods, such as toString()
    @Override
    public String toString() {
        // Assume that a string such as "7 of Clubs" can be returned based on cardNum and suit return cardNum " of " suit;
    }
}

When using the equals method above, 7 of Clubs will be considered equal to 7 of Spades. This is obviously not true in the actual game of poker since they are two different cards.

Now, let's look at the deal() method in a Deck class, which randomly selects a card from the deck and removes it:

 public class Deck {
    private LinkedList<card> m_cards; // All cards in the deck // Constructor omitted, assuming m_cards has been initialized and contains all unique cards public Card deal() {
        Random rand = new Random(); // Create a new Random instance with each call, which is not a best practice Card randomCard;

        // Randomly select a card randomCard = m_cards.get(rand.nextInt(m_cards.size()));
        // Remove this card m_cards.remove(randomCard); // Here the equals method of Card will be called return randomCard;
    }
}</card>

When m_cards.remove(randomCard) is called, LinkedList will use randomCard's equals method to traverse its internal list, looking for elements that are "equal" to randomCard. If our equals method only compares the numbers on the cards, then:

  1. Suppose the randomCard is 7 of Clubs.
  2. m_cards.remove(randomCard) may find 7 of Clubs in the deck and remove it.
  3. But if there are 7 of Spades, 7 of Hearts, etc. in the deck, since their cardNum is the same as 7 of Clubs, when the remove method encounters these cards during traversal, it may also judge that they are equal to the randomCard and mistakenly remove one of them (such as 7 of Hearts) instead of the actual 7 of Clubs drawn.
  4. This will cause the wrong cards to be removed from the deck. Even after the player gets the card, there will still be cards with the same face but different suits in the deck, resulting in repeated cards being dealt and destroying the game logic.

Correctly override the equals method

To solve this problem, the equals method must strictly define the "equality" of objects. For Card objects, this means that not only the numbers on the cards must be the same, but the suits must also be the same.

The following is the correct implementation of the equals method in the Card class:

 import java.util.Objects; //Introduce the Objects class to simplify the implementation of equals and hashCode public class Card {
    private int cardNum; // card number (for example 1-13)
    private String suit; // suit (such as "Hearts", "Spades")

    public Card(int cardNum, String suit) {
        this.cardNum = cardNum;
        this.suit = suit;
    }

    // Method to get the card number public int getCardNum() {
        return cardNum;
    }

    // Method to get the suit public String getSuit() {
        return suit;
    }

    @Override
    public boolean equals(Object obj) {
        // 1. Reflexivity: the object must be equal to itself if (this == obj) {
            return true;
        }
        // 2. Type check: If obj is null or not of Card type, it is not equal if (obj == null || getClass() != obj.getClass()) {
            return false;
        }
        // 3. Forced type conversion Card otherCard = (Card) obj;
        // 4. Compare all key attributes return cardNum == otherCard.cardNum &amp;&amp;
               Objects.equals(suit, otherCard.suit); // Use Objects.equals to handle null safety}

    // When rewriting the equals method, you must also rewrite the hashCode method @Override
    public int hashCode() {
        return Objects.hash(cardNum, suit);
    }

    @Override
    public String toString() {
        // Assume that a string such as "7 of Clubs" can be returned based on cardNum and suit return cardNum " of " suit;
    }
}

The implementation principles of the correct equals method:

  1. Reflexive : x.equals(x) must return true for any non-null reference value x.
  2. Symmetric : For any non-null reference values ​​x and y, x.equals(y) returns true if and only if y.equals(x) returns true.
  3. Transitive : For any non-null reference values ​​x, y, and z, if x.equals(y) returns true, and y.equals(z) returns true, then x.equals(z) must also return true.
  4. Consistent : For any non-null reference values ​​x and y, multiple calls to x.equals(y) should always return the same result as long as the information used in the equals comparison has not been modified.
  5. Provisions for null : x.equals(null) must return false for any non-null reference value x.

In the above example, we compare the two key properties cardNum and suit. Objects.equals(suit, otherCard.suit) is a recommended approach and can safely handle the case where suit is null.

The necessity of hashCode method

When overriding the equals method, it is strongly recommended (in fact required by the specification) to also override the hashCode method. This is because:

  • Convention : If two objects are judged to be equal through the equals method, then their hashCode method must return the same value.
  • Collection performance : Hash-based collections (such as HashSet, HashMap) rely on hashCode to determine the location of object storage. If equals are equal but hashCode is different, these collections will not be able to find or store objects correctly, resulting in functional errors or performance degradation.

The Objects.hash() method provides a concise and secure way to generate hash codes.

Best practices for using Random objects

In the original deal() method, a new Random instance is created with each call: Random rand = new Random();. This is not a good practice for the following reasons:

  • Performance overhead : Frequently creating Random objects will bring unnecessary performance overhead.
  • Insufficient randomness : If Random objects are created very quickly, they may use the same or very similar seeds (usually based on the current time), resulting in a generated random number sequence that is not random enough, or even generates repeated numbers within a short period of time.

Best practice is to reuse a Random instance. It can be declared as a private final field at the class level and initialized in the constructor, or as a static final field if it is a static method.

Example: Reusing Random instances

 import java.util.LinkedList;
import java.util.Objects;
import java.util.Random;

public class Deck {
    private LinkedList<card> m_cards;
    // Declare a private, final Random instance and create it only once private final Random randomGenerator = new Random();

    public Deck() {
        m_cards = new LinkedList();
        // Assume 52 unique cards are initialized here // For example:
        String[] suits = {"Hearts", "Diamonds", "Clubs", "Spades"};
        for (String suit : suits) {
            for (int i = 1; i  deals(int n) {
        LinkedList<card> cardsDealt = new LinkedList<card>();
        for(int i = 0; i <p> Through the above improvements, the deal() method will use the same Random instance to ensure better randomness and reduce resource consumption.</p>
<h3> Summarize</h3>
<p> Correctly overriding the equals method is a key part of object-oriented programming in Java. A well-designed equals method should strictly define the logical equality of objects and follow its inherent five conventions. When a custom object is put into a collection, the behavior of its equals and hashCode methods will directly affect the correctness of the collection operation. For objects like Card, equality should be based on all distinguishing properties (such as card face and suit). At the same time, following the best practices for using Random objects can avoid potential randomness problems and performance overhead. Through these improvements, the logical correctness, data consistency and operating efficiency of the code can be ensured.</p></card></card></card>

The above is the detailed content of Overriding the equals method in Java: Avoiding hidden pitfalls in collection operations. For more information, please follow other related articles on the PHP Chinese website!

Statement of this Website
The content of this article is voluntarily contributed by netizens, and the copyright belongs to the original author. This site does not assume corresponding legal responsibility. If you find any content suspected of plagiarism or infringement, please contact admin@php.cn

Hot AI Tools

Undress AI Tool

Undress AI Tool

Undress images for free

AI Clothes Remover

AI Clothes Remover

Online AI tool for removing clothes from photos.

Undresser.AI Undress

Undresser.AI Undress

AI-powered app for creating realistic nude photos

ArtGPT

ArtGPT

AI image generator for creative art from text prompts.

Stock Market GPT

Stock Market GPT

AI powered investment research for smarter decisions

Popular tool

Notepad++7.3.1

Notepad++7.3.1

Easy-to-use and free code editor

SublimeText3 Chinese version

SublimeText3 Chinese version

Chinese version, very easy to use

Zend Studio 13.0.1

Zend Studio 13.0.1

Powerful PHP integrated development environment

Dreamweaver CS6

Dreamweaver CS6

Visual web development tools

SublimeText3 Mac version

SublimeText3 Mac version

God-level code editing software (SublimeText3)

How to configure Spark distributed computing environment in Java_Java big data processing How to configure Spark distributed computing environment in Java_Java big data processing Mar 09, 2026 pm 08:45 PM

Spark cannot run in local mode, ClassNotFoundException: org.apache.spark.sql.SparkSession. This is the most common first step of getting stuck: even the dependencies are not correct. Only spark-core_2.12 is written in Maven, but spark-sql_2.12 is not added. SparkSession crashes as soon as it is built. The Scala version must strictly match the official Spark compiled version - Spark3.4.x uses Scala2.12 by default. If you use spark-sqljar of 2.13, the class loader cannot directly find the main class. Practical advice: Go to mvnre

How to safely map user-entered weekday string to integer value and implement date offset operation in Java How to safely map user-entered weekday string to integer value and implement date offset operation in Java Mar 09, 2026 pm 09:43 PM

This article introduces a concise and maintainable way to map the weekday string (such as "Monday") to the corresponding serial number (1-7), and use the modulo operation to realize the forward and backward offset of any number of days (such as Monday plus 4 days to get Friday), avoiding lengthy if chains and hard-coded logic.

How to generate a list of duplicate elements using Java's Collections.nCopies_Initialization tips How to generate a list of duplicate elements using Java's Collections.nCopies_Initialization tips Mar 06, 2026 am 06:24 AM

Collections.nCopies returns an immutable view. Calling add/remove will throw UnsupportedOperationException; it needs to be wrapped with newArrayList() to modify it, and it is disabled for mutable objects.

How to use Homebrew to install Java on Mac_A must-have Java tool chain for developers How to use Homebrew to install Java on Mac_A must-have Java tool chain for developers Mar 09, 2026 pm 09:48 PM

Homebrew installs the latest stable version of openjdk (such as JDK22) by default, not the LTS version; you need to explicitly execute brewinstallopenjdk@17 or brewinstallopenjdk@21 to install the LTS version, and manually configure PATH and JAVA_HOME to be correctly recognized by the system and IDE.

What is exception masking (Suppressed Exceptions) in Java_Multiple resource shutdown exception handling What is exception masking (Suppressed Exceptions) in Java_Multiple resource shutdown exception handling Mar 10, 2026 pm 06:57 PM

What is SuppressedException: It is not "swallowed", but actively archived by the JVM. SuppressedException is not an exception loss, but the JVM quietly attaches the secondary exception to the main exception under the premise that "only one exception must be thrown" for you to verify afterwards. It is automatically triggered by the JVM in only two scenarios: one is that the resource closure in try-with-resources fails, and the other is that you manually call addSuppressed() in finally. The key difference is: the former is fully automatic and safe; the latter requires you to keep it to yourself, and it can be written as shadowing if you are not careful. try-

How to correctly implement runtime file writing in Java applications (avoiding JAR internal write failures) How to correctly implement runtime file writing in Java applications (avoiding JAR internal write failures) Mar 09, 2026 pm 07:57 PM

After a Java application is packaged as a JAR, data cannot be written directly to the resources in the JAR package (such as test.txt) because the JAR is essentially a read-only ZIP archive; the correct approach is to write variable data to an external path (such as a user directory, a temporary directory, or a configuration-specified path).

What is the underlying principle of array expansion in Java_Java memory dynamic adjustment analysis What is the underlying principle of array expansion in Java_Java memory dynamic adjustment analysis Mar 09, 2026 pm 09:45 PM

ArrayList.add() triggers expansion because grow() is called when size is equal to elementData.length. The first add allocates 10 capacity, and subsequent expansion is 1.5 times and not less than the minimum requirement, relying on delayed initialization and System.arraycopy optimization.

How to safely read a line of integer input in Java and avoid Scanner blocking How to safely read a line of integer input in Java and avoid Scanner blocking Mar 06, 2026 am 06:21 AM

This article introduces typical blocking problems when using Scanner to read multiple integers in a single line. It points out that hasNextInt() will wait indefinitely when there is no subsequent input, and recommends a safe alternative with nextLine() string splitting as the core.

Related articles