Java Tutorial
🔍

Java equals() and hashCode() in Collections

Java equals() and hashCode()

Two methods on Object. Both inherited by every Java class. Both quietly broken in every class that never overrides them — and that brokenness only shows up at runtime, in the form of HashMap lookups that return null for keys that look identical, or HashSet that silently stores what should be duplicates.

Understanding equals() and hashCode() means understanding the contract between them, why that contract exists, and what breaks when it is violated.

What Are equals() and hashCode()?

Every Java class inherits two methods from java.lang.Object:

equals(Object obj) determines whether two object references point to logically equivalent objects. By default — when not overridden — it uses ==: identity comparison. Two different new Product("LAPTOP-001") objects are not equal by default even though they represent the same product.

hashCode() returns an int that represents the object for hashing purposes. By default, it is based on the object's memory address — a value that varies between JVM runs and has nothing to do with the object's logical content.

The diagram below shows what happens with and without overriding these methods.

WITHOUT OVERRIDE (inherits Object behaviour):
  Product p1 = new Product("LAPTOP-001", 45999.0);
  Product p2 = new Product("LAPTOP-001", 45999.0);

  p1.equals(p2)   → false  (different memory addresses)
  p1 == p2        → false  (different references)
  p1.hashCode()   → 1829164700 (some address-based number)
  p2.hashCode()   → 1347067481 (a different address-based number)

WITH OVERRIDE (based on SKU field):
  p1.equals(p2)   → true   (same SKU value)
  p1.hashCode()   → same   (same SKU produces same hash)

  HashMap result WITHOUT override:
    map.put(p1, "in stock");
    map.get(p2) → null  (p2 seen as a different key — WRONG)

  HashMap result WITH override:
    map.put(p1, "in stock");
    map.get(p2) → "in stock"  (p2 recognized as same key — CORRECT)

This is the core problem both methods exist to solve: giving hash-based collections a way to recognise logically equal objects regardless of their memory location.

The Contract Between equals() and hashCode()

The Java specification defines a contract that any correct implementation must satisfy. Violating it breaks HashMap, HashSet, and every other hash-based collection silently — no exception is thrown, the data simply disappears or duplicates where it should not.

THE CONTRACT (from java.lang.Object documentation):

Rule 1 — CONSISTENCY WITHIN A RUN:
  Multiple calls to hashCode() on the same object must return
  the same integer, as long as the fields used in equals()
  are not modified.

Rule 2 — EQUAL OBJECTS MUST HAVE EQUAL HASH CODES:
  if a.equals(b) is true, then a.hashCode() == b.hashCode() MUST be true.
  This is the critical rule — breaking it makes HashMap lose entries.

Rule 3 — UNEQUAL OBJECTS NEED NOT HAVE DIFFERENT HASH CODES:
  if a.hashCode() == b.hashCode(), a.equals(b) MAY be false.
  Collisions (same hash, different object) are allowed and handled.
  But many collisions degrade HashMap from O(1) to O(n).

Rule 4 — SYMMETRY, REFLEXIVITY, TRANSITIVITY of equals():
  a.equals(a)                          must be true  (reflexive)
  a.equals(b) == b.equals(a)           must be true  (symmetric)
  a.equals(b) && b.equals(c) implies a.equals(c)     (transitive)
  a.equals(null)                       must be false  (null-safe)

Rule 2 is the one that causes real bugs. It says: if two objects are considered equal, they MUST produce the same hash code. The reverse — equal hash codes imply equal objects — is NOT required.

How HashMap Uses Both Methods

The internal working of HashMap makes the contract concrete. Every put() and get() calls both methods.

HashMap<Product, String> catalogue = new HashMap<>();
catalogue.put(product, "in stock");
catalogue.get(anotherProduct);  ← how does this work?

STEP 1: Compute hash
  int h = anotherProduct.hashCode();
  int hash = h ^ (h >>> 16);    ← spread high bits into low bits

STEP 2: Find bucket
  int index = (capacity - 1) & hash;  ← typically 0..15 for default size

STEP 3: Search bucket chain
  For each Node in table[index]:
    if (node.hash == hash && node.key.equals(anotherProduct)):
      return node.value;   ← found!
  return null;             ← not found

