Java Fundamentals for Developers
Java Fundamentals for Developers
This initial part is foundational. It ensures a rock-solid understanding of the Java language's
syntax, structure, and the paradigm it is built upon: Object-Oriented Programming. We will
meticulously cover the building blocks before constructing more complex applications.
This section covers the absolute essentials needed to write, compile, and run a Java program.
We will move quickly but thoroughly through the syntax and basic constructs, always adhering
to the "theory -> code -> practice" model.
Theoretical Explanation
Java is a high-level, object-oriented programming language known for its philosophy of "Write
Once, Run Anywhere" (WORA).1 This is made possible by the Java Virtual Machine (JVM), an
abstract computing machine that enables a computer to run a Java program. When you compile
Java code, it is not converted into platform-specific machine code but into an intermediate
format called bytecode, which is stored in .class files. The JVM, which is platform-dependent,
then interprets this platform-independent bytecode, translating it into native machine code for
the specific operating system it is running on.2
The Java ecosystem consists of three main components 2:
● JDK (Java Development Kit): The full-featured software development kit for Java,
including the JRE and development tools like the compiler (javac).
● JRE (Java Runtime Environment): The software package that provides the Java class
libraries, the JVM, and other components necessary to run Java applications.
● JVM (Java Virtual Machine): The core component of the JRE that executes Java
bytecode.
The traditional first program for any language is "Hello, World!". In Java, it demonstrates the
basic structure of a class and the main entry point for execution.1
Java
Practice Problems
1. Introduce Yourself: Modify the HelloWorld program to print your name and a short
greeting.
2. Multiple Lines: Write a program that prints three separate lines of text, each with a
different fact about Java.
3. ASCII Art: Write a program that uses several [Link]() statements to print a
simple ASCII art shape, like a tree or a smiley face.
4. Command-Line Arguments: Modify the main method to print the first command-line
argument passed to the program. For example, if you run java HelloWorld User, it should
print "Hello, User!". (Hint: The argument will be in args).
5. Compile and Run: Ensure you can compile the Java source file (.java) into a bytecode file
(.class) using the javac command and then run the program using the java command from
your terminal.
Theoretical Explanation
Variables are containers for storing data values. In Java, a variable must be declared with a
specific data type, which determines the size and layout of the variable's memory, the range of
values that can be stored, and the set of operations that can be applied to it. 5 Java's type
system is divided into two main categories: primitive types and reference types.6
This distinction is not merely academic; it has significant practical consequences for
performance and memory usage. Primitive types (int, double, etc.) store their values directly in
memory, typically on the stack for local variables. This makes them extremely fast and memory-
efficient. Reference types (all objects, including arrays and strings) store a reference—
essentially a memory address—that points to the actual object's data, which resides on the
heap.
This leads to a crucial behavior: when you pass a primitive to a method, its value is copied.
When you pass an object, the reference is copied, meaning both the original variable and the
method parameter point to the same object on the heap. This is why the Java Collections
Framework, a powerful set of data structures, can only store objects. To store a primitive like int
in an ArrayList, it must first be "boxed" into its corresponding wrapper class (Integer). This
process, known as autoboxing, adds a slight performance and memory overhead compared to
using a simple primitive array (int). Understanding this trade-off is key to making informed
decisions in performance-critical situations versus general application development where the
convenience of collections is paramount.
Java
Since Java 10, you can use the var keyword for local variable type inference, where the
compiler determines the type from the initializer.5
Java
Operators
Java provides a rich set of operators to perform operations on variables and values.5
● Arithmetic Operators: + (addition), - (subtraction), * (multiplication), / (division), %
(modulus).
● Assignment Operators: = (assignment), +=, -=, *=, /=, %=.
● Unary Operators: + (unary plus), - (unary minus), ++ (increment), -- (decrement), ! (logical
complement).
● Relational Operators: == (equal to), != (not equal to), > (greater than), < (less than), >=
(greater than or equal to), <= (less than or equal to).
● Logical Operators: && (logical AND), || (logical OR).
● Bitwise Operators: & (bitwise AND), | (bitwise OR), ^ (bitwise XOR), ~ (bitwise
complement), << (left shift), >> (signed right shift), >>> (unsigned right shift).
Java
// Using operators
int newScore = score + 5; // Arithmetic
newScore++; // Unary
[Link]("New Score: " + newScore);
// Type casting
int priceAsInt = (int) price; // Explicit casting from double to int
[Link]("Price as integer: " + priceAsInt);
Theoretical Explanation
Control flow statements are fundamental constructs that allow you to dictate the execution path
of your program based on certain conditions or by repeating a block of code.5 They are the
decision-making and looping mechanisms of the language.8
● Decision-Making Statements:
○ if-then-else: The most basic control flow statement. The if block executes if a condition
is true. The optional else if and else blocks provide alternative execution paths.8
○ switch: Allows a variable to be tested for equality against a list of values. Modern Java
(14+) introduced switch expressions, which are more concise, less error-prone (no fall-
through by default), and can return a value.5
● Looping Statements:
○ for: Executes a block of code a specific number of times. It consists of an initialization,
a termination condition, and an increment/decrement expression.8 The enhanced
for-each loop provides a simpler syntax for iterating over arrays or collections.
○ while: Repeats a block of code as long as a given condition is true. The condition is
checked before the loop body is executed.
○ do-while: Similar to while, but the condition is checked after the loop body is executed,
guaranteeing the loop runs at least once.
● Branching Statements:
○ break: Terminates the innermost switch, for, while, or do-while loop.
○ continue: Skips the current iteration of a loop and proceeds to the next one.
○ return: Exits from the current method and can optionally return a value.
Java
vowelCount += isVowel;
index++;
}
[Link]("The text contains " + vowelCount + " vowels.");
}
}
Practice Problems
1. FizzBuzz: Write a program that prints the numbers from 1 to 100. For multiples of three,
print "Fizz" instead of the number. For multiples of five, print "Buzz". For numbers which
are multiples of both three and five, print "FizzBuzz".
2. Prime Number Check: Write a program that takes an integer as input and determines
whether it is a prime number. A prime number is a natural number greater than 1 that has
no positive divisors other than 1 and itself.
3. Factorial Calculator: Create a program that calculates the factorial of a non-negative
integer using a for loop. The factorial of n (denoted as n!) is the product of all positive
integers up to n.
4. Number Guessing Game: Write a simple number guessing game. Generate a random
number between 1 and 100. Prompt the user to guess the number. Use a while loop to
continue prompting until the user guesses correctly. Provide hints like "Too high" or "Too
low".
5. Palindrome Checker: Write a program that checks if a given string is a palindrome (reads
the same forwards and backward), ignoring spaces and case. For example, "A man a plan
a canal Panama" is a palindrome.
6. Fibonacci Sequence: Write a program to print the first n numbers in the Fibonacci
sequence. In this sequence, each number is the sum of the two preceding ones, starting
from 0 and 1. (e.g., 0, 1, 1, 2, 3, 5, 8,...).
Theoretical Explanation
Arrays
An array is a container object that holds a fixed number of values of a single type. The length of
an array is established when it is created and cannot be changed afterward.9 Elements are
stored in contiguous memory locations and are accessed via a numerical index, which starts at
0.10
● Declaration: dataType arrayName; or dataType arrayName;
● Instantiation: arrayName = new dataType[size];
● Initialization: Can be done at declaration using an array literal: int numbers = {10, 20, 30};
or element by element using indices: numbers = 10;.
● Multi-dimensional Arrays: Java supports arrays of arrays, allowing for multi-dimensional
structures like matrices.9
Strings
A String in Java is an object that represents a sequence of characters. A critical characteristic of
Java strings is their immutability—once a String object is created, its value cannot be changed.1
Operations that appear to modify a string, like concatenation, actually create a new
String object.
Java
// Iterate through the array to find the max value and sum
for (int number : numbers) {
if (number > max) {
max = number;
}
sum += number;
}
double average = (double) sum / [Link];
[Link]("Array elements:");
for (int i = 0; i < [Link]; i++) {
[Link](numbers[i] + " ");
}
[Link]("\nMaximum value: " + max);
[Link]("Sum of elements: " + sum);
[Link]("Average of elements: " + average);
}
}
Java
String s2 = [Link](" World"); // The new string must be assigned to a new reference
[Link]("s2 is: " + s2); // Output: s2 is: Hello World
Theoretical Explanation
To create an object, you use the new keyword, which allocates memory for the object and
returns a reference to it. The state of an object is typically initialized using a special method
called a constructor. A constructor has the same name as the class and no return type.13
The this keyword is a reference to the current object instance. It is commonly used to resolve
ambiguity between instance variables and parameters with the same name, or to call another
constructor from within a constructor (constructor chaining).7
Java
[Link]();
[Link]();
}
}
Practice Problems
1. Create a Book Class: Design a Book class with fields for title, author, and publicationYear.
Include a constructor to initialize these fields and a method displayDetails() that prints all
the book's information to the console.
2. Create a Rectangle Class: Develop a Rectangle class with width and height as fields. Add
a constructor and two methods: calculateArea() and calculatePerimeter(), which return the
respective values.
3. Create a BankAccount Class: Model a BankAccount with fields for accountNumber,
accountHolderName, and balance. Implement methods for deposit(double amount) and
withdraw(double amount). Ensure that a withdrawal cannot result in a negative balance.
Add a method checkBalance() to display the current balance.
4. Multiple Constructors: Enhance the Book class from problem 1 by adding a second
constructor that only takes title and author, setting the publicationYear to a default value
(e.g., 2024).
5. Student Roster: Create a Student class with studentId, name, and an array or ArrayList of
grades. Implement a constructor and methods to addGrade(int grade) and
calculateAverageGrade(). In your main method, create a few student objects, add grades
to them, and display their average grades.
Theoretical Explanation
Encapsulation is the practice of bundling an object's data (fields) and the methods that operate
on that data into a single unit, the class.14 A key aspect of encapsulation is
data hiding, which means restricting direct access to an object's internal state from outside the
object's class.16 This is achieved by declaring the fields as
private.17
To allow controlled access to these private fields, you provide public methods known as getters
(to retrieve a value) and setters (to modify a value). This approach has several benefits 16:
● Data Integrity: Setters can include validation logic to ensure that the object's state remains
valid (e.g., preventing a negative age).
● Flexibility and Maintainability: The internal implementation of the class can be changed
without affecting the code that uses it, as long as the public getter/setter methods remain
the same.
● Control: You can create read-only fields (by providing only a getter) or write-only fields (by
providing only a setter).
This example demonstrates an Employee class where the data is encapsulated. The salary can
only be set if it's a positive value, and the employeeId is read-only after creation.
Java
class Company {
public static void main(String args) {
Employee emp1 = new Employee("E1001", "John Doe");
Practice Problems
1. Person Class with Age Validation: Create a Person class with private fields for name
and age. Implement a setter for age that only allows values between 0 and 130.19
2. BankAccount with Secure Balance: Design a BankAccount class with a private balance.
The deposit method should only accept positive amounts. The withdraw method should
only allow withdrawals if the amount is positive and does not exceed the current balance.19
3. Rectangle with Immutable Dimensions: Create a Rectangle class with private length and
width. Make these fields read-only by providing only getter methods. The dimensions
should be set once via the constructor.19
4. Car with Mileage Tracking: Implement a Car class with private fields for model and
mileage. The mileage should be read-only (only a getter). Create a drive(int miles) method
that increases the mileage. The mileage can never decrease.19
5. Student with Grade Management: Create a Student class with private fields studentId,
name, and a list of grades. Provide a public method addGrade(int grade) that adds a grade
to the list, but only if the grade is between 0 and 100. Provide a getter that returns a copy
of the grades list to prevent external modification of the original list.19
6. Book with Price Discount: Design a Book class with private title, author, and price.
Provide getters and setters for all fields. Add a method applyDiscount(double percentage)
that reduces the price by the given percentage, ensuring the discount percentage is
between 0 and 100.19
Theoretical Explanation
Inheritance is a fundamental OOP mechanism that allows a new class (the subclass or child
class) to acquire the properties and methods of an existing class (the superclass or parent
class).15 This establishes an "is-a" relationship; for example, a
Dog "is an" Animal. The primary benefits are code reusability and the ability to create a
hierarchical classification of classes.13
In Java, inheritance is implemented using the extends keyword. Java supports single
inheritance (a class can extend only one superclass) and multi-level inheritance (a chain of
inheritance, e.g., C extends B, and B extends A).21 Java does not support
multiple inheritance with classes to prevent the "diamond problem," where ambiguity arises if
a class inherits methods with the same signature from two different superclasses.18
This example shows a base Vehicle class and a Car subclass that inherits from it, adding its
own specific functionality.
Java
// Superclass
class Vehicle {
protected String brand; // 'protected' allows access by subclasses
// Subclass
class Car extends Vehicle {
private int numberOfDoors;
Practice Problems
1. Animal Hierarchy: Create a base class Animal with a method eat(). Create two
subclasses, Herbivore and Carnivore, that extend Animal. Override the eat() method in
each subclass to print what they eat (e.g., "Eats plants" and "Eats meat").
2. Shape and Rectangle: Create a Shape class with properties color and a method getArea()
that returns 0. Create a Rectangle subclass that extends Shape and has length and width
properties. Override the getArea() method to calculate and return the correct area of the
rectangle.25
3. Employee and Manager: Design an Employee class with name and salary. Create a
Manager subclass that adds a bonus property. Override a calculatePay() method in both
classes. The manager's pay should include the bonus.26
4. Person and Student: Create a Person class with name and age. Then, create a Student
class that extends Person and adds a studentId and major. Ensure the constructors are
chained correctly using super().25
5. BankAccount and SavingsAccount: Create a BankAccount class with a withdraw()
method. Create a SavingsAccount subclass that overrides withdraw() to prevent the
balance from dropping below a minimum threshold (e.g., $100).25
6. Add a Method to Bird: Given an Animal class with a walk() method and a Bird class that
extends Animal and has a fly() method, add a sing() method to the Bird class and call all
three methods (walk, fly, sing) from the main method.27
Theoretical Explanation
Polymorphism, meaning "many forms," is an OOP principle that allows a single action to be
performed in different ways.28 In Java, polymorphism is primarily achieved through method
overloading and method overriding.15
1. Compile-Time Polymorphism (Static Binding): This is achieved through method
overloading. It allows a class to have multiple methods with the same name, as long as
their parameter lists are different (either in the number of parameters, the type of
parameters, or both). The compiler determines which method to call at compile time based
on the method signature.29
2. Runtime Polymorphism (Dynamic Binding): This is achieved through method
overriding and is a more powerful form of polymorphism. It occurs when a subclass
provides a specific implementation for a method that is already defined in its superclass.
The decision of which method to execute is made at runtime, based on the actual type of
the object being referenced, not the type of the reference variable.28 This allows for writing
flexible and extensible code. For example, a reference variable of a superclass type can
point to an object of any of its subclasses, and calling an overridden method on that
reference will invoke the subclass's version.
Java
Java
class Shape {
public void draw() {
[Link]("Drawing a generic shape");
}
}
Practice Problems
1. Area Calculator with Overloading: Create a Geometry class with overloaded
calculateArea() methods. One method should take the radius of a circle (double), and
another should take the length and width of a rectangle (two doubles).
2. Animal Sounds: Create a base class Animal with a method makeSound(). Create
subclasses Dog, Cat, and Cow, each overriding makeSound() to print "Bark", "Meow", and
"Moo" respectively. In your main method, create an array of Animal objects, populate it with
instances of Dog, Cat, and Cow, and then loop through the array, calling makeSound() on
each object.32
3. Employee Salary Calculation: Create an Employee base class with a calculateSalary()
method. Create Manager and Programmer subclasses. Override calculateSalary() in each
to calculate salary based on different rules (e.g., managers get a bonus, programmers get
overtime pay).32
4. Vehicle Speed Up: Create a Vehicle class with a speedUp() method. Create Car and
Bicycle subclasses. Override speedUp() in each to increase speed by a different amount
(e.g., car by 10, bicycle by 2).32
5. Print Formatter: Design a Printer class with overloaded print() methods that can accept
and correctly format different data types for printing (e.g., an int, a double with two decimal
places, and a String in quotes).
6. Drawing Shapes: Extend the Shape example (Code Example 2) by adding a Triangle
class. Create a method drawShapes(Shape shapes) that takes an array of Shape objects
and calls the draw() method on each one, demonstrating that the method works for any
shape.
Abstraction is the concept of hiding complex implementation details and showing only the
essential features of an object.15 It helps in managing complexity by providing a simplified, high-
level interface. In Java, abstraction is achieved using
Method Types Can have abstract and Can only have abstract
concrete methods. methods (Java 8+ allows
default and static methods
with implementation).
Multiple Inheritance A class can extend only one A class can implement
abstract class. multiple interfaces.
Variables Can have final, non-final, Variables are implicitly
static, and non-static public, static, and final.
variables.
Java
// Abstract superclass
abstract class BankAccount {
private String accountNumber;
protected double balance;
@Override
public void deposit(double amount) {
if (amount > 0) {
balance += amount;
}
}
@Override
public void withdraw(double amount) {
if (amount > 0 && (balance - amount) >= 100) { // Minimum balance rule
balance -= amount;
} else {
[Link]("Withdrawal failed for Savings Account.");
}
}
}
Java
@Override
public void play() {
[Link]("Playing MP3: " + currentSong);
}
@Override
public void pause() {
[Link]("Pausing MP3: " + currentSong);
}
@Override
public void stop() {
[Link]("Stopping MP3: " + currentSong);
}
}
@Override
public void play() {
[Link]("Playing video: " + currentMovie);
}
@Override
public void pause() {
[Link]("Pausing video: " + currentMovie);
}
@Override
public void stop() {
[Link]("Stopping video: " + currentMovie);
}
}
Practice Problems
1. Abstract Animal Class: Create an abstract class Animal with an abstract method sound()
and a concrete method sleep(). Create Lion and Tiger subclasses that extend Animal and
implement the sound() method.36
2. Shape Interface: Create an interface Shape with abstract methods calculateArea() and
calculatePerimeter(). Create Circle and Triangle classes that implement this interface.36
3. Instrument Interface: Design an interface Instrument with abstract methods play() and
tune(). Create Violin and Piano classes that implement the Instrument interface with their
specific behaviors.36
4. Abstract Employee Class: Create an abstract Employee class with an abstract method
calculateSalary() and a concrete method displayInfo(). Implement Manager and
Programmer subclasses that provide specific salary calculation logic.36
5. Drawable Interface: Create an interface Drawable with a single method draw(). Have
Circle, Rectangle, and Triangle classes implement this interface. Write a program that can
draw any object that is Drawable.
6. Abstract vs. Interface Choice: Consider a system for different types of vehicles (Car,
Motorcycle, Truck). Would you use an abstract class or an interface as the base type?
Justify your choice. Implement a basic version of your chosen design. (Hint: Consider if
there is any common behavior or state all vehicles share).
This part transitions from language syntax to the powerful frameworks and advanced features
that are used daily in professional Java development, forming the backbone of modern, robust
applications.
The Java Collections Framework is a unified architecture for representing and manipulating
collections, which are objects that group multiple elements into a single unit.37 It provides a set
of interfaces and classes that significantly reduce programming effort and increase performance
by offering optimized, reusable data structures and algorithms.38
Before the Collections Framework, Java relied on basic constructs like arrays, Vector, and
Hashtable. These lacked a common interface, making the code hard to extend and maintain.
The framework, located in the [Link] package, solves this by providing a hierarchy of
interfaces (Collection, List, Set, Map) and standard implementations (ArrayList, HashSet,
HashMap).39
The core of the framework is the Collection interface, which extends the Iterable interface.
Iterable ensures that every collection can be traversed, typically using an Iterator or a for-each
loop. The Collection interface defines the basic operations for all collections, such as adding,
removing, and querying elements.39
3.2 Generics
Theoretical Explanation
Generics are the mechanism that provides type-safety to the Collections Framework. They allow
you to create classes, interfaces, and methods that operate on types as parameters. A generic
type is declared with angle brackets (<>) containing a type parameter, conventionally a single
uppercase letter like T for Type or E for Element.40
Java
import [Link];
import [Link];
public class GenericsDemo {
public static void main(String args) {
// Without generics (raw type) - Not recommended
List rawList = new ArrayList();
[Link]("Hello");
[Link](123); // No compile-time error, but potential for runtime error
Practice Problems
1. Generic Box: Create a generic class Box<T> that can hold a single item of any type. It
should have setItem(T item) and getItem() methods.
2. Generic Pair: Create a generic class Pair<K, V> that holds a key and a value of any two
types.
3. Generic Swap Method: Write a static generic method swap(T array, int i, int j) that swaps
the elements at indices i and j in an array of any type.
4. Bounded Type Parameter: Write a generic method findMax(List<T> list) that finds the
maximum element in a list. Use a bounded type parameter (<T extends Comparable<T>>)
to ensure that the elements can be compared.
5. Wildcard Usage: Write a method printList(List<?> list) that can print any list, regardless of
its element type. Then, write another method sumOfList(List<? extends Number> list) that
calculates the sum of a list of numbers (e.g., List<Integer>, List<Double>).
Theoretical Explanation
The List interface represents an ordered collection of elements, also known as a sequence. It
allows duplicate elements and provides positional access to elements via an integer index.39
The two most common implementations are
Java
import [Link];
import [Link];
import [Link];
Theoretical Explanation
The Set interface represents a collection that contains no duplicate elements. It models the
mathematical set abstraction.37 The three main implementations are:
● HashSet: Stores elements in a hash table. It offers the best performance for add, remove,
and contains operations (average O(1) time), but it makes no guarantees about the
iteration order.39
● LinkedHashSet: A subclass of HashSet that also maintains a doubly-linked list running
through all of its entries. This preserves the insertion order of elements.
● TreeSet: Stores elements in a sorted order (natural ordering or by a specified Comparator).
It is implemented using a red-black tree, providing O(log n) time complexity for most
operations.
Java
import [Link];
import [Link];
import [Link];
Practice Problems
1. Union of Two Sets: Write a method that takes two Set objects and returns a new Set
containing the union of the two (all elements from both sets).
2. Intersection of Two Sets: Write a method that takes two Set objects and returns a new
Set containing only the elements that are present in both sets (the intersection).
3. Custom Object in HashSet: Create a Student class with id and name. Override the
hashCode() and equals() methods. Create a HashSet<Student> and add several Student
objects, including duplicates, to demonstrate that the set correctly handles uniqueness.
4. Find First Recurring Character: Given a string, find the first character that repeats. Use a
Set to keep track of the characters you've seen.
5. Check for Duplicates in Array: Write a method that takes an integer array and returns
true if it contains any duplicate values, and false otherwise. Use a HashSet for an efficient
solution.
Theoretical Explanation
The Queue interface represents a collection designed for holding elements prior to processing,
following a First-In, First-Out (FIFO) principle.43 This is like a line of people waiting for a service;
the first one in line is the first one served.
Common implementations include:
● LinkedList: As seen before, LinkedList implements the Queue interface, making it a
common choice for a general-purpose FIFO queue.
● ArrayDeque: A resizable-array implementation of a double-ended queue (Deque), which
can be used as a highly efficient FIFO queue. It is generally preferred over LinkedList for
queue operations when iteration is not needed.
● PriorityQueue: An implementation that orders elements based on their natural ordering or
a supplied Comparator. The head of this queue is the "least" element with respect to the
specified ordering. It does not follow a strict FIFO order but rather a priority-based order. 39
Java
import [Link];
import [Link];
Practice Problems
1. Ticket Counter Simulation: Simulate a ticket counter where customers arrive and are
served in the order they arrive. Use a Queue to manage the line of customers.
2. Priority Task Scheduler: Use a PriorityQueue to manage a list of tasks. Each task should
have a priority (e.g., 1 for high, 5 for low). The program should always process the task
with the highest priority first.
3. Breadth-First Search (BFS) Traversal: Implement a simple BFS algorithm for a graph or
tree. A Queue is essential for keeping track of the nodes to visit at each level.
4. Implement a Queue using Two Stacks: A classic interview question. Implement a
MyQueue class that has enqueue and dequeue methods, using only two Stack objects for
storage.
5. Sliding Window Maximum: Given an array of integers and a window size k, find the
maximum value in each sliding window of size k. A Deque (like ArrayDeque) is often used
for an efficient O(n) solution.
Theoretical Explanation
The Map interface represents a collection of key-value pairs. Each key must be unique, and it
maps to exactly one value. Maps are ideal for fast lookups, insertions, and deletions based on a
key.37
Just like with HashSet, the keys in a HashMap or LinkedHashMap must have a correct
implementation of hashCode() and equals().
Java
import [Link];
import [Link];
public class WordFrequencyCounter {
public static void main(String args) {
String text = "the quick brown fox jumps over the lazy dog and the fox was quick";
String words = [Link](" ");
[Link]("Word Frequencies:");
// Iterate over the map's entry set
for ([Link]<String, Integer> entry : [Link]()) {
[Link]([Link]() + ": " + [Link]());
}
}
}
Practice Problems
1. Character Frequency: Write a program to count the frequency of each character in a
given string and store the results in a HashMap.
2. Two Sum Problem: Given an array of integers and a target sum, find the indices of the
two numbers that add up to the target. Use a HashMap for an efficient O(n) solution.
3. Phone Book: Create a simple phone book application using a HashMap to store names
(keys) and phone numbers (values). Implement functionalities to add, remove, and look up
a contact.
4. Sort a Map by Value: Write a program to sort a HashMap by its values. (Hint: This is tricky
because maps are not inherently sortable by value. You might need to transfer the entries
to a List and sort the list).
5. LRU Cache: Implement a Least Recently Used (LRU) cache. This is a classic interview
problem that can be solved efficiently using a LinkedHashMap. The cache should have a
fixed capacity and evict the least recently used item when full.
This table provides an at-a-glance reference for the performance characteristics of the most
common Collection implementations. Understanding these complexities is crucial for selecting
the right data structure for a given problem, a frequent topic in technical interviews.
Data Get/ Search Insertion Insertion Deletion Deletion
Structure Access (End) (Mid) (End) (Mid)
HashMap O(1) avg O(1) avg O(1) avg N/A O(1) avg N/A
HashSet N/A O(1) avg O(1) avg N/A O(1) avg N/A
This section covers topics essential for building robust, real-world applications and are
frequently tested in interviews for intermediate roles.
Theoretical Explanation
Exception handling is a powerful mechanism for managing runtime errors in a structured and
robust way, allowing a program to handle unexpected situations gracefully instead of crashing.1
An exception is an event that disrupts the normal flow of the program's instructions.
In Java, all exceptions are objects that are subclasses of the Throwable class. The hierarchy is:
● Throwable: The root class.
○ Error: Represents serious problems that a reasonable application should not try to
catch, such as OutOfMemoryError.
○ Exception: Represents conditions that a reasonable application might want to catch.
■ Checked Exceptions: Exceptions that are checked at compile-time (e.g.,
IOException, SQLException). A method that might throw a checked exception
must either handle it using a try-catch block or declare it using the throws keyword.
■ Unchecked Exceptions (Runtime Exceptions): Subclasses of RuntimeException
(e.g., NullPointerException, IllegalArgumentException). They are not checked at
compile-time and typically indicate programming errors.
Keywords:
● try: The block of code to be monitored for exceptions.
● catch: The block of code that handles the exception.
● finally: The block of code that is always executed, whether an exception is thrown or not.
It's often used for cleanup, like closing resources.
● throw: Used to manually throw an exception.
● throws: Used in a method signature to declare the exceptions that the method might throw.
The try-with-resources statement (Java 7+) is the recommended way to handle resources that
implement the AutoCloseable interface, as it ensures the resource is closed automatically.7
Java
import [Link];
import [Link];
import [Link];
Practice Problems
1. Custom Exception: Create a custom checked exception called InvalidAgeException.
Write a method that takes an age as input and throws this exception if the age is negative.
2. Input Validation: Write a program that prompts the user for a number. Use a try-catch
block to handle InputMismatchException if the user enters non-numeric input.
3. Multiple Catch Blocks: Write a method that might throw either an ArithmeticException
(e.g., division by zero) or a NullPointerException. Use separate catch blocks to handle
each exception differently.
4. Chained Exceptions: Create a scenario where a low-level method throws an exception,
which is caught by a mid-level method. The mid-level method should then wrap the original
exception in a new, more specific custom exception and throw it again.7
5. Resource Cleanup: Write a program that opens a file for writing and writes some text to it.
Use a finally block to ensure the file writer is closed, even if an error occurs during writing.
Then, rewrite the program using a try-with-resources statement.
Theoretical Explanation
Creating Threads:
There are two primary ways to create a thread in Java:
1. Extending the Thread class: Create a new class that extends Thread and override its
run() method.
2. Implementing the Runnable interface: Create a new class that implements Runnable
and its run() method. Then, pass an instance of this class to a Thread object's constructor.
This is the preferred approach as it allows the class to extend another class if needed.
Synchronization:
When multiple threads access shared resources, concurrency issues like race conditions
(where the outcome depends on the non-deterministic timing of thread execution) can occur. To
prevent this, Java provides synchronization mechanisms:
● synchronized keyword: Can be applied to methods or blocks of code. It ensures that only
one thread can execute a synchronized block of code for a given object instance at a time,
by acquiring an intrinsic lock (or monitor).
● Locks: The [Link] package provides more advanced and flexible
locking mechanisms, like ReentrantLock.
Thread States:
A thread can be in one of several states: NEW, RUNNABLE, BLOCKED, WAITING,
TIMED_WAITING, and TERMINATED.
[Link] Package:
This package provides high-level concurrency utilities that simplify the development of
concurrent applications. Key components include:
● Executor Framework: Manages a pool of worker threads, decoupling task submission
from task execution (e.g., ExecutorService, ThreadPoolExecutor).
● Concurrent Collections: Thread-safe versions of collections like ConcurrentHashMap.
● Atomic Variables: Classes like AtomicInteger that support lock-free, thread-safe
programming on single variables.
Java
@Override
public void run() {
for (int i = 0; i < 5; i++) {
[Link](100);
try {
[Link](100); // Simulate some work
} catch (InterruptedException e) {
[Link]();
}
}
}
}
[Link]();
[Link]();
}
}
Practice Problems
1. Producer-Consumer Problem: Implement the classic producer-consumer problem using
wait() and notify() on a shared buffer (e.g., a LinkedList). A producer thread adds items to
the buffer, and a consumer thread removes them.
2. Using ExecutorService: Rewrite the Producer-Consumer problem using a thread pool
created with [Link]() and a thread-safe queue like
ArrayBlockingQueue.
3. Deadlock Creation: Write a program that creates a deadlock scenario between two
threads that are trying to acquire locks on two different resources in a different order.
4. Singleton with Double-Checked Locking: Implement a thread-safe Singleton pattern
using the double-checked locking idiom and the volatile keyword.
5. Web Crawler: Design a simple multi-threaded web crawler. Use an ExecutorService to
manage worker threads that download web pages. Use a concurrent collection to keep
track of visited URLs to avoid redundant work.
6. Parallel Summation: Write a program that calculates the sum of a large array of numbers
using multiple threads. Divide the array into segments and assign each segment to a
different thread. Finally, aggregate the results from all threads.
Theoretical Explanation
Java 8 introduced functional programming features, most notably lambda expressions and the
Stream API, which provide a more expressive, concise, and powerful way to work with data,
especially collections.1
● Lambda Expressions: A lambda expression is an anonymous function (a function without
a name) that you can use to create instances of functional interfaces (interfaces with a
single abstract method). They provide a clear and compact way to represent a piece of
functionality.
○ Syntax: (parameters) -> { body } or (parameters) -> expression
● Stream API: A stream is a sequence of elements that supports various aggregate
operations. It is not a data structure that stores elements; instead, it carries values from a
source (like a collection) through a pipeline of operations.
○ Pipeline: A stream pipeline consists of a source, zero or more intermediate
operations, and a terminal operation.
○ Intermediate Operations: These are lazy operations that return a new stream,
allowing operations to be chained. Examples include filter() (selects elements based on
a predicate), map() (transforms each element), and sorted().
○ Terminal Operations: These are eager operations that produce a result or a side-
effect, triggering the execution of the pipeline. Examples include forEach() (performs
an action for each element), collect() (gathers elements into a collection), and reduce()
(combines elements into a single result).
Java
import [Link];
import [Link];
import [Link];
class Person {
private String name;
private int age;
@Override
public String toString() { return "Person{" + "name='" + name + '\'' + ", age=" + age + '}'; }
}
// Find the names of people older than 25, sorted by age, in uppercase.
List<String> result = [Link]() // 1. Get a stream from the list
.filter(p -> [Link]() > 25) // 2. Intermediate op: filter by age
.sorted((p1, p2) -> [Link]([Link](), [Link]())) // 3. Intermediate op: sort by
age
.map(p -> [Link]().toUpperCase()) // 4. Intermediate op: transform name to uppercase
.collect([Link]()); // 5. Terminal op: collect results into a new list
[Link](result);
}
}
Practice Problems
1. Filter and Count: Given a list of strings, use a stream to count how many strings have a
length greater than 5.
2. Map and Sum: Given a list of Product objects (with name and price fields), use a stream to
calculate the total price of all products.
3. Find First: Given a list of integers, use a stream to find the first even number greater than
10. Use Optional to handle the case where no such number exists.
4. Grouping By: Given the Person list from the code example, use [Link]() to
group the people by their age into a Map<Integer, List<Person>>.
5. FlatMap: You have a list of lists of integers, List<List<Integer>>. Use flatMap to flatten it
into a single list of integers, List<Integer>.
6. Reduce to Find Longest String: Given a list of strings, use the reduce operation to find
the longest string in the list.
Theoretical Explanation
Java Database Connectivity (JDBC) is a Java API that enables Java applications to connect
and interact with databases.7 It provides a standard, vendor-independent way to execute SQL
statements against a relational database.46
This example assumes you have a MySQL database with a table named employees and the
MySQL Connector/J driver JAR in your project's classpath.
Java
import [Link].*;
Practice Problems
1. Insert a Record: Write a JDBC program that inserts a new record into the employees table
using a PreparedStatement.
2. Update a Record: Write a program to update the salary of a specific employee (identified
by their ID) in the employees table.
3. Delete a Record: Create a program that deletes an employee from the table based on
their ID.
4. Transaction Management: Write a program that transfers a certain amount from one bank
account to another. This should be done within a single transaction. Use
[Link](false), [Link](), and [Link]() to ensure the atomicity of
the operation.
5. Database Metadata: Use the DatabaseMetaData interface to retrieve and print information
about the connected database, such as the database product name, version, and a list of
tables.
Theoretical Explanation
While deep JVM expertise is a specialized field, a high-level understanding of its architecture is
crucial for any intermediate Java developer to diagnose performance issues, understand
memory management, and write efficient code.2 The JVM can be broken down into three main
subsystems.2
1. Classloader Subsystem: Responsible for loading, linking, and initializing class files. It
dynamically loads classes into the JVM at runtime as they are needed.3
2. Runtime Data Areas: These are the memory areas the JVM uses during program
execution.
○ Method Area: A shared resource among all threads that stores class-level data, such
as the runtime constant pool, field and method data, and the code for methods.
○ Heap: Another shared resource where all objects and arrays are allocated. This is the
area managed by the Garbage Collector.
○ Stack: Each thread has its own private JVM stack, created at the same time as the
thread. It stores frames. Each time a method is invoked, a new frame is created and
pushed onto the stack for that thread. A frame holds local variables, operand stacks,
and data for method dispatch. When the method completes, its frame is popped. This
is why a deep, unterminated recursion results in a StackOverflowError.
○ PC (Program Counter) Registers: Each thread has a PC register that stores the
address of the JVM instruction currently being executed.
○ Native Method Stacks: Used for native (non-Java) code.
3. Execution Engine: This is responsible for executing the bytecode.
○ Interpreter: Reads, interprets, and executes bytecode instructions one by one.
○ JIT (Just-In-Time) Compiler: To improve performance, the JIT compiler analyzes the
bytecode as it runs and compiles frequently executed parts ("hotspots") into native
machine code, which can be executed much faster than interpreted code.3
○ Garbage Collector (GC): An automatic memory management process. The GC
reclaims heap memory used by objects that are no longer referenced by the program,
preventing memory leaks. Different GC algorithms exist (e.g., Serial, Parallel, G1) with
different performance characteristics.49
This part is the dedicated interview preparation section, focusing on the implementation and
analysis of core Data Structures and Algorithms (DSA). A strong command of DSA is the single
most important factor for success in technical interviews at top-tier companies.50 For each topic,
we will provide a from-scratch implementation to build a deep understanding, followed by a
discussion of its real-world counterpart in the Java Collections Framework.
A key to bridging academic knowledge with professional practice is recognizing that the Java
Collections Framework is a production-grade, highly optimized implementation of these
fundamental data structures. ArrayList is a dynamic array, LinkedList is a doubly-linked list,
HashMap is a hash table, and TreeMap is a self-balancing binary search tree. Understanding
the underlying DSA will demystify how these collections work and empower you to choose the
right tool for the job based on performance characteristics.
Section 5: Core Data Structures
5.1 Arrays
Theoretical Explanation
An array is the simplest data structure, storing a collection of elements of the same type in a
contiguous block of memory. Its key feature is constant-time access, O(1), to any element via its
index. However, its size is fixed upon creation, and insertions or deletions in the middle are
expensive (O(n)) because they require shifting elements.51
This implementation shows how a dynamic array can automatically resize itself when it runs out
of capacity.
Java
import [Link];
public DynamicArray() {
[Link] = new Object;
[Link] = 0;
}
@SuppressWarnings("unchecked")
public T get(int index) {
if (index < 0 |
Practice Problems 53
1. Find the Duplicate Number: Given an array containing n + 1 integers where each integer
is between 1 and n (inclusive), prove that at least one duplicate number must exist.
Assume that there is only one duplicate number, find the duplicate.
2. Maximum Subarray: Given an integer array, find the contiguous subarray (containing at
least one number) which has the largest sum and return its sum (Kadane's Algorithm).
3. Product of Array Except Self: Given an integer array nums, return an array answer such
that answer[i] is equal to the product of all the elements of nums except nums[i]. You must
solve it in O(n) time and without using the division operation.
4. Merge Intervals: Given an array of intervals where intervals[i] = [start_i, end_i], merge all
overlapping intervals, and return an array of the non-overlapping intervals that cover all the
intervals in the input.
5. Container With Most Water: You are given an integer array height of length n. There are
n vertical lines drawn such that the two endpoints of the i-th line are (i, 0) and (i, height[i]).
Find two lines that together with the x-axis form a container, such that the container
contains the most water.
6. Trapping Rain Water: Given n non-negative integers representing an elevation map
where the width of each bar is 1, compute how much water it can trap after raining.
Theoretical Explanation
A linked list is a linear data structure where elements are not stored at contiguous memory
locations. Instead, elements are stored in nodes, where each node contains its data and a
reference (or pointer) to the next node in the sequence.45
● Singly Linked List: Each node points only to the next node.
● Doubly Linked List: Each node points to both the next and the previous node, allowing for
bidirectional traversal. Java's LinkedList class is a doubly-linked list.44
Linked lists offer dynamic size and efficient insertions/deletions at the ends (O(1)), but slow
element access (O(n)).56
Java
Node(T data) {
[Link] = data;
[Link] = null;
}
}
public void add(T data) {
Node<T> newNode = new Node<>(data);
if (head == null) {
head = newNode;
} else {
Node<T> current = head;
while ([Link]!= null) {
current = [Link];
}
[Link] = newNode;
}
size++;
}
Practice Problems 57
1. Reverse a Linked List: Given the head of a singly linked list, reverse the list, and return
the new head.
2. Detect Cycle in a Linked List: Given head, the head of a linked list, determine if the
linked list has a cycle in it (Floyd's Tortoise and Hare algorithm).
3. Merge Two Sorted Lists: You are given the heads of two sorted linked lists. Merge the
two lists into one sorted list and return the head of the new list.
4. Remove Nth Node From End of List: Given the head of a linked list, remove the nth node
from the end of the list and return its head.
5. Intersection of Two Linked Lists: Given the heads of two singly linked-lists, return the
node at which the two lists intersect. If the two linked lists have no intersection at all, return
null.
6. Palindrome Linked List: Given the head of a singly linked list, return true if it is a
palindrome.
5.3 Stacks
Theoretical Explanation
A stack is a linear data structure that follows the Last-In, First-Out (LIFO) principle. Think of it
like a stack of plates: the last plate you put on top is the first one you take off. 59 The primary
operations are:
● push: Adds an element to the top of the stack.
● pop: Removes and returns the element from the top of the stack.
● peek: Returns the element at the top without removing it.
In Java, the legacy Stack class exists, but it's recommended to use a Deque implementation like
ArrayDeque for stack operations, as it is more efficient and consistent.61
Java
public ArrayStack() {
[Link] = new Object;
[Link] = -1;
}
@SuppressWarnings("unchecked")
public T peek() {
if (isEmpty()) {
throw new IllegalStateException("Stack is empty");
}
return (T) array[top];
}
Practice Problems 60
1. Valid Parentheses: Given a string containing just the characters (, ), {, }, [ and ], determine
if the input string is valid. An input string is valid if open brackets are closed by the same
type of brackets and in the correct order.
2. Min Stack: Design a stack that supports push, pop, top, and retrieving the minimum
element in constant time.
3. Evaluate Reverse Polish Notation: Evaluate the value of an arithmetic expression in
Reverse Polish Notation (e.g., ["2", "1", "+", "3", "*"] -> (2 + 1) * 3 = 9).
4. Daily Temperatures: Given a list of daily temperatures, produce a list that, for each day in
the input, tells you how many days you would have to wait until a warmer temperature. If
there is no future day for which this is possible, put 0 instead.
5. Implement Queue using Stacks: Implement a FIFO queue using only two stacks. The
implemented queue should support all the functions of a normal queue (push, peek, pop,
and empty).
5.4 Queues
Theoretical Explanation
A queue is a linear data structure that follows the First-In, First-Out (FIFO) principle. Elements
are added to the rear (enqueue) and removed from the front (dequeue).43 This models a real-
world queue or line.
● enqueue (or add/offer): Adds an element to the rear of the queue.
● dequeue (or remove/poll): Removes and returns the element from the front of the queue.
● peek (or element): Returns the element at the front without removing it.
Java
public T dequeue() {
if (front == null) {
throw new IllegalStateException("Queue is empty");
}
T data = [Link];
front = [Link];
if (front == null) {
rear = null;
}
return data;
}
Practice Problems
1. Implement Stack using Queues: Implement a LIFO stack using only two queues. The
implemented stack should support all the functions of a normal stack (push, top, pop, and
empty).
2. Number of Islands: Given a 2D grid map of '1's (land) and '0's (water), count the number
of islands. An island is surrounded by water and is formed by connecting adjacent lands
horizontally or vertically. (BFS using a queue is a common approach).
3. Level Order Traversal of a Binary Tree: Given the root of a binary tree, return the level
order traversal of its nodes' values (i.e., from left to right, level by level). A queue is
essential for this algorithm.
4. Generate Binary Numbers: Given a number n, write a function that generates and prints
all binary numbers with decimal values from 1 to n. Use a queue.
5. Moving Average from Data Stream: Given a stream of integers and a window size,
calculate the moving average of all integers in the sliding window.
5.5 Hash Tables
Theoretical Explanation
A hash table (or hash map) is a data structure that stores key-value pairs. It uses a hash
function to compute an index, also called a hash code, into an array of buckets or slots, from
which the desired value can be found.62 This allows for average-case constant time complexity,
O(1), for insertion, deletion, and retrieval.64
A collision occurs when two different keys hash to the same index. Common collision
resolution strategies include 63:
● Separate Chaining: Each bucket is the head of a linked list. All keys that hash to the same
bucket are stored in that list. This is the strategy used by Java's HashMap.
● Open Addressing: When a collision occurs, the algorithm probes the table for the next
empty slot.
Java
import [Link];
@SuppressWarnings("unchecked")
public SimpleHashMap(int capacity) {
[Link] = capacity;
[Link] = new LinkedList[capacity];
for (int i = 0; i < capacity; i++) {
table[i] = new LinkedList<>();
}
}
Practice Problems 65
1. Two Sum: Given an array of integers nums and an integer target, return indices of the two
numbers such that they add up to target. You may assume that each input would have
exactly one solution, and you may not use the same element twice.
2. Group Anagrams: Given an array of strings, group the anagrams together. You can return
the answer in any order.
3. Logger Rate Limiter: Design a logger system that receives a stream of messages along
with their timestamps. Each unique message should only be printed at most every 10
seconds.
4. First Unique Character in a String: Given a string, find the first non-repeating character
in it and return its index. If it does not exist, return -1.
5. Ransom Note: Given two strings, ransomNote and magazine, return true if ransomNote
can be constructed from the letters in magazine, and false otherwise. Each letter in
magazine can only be used once.
6. Insert Delete GetRandom O(1): Implement a data structure that supports inserting,
deleting, and retrieving a random element, all in average O(1) time.
5.6 Trees
Theoretical Explanation
Java
public class BinarySearchTree {
private Node root;
Node(int value) {
[Link] = value;
left = right = null;
}
}
Practice Problems 70
1. Maximum Depth of Binary Tree: Given the root of a binary tree, return its maximum
depth.
2. Validate Binary Search Tree: Given the root of a binary tree, determine if it is a valid
binary search tree (BST).
3. Invert Binary Tree: Given the root of a binary tree, invert the tree, and return its root.
4. Lowest Common Ancestor of a BST: Given a binary search tree (BST), find the lowest
common ancestor (LCA) of two given nodes in the BST.
5. Binary Tree Level Order Traversal: Given the root of a binary tree, return the level order
traversal of its nodes' values.
6. Convert Sorted Array to Binary Search Tree: Given an integer array nums where the
elements are sorted in ascending order, convert it to a height-balanced binary search tree.
5.7 Graphs
Theoretical Explanation
A graph is a non-linear data structure consisting of a set of vertices (or nodes) and a set of
edges that connect pairs of vertices.72 Graphs are used to model networks and relationships.
Graph Representations:
● Adjacency Matrix: A 2D array where matrix[i][j] = 1 if there is an edge from vertex i to
vertex j. It's space-inefficient for sparse graphs but fast for checking the existence of an
edge.74
● Adjacency List: An array of lists, where adjList[i] contains a list of all vertices adjacent to
vertex i. It's space-efficient for sparse graphs.75
Graph Traversal Algorithms:
● Breadth-First Search (BFS): Explores the graph level by level from a starting vertex. It
uses a queue.
● Depth-First Search (DFS): Explores as far as possible along each branch before
backtracking. It uses a stack (or recursion).
Java
import [Link].*;
public Graph() {
[Link] = new HashMap<>();
}
Practice Problems 76
1. Number of Islands: Given an m x n 2D binary grid which represents a map of '1's (land)
and '0's (water), return the number of islands.
2. Clone Graph: Given a reference of a node in a connected undirected graph, return a deep
copy (clone) of the graph.
3. Course Schedule: There are a total of numCourses courses you have to take, labeled
from 0 to numCourses - 1. You are given an array prerequisites where prerequisites[i] =
[a_i, b_i] indicates that you must take course b_i first if you want to take course a_i. Return
true if you can finish all courses. Otherwise, return false. (This is a topological sort
problem).
4. Word Ladder: Given two words, beginWord and endWord, and a dictionary's word list,
return the length of the shortest transformation sequence from beginWord to endWord,
such that only one letter can be changed at a time and each transformed word must exist
in the word list.
5. Shortest Path in Unweighted Graph: Find the shortest path between two nodes in an
unweighted graph using BFS.
6. Detect Cycle in a Directed Graph: Given a directed graph, check whether the graph
contains a cycle or not.
Theoretical Explanation
Searching algorithms are used to find a specific element within a data structure. The choice of
algorithm depends heavily on whether the data is sorted.78
● Linear Search: This is the simplest search algorithm. It sequentially checks each element
of the list until a match is found or the whole list has been searched. It works on unsorted
data but has a time complexity of O(n).79
● Binary Search: A highly efficient algorithm that works only on sorted data. It repeatedly
divides the search interval in half. If the value of the search key is less than the item in the
middle of the interval, it narrows the interval to the lower half. Otherwise, it narrows it to the
upper half. This process continues until the value is found or the interval is empty. Its time
complexity is O(log n).79
Java
Java
if (sortedArr[mid] == target) {
return mid; // Target found
} else if (sortedArr[mid] < target) {
left = mid + 1; // Search in the right half
} else {
right = mid - 1; // Search in the left half
}
}
return -1; // Target not found
}
A peak element is an element that is strictly greater than its neighbors. Given an integer array
Theoretical Explanation
Code Examples
Java
import [Link];
// Bubble Sort
public static void bubbleSort(int arr) {
int n = [Link];
for (int i = 0; i < n - 1; i++) {
for (int j = 0; j < n - i - 1; j++) {
if (arr[j] > arr[j + 1]) {
int temp = arr[j];
arr[j] = arr[j + 1];
arr[j + 1] = temp;
}
}
}
}
// Merge Sort
public static void mergeSort(int arr, int n) {
if (n < 2) {
return;
}
int mid = n / 2;
int l = new int[mid];
int r = new int[n - mid];
private static void merge(int a, int l, int r, int left, int right) {
int i = 0, j = 0, k = 0;
while (i < left && j < right) {
if (l[i] <= r[j]) {
a[k++] = l[i++];
} else {
a[k++] = r[j++];
}
}
while (i < left) {
a[k++] = l[i++];
}
while (j < right) {
a[k++] = r[j++];
}
}
Practice Problems
1. Sort Colors: Given an array nums with n objects colored red, white, or blue, sort them in-
place so that objects of the same color are adjacent, with the order red, white, and blue.
Use integers 0, 1, and 2 to represent the color red, white, and blue, respectively. (Dutch
National Flag problem).
2. Kth Largest Element in an Array: Given an integer array nums and an integer k, return
the k-th largest element in the array. (Hint: A modified Quick Sort partition method is very
efficient).
3. Top K Frequent Elements: Given an integer array nums and an integer k, return the k
most frequent elements. You may return the answer in any order.
4. Meeting Rooms II: Given an array of meeting time intervals intervals where intervals[i] =
[start_i, end_i], return the minimum number of conference rooms required. (Hint: Sorting is
the first step).
5. Merge K Sorted Lists: You are given an array of k linked-lists, where each linked-list is
sorted in ascending order. Merge all the linked-lists into one sorted linked-list and return it.
6.3 Recursion
Theoretical Explanation
Recursion can lead to elegant and simple solutions for problems that can be broken down into
smaller, self-similar subproblems, such as tree traversals or factorial calculations.
Java
Practice Problems 85
1. Fibonacci Number: Write a recursive function to calculate the nth Fibonacci number.
2. Reverse a String: Write a recursive function that takes a string as input and returns the
string reversed.
3. Power Function: Implement pow(x, n), which calculates x raised to the power n (i.e., xn),
using recursion.
4. Subsets (Power Set): Given an integer array nums of unique elements, return all possible
subsets (the power set). The solution set must not contain duplicate subsets.
5. Merge Sort: Implement the Merge Sort algorithm using recursion. The core logic of
dividing the array is recursive.
Theoretical Explanation
Java
import [Link];
import [Link];
Practice Problems 88
1. Climbing Stairs: You are climbing a staircase. It takes n steps to reach the top. Each time
you can either climb 1 or 2 steps. In how many distinct ways can you climb to the top?
2. 0/1 Knapsack Problem: You are given weights and values of n items. You need to put
these items in a knapsack of capacity W to get the maximum total value. You cannot break
an item.
3. Longest Increasing Subsequence: Given an integer array nums, return the length of the
longest strictly increasing subsequence.
4. Coin Change: You are given an integer array coins representing coins of different
denominations and an integer amount representing a total amount of money. Return the
fewest number of coins that you need to make up that amount. If that amount of money
cannot be made up by any combination of the coins, return -1.
5. Word Break Problem: Given a string s and a dictionary of strings wordDict, return true if s
can be segmented into a space-separated sequence of one or more dictionary words.
This final part provides a glimpse into the tools and practices of a professional Java developer.
Mastering these tools is essential for working effectively in a team, building robust applications,
and moving beyond academic exercises into real-world software engineering.
Theoretical Explanation
Build automation tools are essential for managing a project's lifecycle, including compiling
source code, managing dependencies, running tests, and packaging the application. The two
dominant build tools in the Java ecosystem are Maven and Gradle.89
● Apache Maven: A long-standing and widely used tool that uses a declarative XML file,
[Link], for configuration. It follows a principle of "convention over configuration,"
providing a rigid
Works cited