Posts

Showing posts with the label Java

Command Design Pattern in Java

The Command design pattern is a behavioral design pattern that encapsulates a request or an action as a separate object, allowing for more flexibility and extensibility in handling requests. What is the Command Design Pattern? The Command pattern is used to decouple the requester of an action from the provider of the action. It does this by introducing an intermediate object, known as the Command, which encapsulates the request. Components of the Command Pattern Command : This is the interface that declares the execute method. ConcreteCommand : This class implements the Command interface and defines the actions to be performed. Receiver : This is the object that actually performs the action. Invoker : This is the object that receives the command and invokes the execute method. Client : This is the object that creates the command and sets the receiver. Java Implementation Here's an example implementation of the Command pattern in Java: Java // Command interface public interface C...

Fork/Join Framework in Java — Parallelism with Work-Stealing

Image
The Fork/Join Framework (java.util.concurrent) is designed for parallelizing CPU-bound, divide-and-conquer tasks. It splits a big task into smaller subtasks (fork), runs them in parallel, and merges results (join). It's ideal for recursive algorithms (e.g., parallel sum, merge sort, matrix ops) and uses a work-stealing scheduler to balance load across worker threads. 1. When to use Fork/Join Large CPU-bound recursive tasks that can be divided into independent subtasks. Problems that follow divide-and-conquer pattern (array sum, parallel sort, tree algorithms). Not ideal for many short I/O-bound tasks — use ExecutorService for that. 2. Core Concepts ForkJoinPool — pool of worker threads optimized for Fork/Join tasks. ForkJoinTask — abstract base for tasks; two common subclasses: RecursiveTask<V> — returns a result. RecursiveAction — returns no result (void). Work-stealing — idle threads steal subtasks from busy threads...

Producer–Consumer Problem in Java Using BlockingQueue

Image
The Producer–Consumer problem is one of the most important multi-threading patterns in Java. Traditionally, developers used wait and notify to coordinate threads, but this approach is complex and error-prone. Modern Java provides a cleaner solution: BlockingQueue . 1. What is the Producer–Consumer Problem? Two types of threads share a common buffer: Producer → Generates data and puts it into the buffer Consumer → Retrieves data from the buffer and processes it The challenge: prevent producers from writing when the buffer is full and consumers from reading when the buffer is empty. 2. Problems with wait()/notify() The older approach required: Manual locking Complex condition handling Risk of deadlocks Hard to debug BlockingQueue solves these issues with built-in thread management. 3. Why BlockingQueue? BlockingQueue handles synchronization internally. Its key methods: put() – waits if the queue is full take() – waits if th...

How HashMap get() Method Works Internally in Java

Image
The get() method in HashMap is one of the most commonly used operations. Although it looks like a simple key lookup, internally it involves hashing, bucket indexing, and collision handling. In this article, we’ll break down the internal working of HashMap.get() step by step with examples. 1. Hashing the Key When you call map.get(key) , the HashMap computes a hash value using the key’s hashCode() . In Java 8+, this value is further processed with bitwise operations to spread the hash bits and reduce collisions. int hash = hash(key.hashCode()); 2. Finding the Bucket The hash value is then converted into an index in the internal array (called table ) using: int index = (n - 1) & hash; // n = table.length This determines the bucket where the entry might be stored. 3. Searching Inside the Bucket Each bucket can contain: null → no entry stored A single key-value pair A linked list of nodes (if multiple keys hash to the same bucket) A red-blac...

Serialization, Deserialization and Externalization in Java

Image
In Java interviews, one of the most common topics you’ll encounter is object persistence —how Java objects are saved, transferred, and reconstructed. This is where concepts like Serialization , Deserialization , and Externalization come into play. These aren’t just theoretical ideas; they are crucial in distributed systems, caching, session management, and inter-process communication . Let’s explore these concepts step by step, highlight their differences, and discuss real-world use cases. 1. What is Serialization in Java? Serialization is the process of converting a Java object into a byte stream so that it can be stored in a file, transmitted over a network, or persisted in a database. How it works: The class must implement the Serializable interface (marker interface). Use ObjectOutputStream.writeObject() to serialize an object. Non-transient, non-static fields are serialized; transient fields are skipped. ...

Factory Design Pattern in Java — A Practical Guide

Image
Understand the Factory (creational) pattern with real-world analogies, Java code, diagrams, and interview tips — ready for publishing on DevForgeHub. Who should read this? Java developers preparing for interviews, software engineers learning creational patterns, and full-stack professionals wanting to decouple object creation from usage. Introduction The Factory Design Pattern is a creational pattern that centralizes object creation logic into a factory class. Instead of instantiating objects directly, client code requests objects from the factory. This separates what is created from how it is created, enabling greater flexibility and easier maintenance. Real-world analogy Think of Tata Motors producing multiple car variants: petrol, diesel, and electric. A single factory line (or a factory class) receives a request for a car type and returns the right model. The client (customer) doesn't need to know the assembly details — it just asks...