The two-step process:
  hashCode() narrows the search to one bucket  → fast (O(1))
  equals()   finds the exact match in the chain → precise

If hashCode() is overridden but equals() is not, two logically equal objects land in the same bucket but equals() returns false (default identity check), so no match is found. If equals() is overridden but hashCode() is not, two equal objects may land in completely different buckets and the get() returns null.

Basic Overview — equals() from Object

The default equals() and what overriding it means, starting from the simplest case.

// File: EqualsBasicsDemo.java import java.util.Objects; public class EqualsBasicsDemo { // Simple value class — two products are equal if their SKU matches static class Product { private final String sku; private final String name; private final double price; Product(String sku, String name, double price) { this.sku = sku; this.name = name; this.price = price; } // Without this override, equals() uses identity (==) @Override public boolean equals(Object obj) { if (this == obj) return true; // same reference — trivially equal if (obj == null) return false; // null is never equal to an object if (!(obj instanceof Product other)) return false; // different class return Objects.equals(this.sku, other.sku); // compare by SKU only } @Override public String toString() { return "Product[" + sku + "]"; } } public static void main(String[] args) { Product p1 = new Product("LAPTOP-001", "Lenovo ThinkPad", 45999.0); Product p2 = new Product("LAPTOP-001", "Lenovo ThinkPad", 45999.0); Product p3 = new Product("MOUSE-007", "Logitech MX", 1299.0); System.out.println("=== Reference comparison (==) ==="); System.out.println("p1 == p2 : " + (p1 == p2)); // always false — different objects System.out.println("p1 == p1 : " + (p1 == p1)); // always true — same reference System.out.println("\n=== Logical comparison (equals) ==="); System.out.println("p1.equals(p2) : " + p1.equals(p2)); // true — same SKU System.out.println("p1.equals(p3) : " + p1.equals(p3)); // false — different SKU System.out.println("p1.equals(null): " + p1.equals(null)); // false — null safe System.out.println("\n=== Symmetry check ==="); System.out.println("p1.equals(p2) : " + p1.equals(p2)); // true System.out.println("p2.equals(p1) : " + p2.equals(p1)); // true — must match } }
Output:
=== Reference comparison (==) ===
p1 == p2 : false
p1 == p1 : true

=== Logical comparison (equals) ===
p1.equals(p2) : true
p1.equals(p3) : false
p1.equals(null): false

=== Symmetry check ===
p1.equals(p2) : true
p2.equals(p1) : true

Implementing hashCode() Correctly

hashCode() must be implemented alongside equals() — they form a pair. The standard modern approach uses Objects.hash(), which handles null fields safely and applies a good hash mix automatically.

// File: HashCodeImplementationDemo.java import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Objects; import java.util.Set; public class HashCodeImplementationDemo { static class UserAccount { private final String userId; private final String email; private final String role; UserAccount(String userId, String email, String role) { this.userId = userId; this.email = email; this.role = role; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof UserAccount other)) return false; // Two accounts are equal if userId AND email both match return Objects.equals(this.userId, other.userId) && Objects.equals(this.email, other.email); } @Override public int hashCode() { // Objects.hash() combines fields using a prime-based mix // Both fields used in equals() must appear here — no more, no less return Objects.hash(userId, email); } @Override public String toString() { return "User[" + userId + ", " + email + ", " + role + "]"; } } public static void main(String[] args) { UserAccount acc1 = new UserAccount("U001", "priya@zepto.in", "ADMIN"); UserAccount acc2 = new UserAccount("U001", "priya@zepto.in", "ADMIN"); // same logical user UserAccount acc3 = new UserAccount("U002", "rohan@zepto.in", "USER"); System.out.println("=== hashCode values ==="); System.out.println("acc1.hashCode() : " + acc1.hashCode()); System.out.println("acc2.hashCode() : " + acc2.hashCode()); // must equal acc1's hash System.out.println("acc3.hashCode() : " + acc3.hashCode()); // different — different user System.out.println("\n=== HashSet — deduplication ==="); Set<UserAccount> activeUsers = new HashSet<>(); activeUsers.add(acc1); activeUsers.add(acc2); // duplicate — add() returns false, set unchanged activeUsers.add(acc3); System.out.println("Set size: " + activeUsers.size()); // 2, not 3 activeUsers.forEach(u -> System.out.println(" " + u)); System.out.println("\n=== HashMap — key lookup ==="); Map<UserAccount, String> sessionMap = new HashMap<>(); sessionMap.put(acc1, "SESSION-TOKEN-XYZ"); // acc2 is a different object but equal to acc1 — must find the token String token = sessionMap.get(acc2); System.out.println("get(acc2) token : " + token); // SESSION-TOKEN-XYZ — not null System.out.println("get(acc3) token : " + sessionMap.get(acc3)); // null — different user } }
Output:
=== hashCode values ===
acc1.hashCode() : 1073314563
acc2.hashCode() : 1073314563
acc3.hashCode() : 1073346271

