Wednesday, April 9, 2025

Interview Preparation Questions & Answers

Question:  can you explain for Composite in hibernate?

Certainly! In Hibernate, a Composite Key (also known as a composite primary key) is a key that is made up of two or more fields to uniquely identify an entity. Instead of using a single attribute as the primary key, Hibernate can use a combination of fields as the identifier for an entity.

This is useful when:

  1. You don't have a single field that can serve as the unique identifier for the entity.

  2. You need a combination of fields to represent the uniqueness of the entity.

Types of Composite Keys in Hibernate

There are two main approaches for using composite keys in Hibernate:

  1. Using @Embeddable and @EmbeddedId annotations

  2. Using @IdClass annotation

1. Composite Key Using @Embeddable and @EmbeddedId

Steps:

  • Create an embeddable class that represents the composite key.

  • Use the @EmbeddedId annotation on the entity to link the composite key.

Example:

Let's say we have an entity OrderDetail that has a composite key made up of orderId and productId.

Step 1: Create the Composite Key Class (@Embeddable)

import javax.persistence.Embeddable;

import java.io.Serializable;

@Embeddable
public class OrderDetailId implements Serializable {

    private int orderId;

    private int productId;

    // Getters, Setters, hashCode, and equals methods

    public OrderDetailId() {}

    public OrderDetailId(int orderId, int productId) {

        this.orderId = orderId;

        this.productId = productId;

    }

    public int getOrderId() {

        return orderId;

    }

    public void setOrderId(int orderId) {

        this.orderId = orderId;

    }

    public int getProductId() {

        return productId;

    }

    public void setProductId(int productId) {

        this.productId = productId;

    }

    @Override

    public boolean equals(Object o) {

        if (this == o) return true;

        if (o == null || getClass() != o.getClass()) return false;

        OrderDetailId that = (OrderDetailId) o;

        return orderId == that.orderId && productId == that.productId;

    }

    @Override

    public int hashCode() {

        return 31 * orderId + productId;

    }

}

import javax.persistence.EmbeddedId;
import javax.persistence.Entity;

@Entity
public class OrderDetail {

    @EmbeddedId
    private OrderDetailId id;

    private int quantity;
    private double price;

    // Getters, Setters, and other methods

    public OrderDetail() {}

    public OrderDetail(OrderDetailId id, int quantity, double price) {
        this.id = id;
        this.quantity = quantity;
        this.price = price;
    }

    public OrderDetailId getId() {
        return id;
    }

    public void setId(OrderDetailId id) {
        this.id = id;
    }

    public int getQuantity() {
        return quantity;
    }

    public void setQuantity(int quantity) {
        this.quantity = quantity;
    }

    public double getPrice() {
        return price;
    }

    public void setPrice(double price) {
        this.price = price;
    }
}

Explanation:

  • OrderDetailId is a class that is embeddable (@Embeddable), and it contains two fields: orderId and productId. It also overrides equals() and hashCode() to ensure proper comparison of composite keys.

  • The OrderDetail class uses the @EmbeddedId annotation to embed the composite key. The OrderDetailId object will be the primary key of OrderDetail.

Advantages:

  • Clear separation of composite key from entity.

  • Easy to manage complex composite keys.

Question : Write a program to design a string which first , last , second ,second, last character and so on .. in java ?

public class ZigZagString {

    public static void main(String[] args) {
        String input = "abcdefghi"; // Example input
        String result = rearrangeZigZag(input);

        System.out.println("Original: " + input);
        System.out.println("ZigZag Pattern: " + result);
    }

    public static String rearrangeZigZag(String input) {
        StringBuilder result = new StringBuilder();
        int start = 0;
        int end = input.length() - 1;

        while (start <= end) {
            if (start == end) {
                result.append(input.charAt(start)); // middle character for odd length
            } else {
                result.append(input.charAt(start));
                result.append(input.charAt(end));
            }
            start++;
            end--;
        }

        return result.toString();
    }
}