Java Singleton Design Pattern: Implementation, Pitfalls & Fixes

Image
Master the Singleton design pattern in Java: standard approaches, multithreading concerns, serialization/reflection pitfalls, and fixes explained with code and diagrams. Introduction The Singleton pattern is a creational design pattern that ensures only one instance of a class exists in the JVM, and it provides a global point of access to that instance. It’s widely used for configurations, logging, caching, thread pools, and more. For Java interviews, Singleton is a common topic since it involves discussions on object lifecycle, concurrency, and design robustness . 1. Standard Singleton Implementation class Singleton { private static Singleton instance; private Singleton() {} public static Singleton getInstance() { if (instance == null) { instance = new Singleton(); } return instance; } } This basic form ensures only one instance is created, but it is not thread-safe . Diagram (textual): Cl...

Mastering SOLID Principles in Java: Code Examples, Diagrams & Interview Insights

Image
Write maintainable, scalable, and testable enterprise Java with SRP, OCP, LSP, ISP, and DIP — backed by real-world banking/finance examples, refactorings, and accessible diagrams. Who is this for? Professional Java devs and interview candidates who want production-grade code quality and crisp explanations for system design/OO rounds. Contents Introduction Single Responsibility Principle (SRP) Open/Closed Principle (OCP) Liskov Substitution Principle (LSP) Interface Segregation Principle (ISP) Dependency Inversion Principle (DIP) How SOLID helps in interviews & real projects Practical tips for projects & interviews References & further learning Introduction SOLID is an acronym popularized by Robert C. Martin for five OO design principles. In enterprise Java, these guardrails reduce coupling, protect against regressions, and make code easier to extend an...

Insertion Sort — Theory & Code

Image
Insertion Sort is a simple, intuitive algorithm ideal for small or nearly sorted datasets. In this blog post, we’ll delve deep into the workings of Insertion Sort—covering the core concept, step-by-step visualization, Java implementation, and performance evaluation. 1. What is Insertion Sort? Insertion Sort builds a sorted array one element at a time. You pick the next element from the unsorted portion and insert it into its correct position within the sorted portion, shifting elements as needed. 2. Step-by-Step Illustration Unsorted array: [5] [2] [4] [6] [1] [3] Iteration 1: Sorted: [5] Take next: 2 → insert before 5 Array becomes: [2] [5] [4] [6] [1] [3] Iteration 2: Sorted: [2] [5] Next: 4 → shift 5 to right, insert 4 → [2] [4] [5] [6] [1] [3] Continue until sorted. Diagram (ASCII): Initial: 5 | 2 4 6 1 3 Step 1: 2 5 | 4 6 1 3 Step 2: 2 4 5 | 6 1 3 Step 3: 2 4 5 6 | 1 3 ... Final: 1 2 3 4 5 6 3. Java Implementat...

Selection Sort — Theory & Code

Image
Selection Sort is one of the simplest sorting algorithms and a popular choice for learning how sorting works “under the hood.” This post goes beyond the standard explanation—covering the algorithm’s logic, visual steps, Java code, and complete performance analysis. 1. What is Selection Sort? Selection Sort sorts an array by maintaining two regions: Sorted subarray (initially empty, grows from the left). Unsorted subarray (initially the whole array). In each iteration, it picks the minimum element from the unsorted portion and swaps it with the element at the current boundary, thus growing the sorted part by one.:contentReference[oaicite:4]{index=4} 2. Step-by-Step Illustration Unsorted: [64] [25] [12] [22] [11] Sorted: [] Iteration 1: Find min from unsorted → 11 Swap 11 with 64: Sorted: [11] Unsorted: [25] [12] [22] [64] Iteration 2: Find min → 12 Swap with 25: Sorted: [11][12] Unsorted: [25][22][64] Continue similarly until fully sorted. Diagrammatic ...

Time and Space Complexity

Image
Every programmer eventually comes across the terms time complexity and space complexity . These concepts are critical in writing efficient programs and are fundamental in coding interviews. In this post, we’ll dive deep into Big O , Big Ω , and Big Θ , understand how to analyze algorithms, and illustrate everything with diagrams and examples. 1. Why Do We Need Complexity Analysis? Imagine two algorithms solving the same problem. One takes 1 second for 1000 inputs, while the other takes 10 seconds . As input size grows, the difference becomes massive. Complexity analysis gives us a way to predict performance without running the program on all possible inputs. Diagram: Growth Rates Input size (n) → O(1): ■■■■■■■■■ (constant) O(log n): ■■■■■■■■■■■■■■ (slow growth) O(n): ■■■■■■■■■■■■■■■■■■■■■ (linear growth) O(n²): ■■■■■■■■■■■■■■■■■■■■■■■■■■■■■ (steep) O(2^n): skyrockets! 2. Big O, Big ...