=== HashSet — deduplication ===
Set size: 2
  User[U001, priya@zepto.in, ADMIN]
  User[U002, rohan@zepto.in, USER]

=== HashMap — key lookup ===
get(acc2) token : SESSION-TOKEN-XYZ
get(acc3) token : null

Three Ways to Implement equals() and hashCode()

Option 1 — Manual Implementation

Full control, most verbose. Useful when fine-grained control over comparison or performance is needed.

// File: ManualImplementation.java import java.util.Objects; public class ManualImplementation { static class OrderItem { private final String productId; private final int quantity; private final double unitPrice; OrderItem(String productId, int quantity, double unitPrice) { this.productId = productId; this.quantity = quantity; this.unitPrice = unitPrice; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (obj == null || getClass() != obj.getClass()) return false; OrderItem other = (OrderItem) obj; return this.quantity == other.quantity && Double.compare(this.unitPrice, other.unitPrice) == 0 && Objects.equals(this.productId, other.productId); } // Manual prime-based implementation — what IDE generators produce @Override public int hashCode() { int result = Objects.hashCode(productId); result = 31 * result + quantity; result = 31 * result + Double.hashCode(unitPrice); return result; } } }

Option 2 — Objects.hash() (Recommended)

Cleaner, null-safe, and harder to get wrong. Use this in most production code.

// File: ObjectsHashDemo.java import java.util.Objects; public class ObjectsHashDemo { static class DeliveryAddress { private final String street; private final String city; private final String pincode; DeliveryAddress(String street, String city, String pincode) { this.street = street; this.city = city; this.pincode = pincode; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof DeliveryAddress other)) return false; return Objects.equals(street, other.street) && Objects.equals(city, other.city) && Objects.equals(pincode,other.pincode); } @Override public int hashCode() { // Single call — all fields combined, null-safe return Objects.hash(street, city, pincode); } } public static void main(String[] args) { DeliveryAddress addr1 = new DeliveryAddress("42 MG Road", "Bengaluru", "560001"); DeliveryAddress addr2 = new DeliveryAddress("42 MG Road", "Bengaluru", "560001"); DeliveryAddress addr3 = new DeliveryAddress("15 Linking Rd", "Mumbai", "400054"); System.out.println("addr1.equals(addr2) : " + addr1.equals(addr2)); // true System.out.println("addr1.hashCode() == addr2.hashCode(): " + (addr1.hashCode() == addr2.hashCode())); // true System.out.println("addr1.equals(addr3) : " + addr1.equals(addr3)); // false } }
Output:
addr1.equals(addr2) : true
addr1.hashCode() == addr2.hashCode(): true
addr1.equals(addr3) : false

Option 3 — Java Records (Java 16+)

Records auto-generate correct equals(), hashCode(), and toString() from all components. For immutable value classes, this is the cleanest approach.

// File: RecordEqualsDemo.java import java.util.HashSet; import java.util.Set; public class RecordEqualsDemo { // record generates equals, hashCode, toString automatically from components record Coupon(String code, int discountPercent) {} public static void main(String[] args) { Coupon c1 = new Coupon("SAVE20", 20); Coupon c2 = new Coupon("SAVE20", 20); Coupon c3 = new Coupon("FLAT50", 50); System.out.println("c1.equals(c2) : " + c1.equals(c2)); // true — auto-generated System.out.println("c1.equals(c3) : " + c1.equals(c3)); // false Set<Coupon> appliedCoupons = new HashSet<>(); appliedCoupons.add(c1); appliedCoupons.add(c2); // duplicate — rejected by HashSet appliedCoupons.add(c3); System.out.println("Applied coupons count: " + appliedCoupons.size()); // 2 } }
Output:
c1.equals(c2) : true
c1.equals(c3) : false
Applied coupons count: 2