Input: abcdefghi

OutPut : aibchdgfe


Explanation: a (first), i (last), b (second), h (second last), c (third), g (third last), .

Question :  Create a class , Create a string , remove the duplicates and print remaining words in java?

Sure! Here's a simple Java program that:

  1. Accepts a string

  2. Splits it into words

  3. Removes duplicate words

  4. Prints only the unique words

import java.util.LinkedHashSet;
import java.util.Set;

public class RemoveDuplicateWords {

    public static void main(String[] args) {
        String input = "hello world hello java spring spring boot world";

        // Call method to remove duplicates
        String result = removeDuplicateWords(input);

        // Print result
        System.out.println("Original: " + input);
        System.out.println("Without duplicates: " + result);
    }

    public static String removeDuplicateWords(String input) {
        String[] words = input.split("\\s+"); // Split on spaces
        Set<String> uniqueWords = new LinkedHashSet<>(); // Maintains insertion order

        for (String word : words) {
            uniqueWords.add(word); // Duplicates will automatically be ignored
        }

        return String.join(" ", uniqueWords);
    }
}

Output:

Original: hello world hello java spring spring boot world Without duplicates: hello world java spring boot

Question: Rest Endpoints and how they handled transactions in Restful

REST Endpoints in Spring Boot

In a Spring Boot RESTful application, endpoints are defined using annotations in @RestController classes.

Here’s a simple breakdown of common HTTP methods:

HTTP MethodPurposeSpring Annotation
GETRead data@GetMapping
POSTCreate new resource@PostMapping
PUTUpdate entire resource@PutMapping
PATCHUpdate partial resource@PatchMapping
DELETEDelete a resource@DeleteMapping

✅ Example of REST Endpoints


@RestController @RequestMapping("/api/employees") public class EmployeeController { @Autowired private EmployeeService employeeService; @GetMapping("/{id}") public Employee getEmployee(@PathVariable Long id) { return employeeService.getEmployeeById(id); } @PostMapping public Employee createEmployee(@RequestBody Employee employee) { return employeeService.saveEmployee(employee); } @PutMapping("/{id}") public Employee updateEmployee(@PathVariable Long id, @RequestBody Employee updated) { return employeeService.updateEmployee(id, updated); } @DeleteMapping("/{id}") public void deleteEmployee(@PathVariable Long id) { employeeService.deleteEmployee(id); } }

๐Ÿ”„ Handling Transactions in RESTful APIs

✅ 1. Why Use Transactions?

Transactions ensure data consistency and integrity. If one part of a multi-step operation fails, the entire operation rolls back.


✅ 2. Spring’s @Transactional

In Spring, we use @Transactional from org.springframework.transaction.annotation.Transactional.

@Service public class EmployeeService { @Autowired private EmployeeRepository employeeRepository; @Transactional public Employee saveEmployee(Employee employee) { // multiple database operations employeeRepository.save(employee); // you can do more operations here // if any exception occurs, changes will be rolled back return employee; } }

๐Ÿ”ฅ Key Features of @Transactional

FeatureBehavior
AtomicityAll operations in a method succeed or fail together
Rollback on ExceptionBy default, rolls back on runtime exceptions
Propagation SupportHandles nested transactions (REQUIRED, REQUIRES_NEW, etc.)
Read-only OptimizationCan mark queries as @Transactional(readOnly = true) for performance

⚠️ When to Use @Transactional

  • Any service method doing multiple DB operations.

  • In create/update/delete APIs.

