Explain the contract between `equals()` and `hashCode()`.
The contract between equals()
and hashCode()
is critical when using objects in hash-based collections like HashMap
, HashSet
, and Hashtable
.
The contract, as defined in the Object
class documentation, states:
equals(Object)
method, then calling the hashCode()
method on each of the two objects must produce the same integer result. (If a.equals(b)
is true, then a.hashCode() == b.hashCode()
must be true.)equals(Object)
method, it is not required that calling the hashCode()
method on each of the two objects produce distinct integer results. However, producing distinct results for unequal objects may improve the performance of hash tables. (If a.equals(b)
is false, a.hashCode() == b.hashCode()
can be true or false.)In simple terms:
Failing to follow this contract will lead to incorrect behavior when objects are stored in hash-based collections. For example, if you add an object to a `HashSet` and then try to check if it `contains()` an equal object, the check will fail if the hash codes are different, even if `.equals()` would return true.
What is the difference between checked and unchecked exceptions?
In Java, the exception hierarchy is headed by the Throwable
class.
Checked Exceptions:
Exception
class (but not subclasses of RuntimeException
).try-catch
block or declare it using the throws
keyword.IOException
, SQLException
, and ClassNotFoundException
. They represent exceptional conditions that a well-written application should anticipate and recover from.Unchecked Exceptions (Runtime Exceptions):
RuntimeException
class.NullPointerException
, ArrayIndexOutOfBoundsException
, and IllegalArgumentException
.There is also a third category, Errors, which are also unchecked. They represent serious problems that a reasonable application should not try to catch, such as OutOfMemoryError
or StackOverflowError
.
What is the difference between `throw` and `throws`?
throw
keyword is used to explicitly throw a single exception from a method or any block of code. It is used inside a method body to throw an instance of an Exception
or any of its subclasses.void checkAge(int age) {
if (age < 18) {
throw new ArithmeticException("Access denied - You must be at least 18 years old.");
}
}
throws
keyword is used in a method signature to declare the exceptions that the method might throw. It informs the caller of the method about the exceptions it needs to handle. It can declare multiple exceptions, separated by a comma.// This method declares that it can throw an IOException
import java.io.IOException;
void myMethod() throws IOException {
// code that might produce an IOException
}
What is the purpose of the `finally` block?
The finally
block is an optional block in a try-catch
statement that is used to execute important cleanup code.
The key feature of the finally
block is that it always executes, regardless of whether an exception was thrown or caught in the try
block. The only times a `finally` block will not execute are if the JVM exits (e.g., System.exit()
is called) or if a catastrophic error occurs that halts the thread.
It is primarily used for resource de-allocation, such as:
This ensures that resources are released properly, preventing resource leaks.
Connection conn = null;
try {
conn = Database.getConnection();
// ... perform database operations
} catch (SQLException e) {
// ... handle exception
} finally {
if (conn != null) {
try {
conn.close();
} catch (SQLException e) {
/* log or ignore */
}
}
}
Note: The `try-with-resources` statement, introduced in Java 7, is now the preferred way to handle resource cleanup as it's more concise and less error-prone.
What is the `try-with-resources` statement?
Introduced in Java 7, the try-with-resources
statement is a feature that ensures that each resource is closed at the end of the statement. It simplifies the cleanup of resources like files, sockets, and database connections.
Any object that implements the java.lang.AutoCloseable
or java.io.Closeable
interface can be used as a resource.
Before Java 7 (using a `finally` block):
FileOutputStream fos = null;
try {
fos = new FileOutputStream("file.txt");
fos.write("Hello".getBytes());
} catch (IOException e) {
e.printStackTrace();
} finally {
if (fos != null) {
try {
fos.close();
} catch (IOException e) {
e.printStackTrace();
}
}
}
With `try-with-resources`:
try (FileOutputStream fos = new FileOutputStream("file.txt")) {
fos.write("Hello".getBytes());
} catch (IOException e) {
e.printStackTrace();
}
The compiler automatically generates the code to close the FileOutputStream
resource, making the code much cleaner and safer by avoiding common resource leak errors.
What is the Collections Framework?
The Java Collections Framework is a unified architecture for representing and manipulating collections (groups of objects).
It provides a set of standard interfaces and classes to handle common data structures. Key interfaces include:
Collection
: The root interface of the framework.List
: An ordered collection that allows duplicate elements. Major implementations are ArrayList
and LinkedList
.Set
: A collection that does not allow duplicate elements. Major implementations are HashSet
, LinkedHashSet
, and TreeSet
.Queue
: A collection used to hold elements prior to processing, typically in a FIFO (First-In, First-Out) order.Map
: An object that maps keys to values. It does not allow duplicate keys. It is not a true 'Collection' as it doesn't extend the Collection
interface, but it is considered part of the framework. Major implementations are HashMap
, LinkedHashMap
, and TreeMap
.The framework provides high-performance implementations of these data structures and algorithms to manipulate them (like sorting and searching via the `Collections` utility class).
What is the difference between `ArrayList` and `LinkedList`?
Both `ArrayList` and `LinkedList` are implementations of the `List` interface, but they have very different underlying data structures.
Feature | ArrayList | LinkedList |
---|---|---|
Underlying Structure | Uses a dynamic array to store elements. | Uses a doubly-linked list (each element has a reference to the previous and next element). |
Element Access | Fast. Provides fast random access using the get(int index) method, which takes O(1) time, because it can be calculated directly from the array index. | Slow. To access an element at a specific index, it has to traverse the list from the beginning or end, which takes O(n) time. |
Insertion/Deletion | Slow. Adding or removing an element from the middle of the list is slow because it requires shifting all subsequent elements, which takes O(n) time. Adding to the end is generally O(1). | Fast. Adding or removing an element from the beginning or end is very fast (O(1)). Insertion/deletion in the middle is also faster than ArrayList if you already have a reference to the node. |
Memory Overhead | Lower memory overhead as it only stores the elements. | Higher memory overhead because each element (node) stores the data plus references to the next and previous nodes. |
When to Use | Best choice when your primary need is to access elements by their index and you don't have a lot of insertion/deletion operations in the middle of the list. | Best choice when you have a large number of insertion/deletion operations, especially at the beginning or end of the list, and random access is not a priority. |
How does a `HashMap` work internally?
A HashMap
in Java works on the principle of hashing. It stores data in key-value pairs.
put(key, value)
operation:put(key, value)
, the HashMap
first calculates the hash code of the key by calling its hashCode()
method.HashMap
's internal array of nodes. The formula is typically index = hashCode(key) & (n-1)
, where 'n' is the size of the array.HashMap
goes to that index.Node(key, value)
and places it there.equals()
method.get(key)
operation:equals()
method to find the node with the exact key, and returns the corresponding value. If no such key is found, it returns null
.This is why the contract between equals()
and hashCode()
is so important for the correct functioning of a `HashMap`.
What is the difference between `HashSet`, `LinkedHashSet`, and `TreeSet`?
All three are implementations of the `Set` interface, which means they store unique elements.
Feature | HashSet | LinkedHashSet | TreeSet |
---|---|---|---|
Underlying Structure | Backed by a HashMap . | Backed by a LinkedHashMap . It's a combination of a hash table and a linked list. | Backed by a TreeMap , which uses a Red-Black Tree (a self-balancing binary search tree). |
Ordering | Unordered. It makes no guarantees about the iteration order of the elements; it may even change over time. | Ordered. It maintains the insertion order of elements. When you iterate through it, elements appear in the order they were added. | Sorted. It stores elements in a sorted order (either natural ordering or by a specified Comparator ). |
Performance | Offers the best performance for add, remove, and contains operations (O(1) on average). | Slightly slower than HashSet due to the overhead of maintaining the linked list, but still O(1) on average. | Slower for add, remove, and contains operations (O(log n)) because it needs to maintain the sorted order. |
Null Elements | Allows one null element. | Allows one null element. | Does not allow null elements (will throw a NullPointerException ), as it needs to compare elements. |
What is the difference between `Comparable` and `Comparator`?
Both interfaces are used to sort objects in Java.
Comparable
Interface:
int compareTo(Object obj)
.public class Employee implements Comparable<Employee> {
//...
private int id;
@Override
public int compareTo(Employee other) {
return Integer.compare(this.id, other.id);
}
}
// To sort: Collections.sort(listOfEmployees);
Comparator
Interface:
int compare(Object obj1, Object obj2)
.public class SortEmployeeByName implements Comparator<Employee> {
@Override
public int compare(Employee e1, Employee e2) {
return e1.getName().compareTo(e2.getName());
}
}
// To sort: Collections.sort(listOfEmployees, new SortEmployeeByName());
In summary, use `Comparable` for the default, natural order. Use `Comparator` for custom or multiple sorting orders.