Basic Example

import java.util.HashSet; import java.util.Objects; import java.util.Set; public class Main { public static void main(String[] args) { Point p1 = new Point(2, 3); Point p2 = new Point(2, 3); // same coordinates, different object System.out.println("p1 == p2 : " + (p1 == p2)); System.out.println("p1.equals(p2) : " + p1.equals(p2)); Set<Point> points = new HashSet<>(); points.add(p1); points.add(p2); // logically the same point — rejected as a duplicate System.out.println("Set size: " + points.size()); } } class Point { int x, y; Point(int x, int y) { this.x = x; this.y = y; } @Override public boolean equals(Object obj) { if (this == obj) return true; if (!(obj instanceof Point other)) return false; return x == other.x && y == other.y; } @Override public int hashCode() { return Objects.hash(x, y); } }
Output:
p1 == p2      : false
p1.equals(p2) : true
Set size: 1

How it works:

  1. ›p1 and p2 are two separate objects in memory, so p1 == p2 is false — reference equality only checks identity.
  2. ›equals() is overridden to compare the x and y fields instead, so p1.equals(p2) correctly reports true because they represent the same coordinates.
  3. ›hashCode() is overridden alongside equals() using Objects.hash(x, y), so p1 and p2 produce the same hash code — this is required for HashSet to recognize them as duplicates.
  4. ›Because both methods are overridden consistently, adding both points to a HashSet keeps the size at 1 instead of 2 — without hashCode(), the set would treat them as different entries even though equals() says they match.

Performance Considerations

A poor hashCode() implementation degrades HashMap from O(1) to O(n). The examples below show the spectrum from worst to best.

HASH CODE QUALITY — IMPACT ON HASHMAP:

CONSTANT hash (absolute worst):
  @Override public int hashCode() { return 42; }
  Every entry lands in bucket 42 — HashMap becomes a linked list
  get() degrades from O(1) average to O(n) — fatal for large maps

PARTIAL fields (common mistake):
  @Override public int hashCode() { return Objects.hash(city); }
  All products from Mumbai land in the same bucket
  Cities in India concentrate entries — poor distribution

ALL identity fields (correct):
  @Override public int hashCode() { return Objects.hash(productId, sku); }
  Entries spread across all buckets
  get() stays O(1) average — maximum performance

JAVA 8 TREEIFICATION (safety net):
  When any bucket chain grows beyond 8 entries, HashMap converts
  the chain to a Red-Black tree — O(log n) instead of O(n)
  This catches poor hashCode() implementations but does not fix them

Memory: Each HashMap node holds hash, key, value, and next pointer — approximately 48 bytes. A poor hash function that concentrates all entries in one chain wastes this structure without gaining any lookup benefit.

Best Practices

Always override both methods together, never just one. An IDE like IntelliJ or Eclipse generates both with one action — Alt+Insert → Generate → equals() and hashCode(). Overriding only equals() breaks HashSet and HashMap silently. Overriding only hashCode() breaks equals()-based comparisons in List.contains() and Collections.frequency().

Include exactly the fields that define identity in both methods. If two Order objects with the same orderId are the same order, use only orderId in both equals() and hashCode(). Do not include mutable status fields — if the status changes after an Order is put in a HashSet, the hash code changes and the entry becomes unreachable.

Use Objects.hash() for hashCode() and Objects.equals() for field comparison in equals(). Both handle null fields correctly. Writing this.field.hashCode() throws NullPointerException if the field is null. Objects.hash(field) does not.

For value classes that will never change, use Java records. Records generate correct equals(), hashCode(), and toString() from all components automatically. They enforce immutability, which removes the mutable-key bug entirely. Any class that is logically a bundle of final values is a candidate for a record.

Common Mistakes

Mistake 1 — Overriding equals() Without hashCode()

// WRONG — equals() overridden, hashCode() left as Object's identity hash class Employee { String employeeId; @Override public boolean equals(Object obj) { if (!(obj instanceof Employee other)) return false; return Objects.equals(this.employeeId, other.employeeId); } // hashCode() NOT overridden — defaults to identity hash } // Consequence: two equal employees have different hash codes Employee e1 = new Employee("E-001"); Employee e2 = new Employee("E-001"); e1.equals(e2); // true — overridden equals says equal e1.hashCode() == e2.hashCode(); // false — different addresses! Set<Employee> team = new HashSet<>(); team.add(e1); team.contains(e2); // false — e2 goes to different bucket than e1 // The set silently stores both "E-001" employees as separate entries

Mistake 2 — Using a Mutable Field in hashCode()

// WRONG — status is mutable; changing it after insertion loses the entry class Task { String taskId; String status; // mutable — changes from OPEN to DONE @Override public boolean equals(Object obj) { ... } @Override public int hashCode() { return Objects.hash(taskId, status); // status changes after put()! } } Set<Task> openTasks = new HashSet<>(); Task task = new Task("T-001", "OPEN"); openTasks.add(task); task.status = "DONE"; // hash code changes! openTasks.contains(task); // false — task is lost in wrong bucket openTasks.remove(task); // false — cannot remove either // CORRECT — hashCode uses only the immutable identity field @Override public int hashCode() { return Objects.hash(taskId); // taskId never changes }

Mistake 3 — Breaking Symmetry in equals()

// WRONG — equals is not symmetric: employee.equals(manager) != manager.equals(employee) class Employee { String id; @Override public boolean equals(Object obj) { if (!(obj instanceof Employee)) return false; // accepts Manager as well return Objects.equals(this.id, ((Employee) obj).id); } } class Manager extends Employee { @Override public boolean equals(Object obj) { if (!(obj instanceof Manager)) return false; // only accepts Manager return Objects.equals(this.id, ((Manager) obj).id); } } Employee emp = new Employee(); emp.id = "E-001"; Manager mgr = new Manager(); mgr.id = "E-001"; emp.equals(mgr); // true — Employee.equals accepts Manager mgr.equals(emp); // false — Manager.equals rejects Employee // Symmetry broken — this causes unpredictable List.contains() results

Mistake 4 — Comparing Floating-Point With ==

// WRONG — floating-point equality with == misses precision edge cases @Override public boolean equals(Object obj) { if (!(obj instanceof PricePoint other)) return false; return this.amount == other.amount; // floating-point == unreliable } // CORRECT — use Double.compare() for doubles and BigDecimal.compareTo() for currency @Override public boolean equals(Object obj) { if (!(obj instanceof PricePoint other)) return false; return Double.compare(this.amount, other.amount) == 0; }

Interview Questions

Q1. What is the contract between equals() and hashCode() in Java?

The contract has two key rules. First, if a.equals(b) returns true, then a.hashCode() must equal b.hashCode() — no exceptions. Second, the reverse is not required: equal hash codes do not imply equal objects — this is a hash collision and is handled by the chain or tree structure inside HashMap. Violating Rule 1 is the silent killer: HashMap stores an entry using key A's bucket (based on A's hash), but retrieval for an equal key B goes to a different bucket (B's different hash) and returns null. No exception fires — the data just vanishes.

Q2. What happens if you override equals() but not hashCode()?

HashSet silently stores duplicates and HashMap loses entries. When you put(key, value), the key's hash (from Object.hashCode()) determines the bucket. When you later get(equalKey), equalKey.hashCode() returns a different number (identity-based), so it looks in a different bucket, finds nothing, and returns null. list.contains() still works because ArrayList uses equals() for a linear scan — but every hash-based collection breaks. This is why the Java specification says both must always be overridden together.

Q3. Why should Map keys be immutable?

HashMap places an entry in a bucket based on the key's hashCode() at the time of put(). If the key is a mutable object and a field used in hashCode() changes after insertion, the key's hash code changes — but the entry stays in the original bucket. Any subsequent get() with the same (now-modified) key computes a new bucket, finds nothing there, and returns null. The entry is now unreachable — effectively lost. Using String, Integer, or a record as a map key avoids this entirely because those types are immutable.

Q4. What does Objects.hash() do internally?

Objects.hash(field1, field2, ...) calls Arrays.hashCode(new Object[]{field1, field2, ...}) internally. It iterates the array, starting with result = 1, and for each element computes result = 31 * result + (element == null ? 0 : element.hashCode()). The prime 31 is chosen because it produces good distribution for typical string-like values and is fast to compute as a bit-shift operation (n << 5) - n. Null fields are mapped to 0, making the method null-safe — calling field.hashCode() directly on a null field would throw NullPointerException.

Q5. How do Java records handle equals() and hashCode()?

Records automatically generate equals(), hashCode(), and toString() from all declared components (the fields in the record header). The generated equals() checks that both objects are of the same record type and all component fields are equal. The generated hashCode() uses all component fields. Records are immutable by default — all components are private final — which eliminates the mutable-key problem entirely. For value-class use cases in modern Java, records are the cleanest and least error-prone implementation choice.

Q6. When should you NOT include a field in equals() and hashCode()?

Exclude fields that are: (1) mutable and change after object creation — changing them after the object is placed in a HashSet or used as a HashMap key corrupts the collection; (2) derived or computed from other included fields — redundant inclusion does not add correctness but increases collision risk if the computation creates similar values; (3) not part of the object's identity — for example, a createdAt timestamp on a Product is metadata, not identity. Only include fields that you would compare to decide "are these the same thing in the domain model?"

FAQs

Do I need to override equals() and hashCode() for every Java class?

No — only for classes whose instances will be: used as HashMap or LinkedHashMap keys, stored in a HashSet or LinkedHashSet, compared with assertEquals() in unit tests by value, or passed to List.contains() or Collections.frequency() where content equality matters. For service classes, controller classes, or any class used by reference only, the default identity-based implementations are correct and safe.

What is the difference between == and equals() in Java?

== is the reference equality operator — it returns true only when both sides point to the exact same object in memory. equals() is a method that can be overridden to define logical equality. For String, Integer, and Long, equals() compares content. For classes that do not override equals(), it falls back to == behaviour. This is why new String("hello") == new String("hello") is false but .equals() between them is true.

Can two objects be equal but have different class types?

Technically yes — you can write an equals() that returns true for objects of different classes. But this almost always breaks the symmetry contract. If a.equals(b) returns true when b is of a parent or sibling class but b.equals(a) returns false because b's equals() uses instanceof more narrowly, symmetry is violated. The safest approach for inheritance hierarchies is to use getClass() != obj.getClass() instead of instanceof — this rejects cross-type equality entirely and keeps the contract clean.

What is the hashCode() value for a null key in HashMap?

HashMap specifically handles null keys by placing them in bucket 0 (index 0), bypassing the normal hashCode() call. This is why HashMap allows exactly one null key while ConcurrentHashMap and Hashtable do not — those implementations call hashCode() on the key, which throws NullPointerException for null.

Does String already have a good equals() and hashCode()?

Yes. String.equals() compares character content. String.hashCode() uses a deterministic formula: s[0]*31^(n-1) + s[1]*31^(n-2) + ... + s[n-1], where s[i] is the character at position i. Crucially, String caches the hash code after the first call — subsequent hashCode() calls on the same String return the cached value in O(1), not O(n). This makes String an excellent HashMap key for performance.

What happens if hashCode() returns 0 for all objects?

The map still works correctly — all entries land in bucket 0, which becomes a single enormous chain (or tree after 8 entries). get() degrades from O(1) to O(n) for every lookup because the bucket chain must be traversed fully. A HashMap with 10,000 entries and a constant hashCode() performs like a linked list on every read. No data is lost — correctness is maintained — but performance collapses entirely.

Summary

equals() and hashCode() form a two-part contract that powers every hash-based collection in Java. The critical rule: equal objects must have equal hash codes — always, without exception. Violating this makes HashMap silently lose entries and HashSet silently accept duplicates, with no exception to alert you.

Override both methods together using the same fields. Use Objects.hash() for hashCode() and Objects.equals() for field comparison in equals() — both handle null safely. Only include fields that define the object's identity, and never include mutable fields that change after the object enters a collection.

For interviews: know the contract by heart, know what breaks when hashCode() is overridden without equals() and vice versa, explain why map keys must be immutable, and describe how HashMap uses both methods in its two-step lookup. These questions appear consistently from service-based campus placements to senior product-company rounds, and getting them right shows that you understand how Java collections actually work internally.

What to Read Next

Practice Programs

Put this topic into practice — each program has full code, output and a step-by-step explanation you can run online.

Browse all Java programs →