  • When calling multiple repositories or modifying multiple entities.


❗ Avoid Using @Transactional in Controller Directly

Always use it in the Service Layer, not in the Controller, to follow clean architecture and separation of concerns.


๐Ÿงช Real-world Example

@Transactional public Order createOrder(OrderRequest request) { Order order = orderRepository.save(new Order(...)); inventoryService.reserveItems(request.getItems()); // Could throw an exception paymentService.chargeCustomer(request.getPaymentInfo()); return order; }

If paymentService.chargeCustomer() fails, the order and inventory operations will be rolled back.


Question: what is object class? In Java, the Object class is the root class of the class hierarchy. Every class in Java directly or indirectly inherits from Object, which means all Java objects are instances of this class. ๐Ÿง  Why is it important? Because it's the base class, it provides a common set of methods that every Java object has. ๐Ÿ”‘ Key Methods of Object Class: Method                                                 Description: toString() Returns a string representation of the object (you can override this for custom output). equals(Object obj) Checks if this object is "equal to" another object. hashCode() Returns a hash code value for the object (used in hashing collections like HashMap). getClass() Returns the runtime class of the object. clone() Creates and returns a copy of this object (requires implementing Cloneable). finalize() Called by the garbage collector before object is removed from memory (not commonly used). wait(), notify(), notifyAll() Used for thread communication (in multithreading scenarios). ๐Ÿงช Example: public class MyClass { public static void main(String[] args) { MyClass obj = new MyClass(); System.out.println(obj.toString()); // Output: className@hashcode System.out.println(obj.getClass()); // Output: class MyClass } } ✅ Summary: Object class is at the top of Java class hierarchy. It provides basic methods that all objects inherit. You can override methods like toString() and equals() to make them work better for your class. Question : Wanna see an example of how overriding equals() or toString() works? Absolutely! Overriding equals() and toString() methods is super important in Java for object comparison and representing objects as strings. Let's break it down with some examples. ✅ 1. Overriding equals() Method The equals() method is used to compare two objects for equality. By default, equals() compares memory references (i.e., object identity), but we often need to compare the content of objects (e.g., if two Person objects have the same name and age). Steps to override equals(): Check if the objects are the same (using ==). Check for null. Check the type (use instanceof). Compare fields that define equality. ๐Ÿงช Example: Overriding equals() for Person class public class Person { private String name; private int age; public Person(String name, int age) { this.name = name; this.age = age; } @Override public boolean equals(Object obj) { if (this == obj) return true; // Same reference if (obj == null || getClass() != obj.getClass()) return false; // Different class or null Person person = (Person) obj; // Cast to Person return age == person.age && name.equals(person.name); // Compare fields } @Override public String toString() { return "Person{name='" + name + "', age=" + age + "}"; } } ๐Ÿงช Example Usage: public class Main { public static void main(String[] args) { Person p1 = new Person("Alice", 30); Person p2 = new Person("Alice", 30); Person p3 = new Person("Bob", 25); System.out.println(p1.equals(p2)); // true (same content) System.out.println(p1.equals(p3)); // false (different content) System.out.println(p1); // Person{name='Alice', age=30} } } ๐Ÿงฉ Output: true false Person{name='Alice', age=30} Why is this important? Hash-based collections like HashMap and HashSet rely on equals() to determine if two objects are the same. Customization: You can define what makes objects equal (e.g., comparing just the name and age in Person). ✅ 2. Overriding toString() Method The toString() method is used to provide a string representation of an object. This is useful for debugging, logging, and printing objects in a human-readable format. By default, the toString() method returns the class name followed by the hashcode. Overriding it helps us to provide custom string representation of the object. ๐Ÿงช Example: Overriding toString() for Person class @Override public String toString() { return "Person{name='" + name + "', age=" + age + "}"; } This overrides the default toString() to return something more useful, like: Person{name='Alice', age=30} ๐Ÿงช Example Usage: public class Main { public static void main(String[] args) { Person p1 = new Person("Alice", 30); System.out.println(p1); // Person{name='Alice', age=30} } } ๐Ÿงฉ Output: Person{name='Alice', age=30} Why is this important? Makes debugging easier when printing objects. Helps with logging and displaying objects in a human-readable way. ๐Ÿ” Key Points: equals() compares the content of the objects for equality, not the references. toString() returns a string representation of the object for easy readability. ๐Ÿงฉ Best Practice: Always override both equals() and toString() together when dealing with custom objects that need to be compared or logged. Question : Difference between wait and sleep ? In Java, both wait() and sleep() are used to pause the execution of a thread, but they are very different in purpose, usage, and behavior. ๐Ÿ†š wait() vs sleep() – Key Differences Feature             wait()                                                         sleep() Belongs to java.lang.Object                             java.lang.Thread Needs synchronized block? ✅ Yes                             ❌ No Releases lock? ✅ Yes                                             ❌ No Purpose Used for inter-thread communication (wait until condition is met) Used to pause execution for a fixed time Can be interrupted? ✅ Yes                                     ✅ Yes Resume Can be resumed by notify() or notifyAll() Resumes automatically after timeout Throws InterruptedException                                 InterruptedException

๐Ÿ”ง Example of sleep():

Thread.sleep(1000); // Pauses current thread for 1 second

๐Ÿ”ง Example of wait(): synchronized(obj) { obj.wait(); // Pauses and releases the lock until notified }

To resume : synchronized(obj) { obj.notify(); // Wakes up one waiting thread } ✅ In Short:

Use sleep() to pause a thread for a specific time. Use wait() when you want one thread to pause and wait for a signal (usually from another thread). Question: class level lock vs Object level Lock? The difference between Class-level Lock and Object-level Lock in Java — both are crucial for handling thread synchronization in multi threading. ๐Ÿ”’ Object-level Lock Lock is on a specific instance (object) of a class. Used when synchronizing instance methods or blocks. Only one thread can access the synchronized code for that specific object. ๐Ÿงช Example: public class Printer { public synchronized void printDocs(String doc) { System.out.println(Thread.currentThread().getName() + " printing: " + doc); } } Multiple threads can call printDocs() on different objects simultaneously. The lock is per object. ๐Ÿ” Class-level Lock Lock is on the Class object (i.e., Printer.class). Used when synchronizing static methods or blocks. Only one thread can access any synchronized static method across all instances of that class. ๐Ÿงช Example: public class Printer { public static synchronized void printDocs(String doc) { System.out.println(Thread.currentThread().getName() + " printing: " + doc); } } Now, even if threads are using different objects, they still need to wait if another thread is already executing this method. ๐Ÿ” You can also explicitly lock on a class or object:

Object-level block: synchronized(this) { // object-level lock }

Class-level block: synchronized (Printer.class) { // class-level lock } ๐Ÿ” Summary: Feature         Object-level Lock                             Class-level Lock Lock Scope Instance of class                                 Class (shared across all instances) Used With Instance methods / synchronized(this) Static methods / synchronized(ClassName.class) Affects         One object at a time                          All instances of the class Use Case Protect instance-level data                  Protect static/shared class-level data Question: implement the Factory Design pattern in java? implement the Factory Design Pattern in Java with a simple and clear example. The Factory Pattern is a creational pattern used to create objects without exposing the instantiation logic to the client. ๐Ÿงช Example: Shape Factory Let’s say we want to create different types of Shape objects like Circle, Rectangle, and Square. ✅ Step-by-step Implementation:

1. Shape Interface public interface Shape { void draw(); }

2. Concrete Classes public class Circle implements Shape { @Override public void draw() { System.out.println("Drawing a Circle"); } } public class Rectangle implements Shape { @Override public void draw() { System.out.println("Drawing a Rectangle"); } } public class Square implements Shape { @Override public void draw() { System.out.println("Drawing a Square"); } }

3. Factory Class public class ShapeFactory { // Factory method public Shape getShape(String shapeType) { if (shapeType == null) return null; switch (shapeType.toLowerCase()) { case "circle": return new Circle(); case "rectangle": return new Rectangle(); case "square": return new Square(); default: return null; } } } 4. Client Code (Main class) public class FactoryPatternDemo { public static void main(String[] args) { ShapeFactory shapeFactory = new ShapeFactory(); Shape shape1 = shapeFactory.getShape("circle"); shape1.draw(); Shape shape2 = shapeFactory.getShape("rectangle"); shape2.draw(); Shape shape3 = shapeFactory.getShape("square"); shape3.draw(); } } ๐Ÿ” Output: Drawing a Circle Drawing a Rectangle Drawing a Square

Benefits of Factory Pattern: Hides object creation logic from the client. Promotes loose coupling. Easier to add new shapes without modifying client code. Question: what are the advantages of using factory pattern? The Factory Design Pattern comes with several advantages, especially in large-scale, scalable, and flexible systems. Here’s a breakdown of its key benefits: ✅ Advantages of Factory Pattern

1. Encapsulation of Object Creation The creation logic is centralized in one place (the factory), so the client code doesn't need to know about the instantiation details of objects. This means the client code is cleaner and more maintainable.

2. Promotes Loose Coupling The client code depends on abstractions (interfaces), not on concrete classes. This makes it easy to change or extend the system without affecting existing code.

3. Easy to Extend You can easily add new product classes (e.g., new Shape types) without modifying existing code—just update the factory logic. This follows the Open/Closed Principle (OCP) from SOLID principles.

4. Improved Maintainability All instantiation logic is in one place—easier to manage, debug, and change. If object creation becomes complex (e.g., with configuration, validations, etc.), the factory handles it without cluttering client code.

5. Control over Object Creation You can control how and when objects are created (e.g., lazy loading, caching). Can return existing instances (singleton/shared) if needed. 6. Useful with Polymorphism Factory returns different subtypes based on input, allowing runtime decision making on which object to use. Very useful in scenarios like plugins, UI components, or command processors. ๐Ÿง  Real-World Analogy: Imagine a Pizza Store. You order a pizza, and they decide whether it’s thin crust or deep dish based on your order. You don’t care how they make it—you just get what you asked for. ๐Ÿ• ๐Ÿงฉ Summary: Benefit                                                                             Why it Matters Clean code                                             Client doesn’t worry about new keywords or object logic Flexibility                                                     Easily add/remove/change object types Scalability                                             Works well for large, modular applications Reusability                                             Centralized object creation improves code reuse Question: Threadsafe Singleton implementation? Let's implement a thread-safe Singleton in Java. The Singleton pattern ensures that only one instance of a class is created and provides a global access point to it. ✅ Best Practice: Thread-safe Singleton with Lazy Initialization ๐Ÿ’ก Recommended: Bill Pugh Singleton (Lazy + Thread-Safe + Fast)

public class Singleton { // Private constructor to prevent instantiation private Singleton() {} // Static nested helper class private static class SingletonHelper { private static final Singleton INSTANCE = new Singleton(); } // Global access point public static Singleton getInstance() { return SingletonHelper.INSTANCE; } } ๐Ÿ”’ Why this works? The inner static class is not loaded until getInstance() is called. It's lazy-loaded and thread-safe without synchronization. Efficient and elegant – preferred in most real-world projects. ๐Ÿ›ก️ Alternative 1: Synchronized getInstance() (Simpler but less performance under high concurrency) public class Singleton { private static Singleton instance; private Singleton() {} public static synchronized Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } ๐Ÿ”ป Downside: The whole method is synchronized, which can slow down access after initialization. ⚡ Alternative 2: Double-Checked Locking (For lazy initialization + better performance) public class Singleton { private static volatile Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { synchronized (Singleton.class) { if (instance == null) { instance = new Singleton(); } } } return instance; } } volatile ensures that multiple threads handle the instance variable correctly. ๐Ÿงฉ Summary Method Thread Safe Lazy Performance Recommended Synchronized Method ✅ ✅ ❌ ❌ (only for simplicity) Double-Checked Locking ✅ ✅ ✅ ๐Ÿ‘ Bill Pugh ✅ ✅ ✅ ✅✅✅ Question : Create an immutable class for this structure on below: class Employee{ String name; List <Project> projects ; } Making classes immutable helps ensure thread-safety and makes debugging easier because their state can't change after creation. Let’s convert your Employee class into a fully immutable class in Java. ๐Ÿ”’ Requirements for Immutability: Make the class final so it can’t be subclassed. Make all fields private and final. Don't provide any setters. Deep copy mutable objects (like List<Project>) in constructor and getters. Ensure Project is also immutable (or at least treated immutably). ✅ Immutable Employee Class: import java.util.Collections; import java.util.List; import java.util.ArrayList; public final class Employee { private final String name; private final List<Project> projects; public Employee(String name, List<Project> projects) { this.name = name; // Defensive copy to ensure immutability this.projects = projects == null ? List.of() : new ArrayList<>(projects); } public String getName() { return name; } public List<Project> getProjects() { // Return unmodifiable list to prevent external modification return Collections.unmodifiableList(projects); } } ☑️ Optional: Make Project Immutable Too If Project is also a mutable class, you'd want to make deep copies in both the constructor and getter. Here’s a simple immutable Project class: public final class Project { private final String title; public Project(String title) { this.title = title; } public String getTitle() { return title; } } ๐Ÿ’ก Usage Example: List<Project> list = new ArrayList<>(); list.add(new Project("AI Development")); Employee emp = new Employee("Alice", list); // list.clear(); <-- won't affect emp.projects due to defensive copy System.out.println(emp.getName()); // Alice System.out.println(emp.getProjects().size()); // 1 Let me know if you want the Employee class to support methods like .withName() or .withProjectAdded() in a fluent immutable style (like record builders)! Data Structures: Question : what is the time complexity of put() and get() in a Hashmap ? The time complexity of put() and get() in a HashMap in Java depends on how well the keys are distributed and whether there are hash collisions. ๐Ÿ•’ Time Complexity of put() and get() in HashMap: Operation Best / Average Case Worst Case put()                             O(1) O(n) get()                            O(1) O(n)

✅ Best/Average Case: O(1) When keys are evenly distributed across buckets (good hash function). Internally uses a hash function to compute the index → access is fast. The bucket contains only one item or a short list. ❌ Worst Case: O(n) Happens when many keys hash to the same bucket (bad hash function or hash collision). In such cases, the bucket becomes a linked list (or balanced tree in Java 8+). If all keys go into one bucket → traversal becomes O(n). ๐Ÿš€ Java 8+ Optimization If a single bucket exceeds a threshold (typically 8 entries) and the map size is large, Java converts the bucket’s linked list into a balanced tree (Red-Black Tree). So worst-case drops to O(log n) instead of O(n) in many cases. ๐Ÿ“Œ Summary: Case Complexity Average O(1) Worst (Java 7 and earlier) O(n) Worst (Java 8+) O(log n) Let me know if you want to explore how HashMap internally resizes or how hash collisions are resolved! Question : Explore how HashMap internally resizes or how hash collisions are resolved? Let’s dive into how HashMap in Java works internally, especially focusing on resizing and handling hash collisions. This is one of those core topics that’s simple on the surface but fascinating under the hood. Let’s break it down. ๐Ÿงฑ 1. Internal Structure of HashMap A HashMap is basically an array of buckets. Each bucket is either: null (empty), a linked list (Java 7 or before), or a tree (Red-Black Tree) (Java 8+ when too many collisions happen). Node<K,V>[] table; // internal array of buckets Each Node stores: int hash; K key; V value; Node<K,V> next; ⚡ 2. How put() Works Internally Steps: Compute hash: hash = hash(key) Calculate index: index = hash % capacity. Check the bucket at that index: If null, create a new Node. If not null, check for a key collision. If key exists → update value. If not → append new node to the list (or insert into tree if already converted). ๐Ÿ”„ 3. Hash Collision Resolution What’s a collision? When two different keys generate the same hash index. How Java handles it: Version Collision Handling Java 7 and before Linked List chaining Java 8+ Linked List → Red-Black Tree if entries in a bucket > 8 and total size > 64 Example: map.put("ABC", 1); // goes to index 5 map.put("DEF", 2); // hash also gives index 5 → collision So, both keys sit in a linked list (or tree) at the same bucket. ๐Ÿ“ 4. When and How HashMap Resizes Resizing happens when: currentSize >= capacity * loadFactor Default loadFactor = 0.75 Default initial capacity = 16 So resize happens when size reaches 16 * 0.75 = 12 What happens during resize: Capacity is doubled (16 → 32 → 64...) A new array is created All entries are rehashed and reinserted into the new array ๐Ÿ“Œ This is expensive, so avoid frequent resizing if possible (set initial capacity if you can estimate size). ๐Ÿง  Bonus: Hash Function in Java Java enhances the hash using: (hash >>> 16) ^ hash; This helps distribute keys more evenly in buckets. ๐Ÿงฉ Summary Topic Explanation Collision handling Linked List (Java 7), Tree (Java 8+) Tree conversion threshold >8 entries in a bucket Resizing trigger size ≥ capacity × loadFactor Resizing effect New array, rehashing all entries Hash function tweak Spreads bits to reduce collision Want to see a step-by-step custom HashMap implementation to really understand the mechanics? Awesome! Let's build a step-by-step custom HashMap from scratch in Java — simple and clean, so you can clearly see how hashing, buckets, collisions, and resizing work under the hood. We'll call it: MyHashMap<K, V> ๐Ÿ›  Step 1: Define the Basic Structure public class MyHashMap<K, V> { private static class Entry<K, V> { K key; V value; Entry<K, V> next; // for handling collisions via chaining Entry(K key, V value) { this.key = key; this.value = value; } } private Entry<K, V>[] buckets; private int capacity = 16; // default capacity private int size = 0; private float loadFactor = 0.75f; public MyHashMap() { buckets = new Entry[capacity]; } } ⚡ Step 2: Implement put() Method public void put(K key, V value) { int index = getBucketIndex(key); Entry<K, V> head = buckets[index]; // Check if key already exists → update value while (head != null) { if (head.key.equals(key)) { head.value = value; return; } head = head.next; } // Insert new entry at the head of the list Entry<K, V> newNode = new Entry<>(key, value); newNode.next = buckets[index]; buckets[index] = newNode; size++; // Resize if needed if ((1.0 * size) / capacity >= loadFactor) { resize(); } } ๐Ÿ” Step 3: Implement get() Method public V get(K key) { int index = getBucketIndex(key); Entry<K, V> head = buckets[index]; while (head != null) { if (head.key.equals(key)) { return head.value; } head = head.next; } return null; // not found } ๐Ÿงฎ Step 4: Helper Methods Get the index from hash private int getBucketIndex(K key) { int hash = key.hashCode(); return Math.abs(hash) % capacity; } ๐Ÿ”„ Step 5: Resizing Logic private void resize() { Entry<K, V>[] oldBuckets = buckets; capacity *= 2; buckets = new Entry[capacity]; size = 0; for (Entry<K, V> headNode : oldBuckets) { while (headNode != null) { put(headNode.key, headNode.value); // rehash and put again headNode = headNode.next; } } } ๐Ÿงช Step 6: Test the Custom HashMap public static void main(String[] args) { MyHashMap<String, Integer> map = new MyHashMap<>(); map.put("Apple", 10); map.put("Banana", 20); map.put("Orange", 30); map.put("Apple", 99); // update value System.out.println(map.get("Apple")); // 99 System.out.println(map.get("Banana")); // 20 System.out.println(map.get("Grapes")); // null } ๐Ÿงฉ Summary of Features: ✅ Uses chaining for collision resolution. ✅ Automatically resizes when the load factor threshold is crossed. ✅ Demonstrates hashing, indexing, and rehashing mechanics.





No comments:

Post a Comment

Java 9 and Java11 and Java17, Java 21 Features

 Java 9 and Java11 and Java17 features along with explanation and examples in realtime scenarios Here's a detailed breakdown of Java 9, ...