Introduction

An array is an indexed collection of fixed number of homogeneous data elements. Alternatively, it represents a group of elements of the same data type. The primary advantage of arrays is that we can represent a large number of elements using a single variable, improving code readability.

Limitations of Arrays

  1. Arrays are fixed in size. Once created, the size cannot be increased or decreased dynamically.
  2. Arrays can hold only homogeneous data elements.
  3. Arrays lack ready-made method support because they are not implemented using any data structure.

Examples of Arrays


// Fixed-size array of Students
Student[] s = new Student[10000];
s[0] = new Student();   // Valid
s[1] = new Customer();  // Compile-time error

// Error Output:
Test.java:7: cannot find symbol
Symbol: class Customer
Location: class Test
s[1] = new Customer();
      

Using Object Arrays to Address Homogeneity Issue

We can use an Object[] array to hold heterogeneous elements:


Object[] o = new Object[10000];
o[0] = new Student();    // Valid
o[1] = new Customer();   // Valid
      

Introduction to Collections

Collections in Java overcome the limitations of arrays by being growable in nature and providing support for both homogeneous and heterogeneous data. They are implemented based on standard data structures and come with ready-made methods for common operations.

Advantages of Collections

  1. Growable: Collections can dynamically increase or decrease in size as per requirements.
  2. Memory Efficient: More suitable for memory management than arrays.
  3. Homogeneous and Heterogeneous: Can hold both types of data elements.
  4. Ready-Made Methods: Collections offer standard methods for operations, simplifying development.

Differences Between Arrays and Collections

Feature Arrays Collections
Size Fixed Growable
Memory Usage Not memory-efficient Highly memory-efficient
Performance Better performance Relatively slower
Data Type Homogeneous only Both homogeneous and heterogeneous
Underlying Data Structure None Implemented based on standard data structures
Primitive Type Support Supports both primitives and objects Supports only objects

Conclusion

While arrays are more performant and simpler for small, fixed-size homogeneous data, collections are versatile, memory-efficient, and developer-friendly for dynamic and mixed-type data requirements.

Java Collection Framework

The Collection Framework in Java provides a standardized way to represent and manipulate groups of objects as a single entity. It offers various classes and interfaces to make working with collections easier and more efficient.

1. What is a Collection?

If we want to represent a group of objects as a single entity, we use a collection. It is a container for storing, managing, and processing objects.

2. Java Collection Framework

The Collection Framework defines several classes and interfaces to represent a group of objects as a single entity. It is analogous to the STL (Standard Template Library) in C++.

Java C++
Collection Containers
Collection Framework STL (Standard Template Library)

3. Key Interfaces of the Collection Framework

The Collection Framework consists of 9 key interfaces that define the structure and behavior of collections in Java:

Java Collection Framework

The Java Collection Framework provides interfaces and classes to represent and manipulate groups of objects as single entities.

1. Collection

The Collection interface is considered the root interface of the entire collection framework. It defines the most common methods applicable to any collection object.

  • If we want to represent a group of "individual objects" as a single entity, we should use a collection.
  • No concrete class implements the Collection interface directly.
  • Child Interfaces: List, Set, Queue

2. List

The List interface is a child of the Collection interface. It is used to represent a group of objects where:

  • Duplicates are allowed.
  • Insertion order is preserved.
  • Child Classes: ArrayList, LinkedList, Vector, Stack

3. Set

The Set interface is a child of the Collection interface. It is used to represent a group of objects where:

  • Duplicates are not allowed.
  • Insertion order is not preserved.
  • Child Classes: HashSet, LinkedHashSet

4. SortedSet

The SortedSet interface is a child of the Set interface. It is used to represent a group of unique objects arranged in a sorted order.

  • Child Classes: TreeSet

5. NavigableSet

The NavigableSet interface is a child of the SortedSet interface. It provides several methods for navigation purposes, such as:

  • lower(), higher()
  • floor(), ceiling()
  • Child Classes: TreeSet

6. Queue

The Queue interface is a child of the Collection interface. It is used to represent a group of objects prior to processing. The common behavior of a queue is FIFO (First In, First Out).

  • Child Classes: LinkedList, PriorityQueue, ArrayDeque

7. Map

The Map interface is used to represent a group of objects as key-value pairs.

Key points:

  • Keys must be unique, but values can be duplicated.
  • The Map interface is not a child of the Collection interface.
  • Child Classes: HashMap, LinkedHashMap, Hashtable, TreeMap

8. SortedMap

The SortedMap interface is a child of the Map interface. It is used to represent a group of key-value pairs sorted by keys.

  • Child Classes: TreeMap

9. NavigableMap

The NavigableMap interface is a child of the SortedMap interface. It provides navigation methods, such as:

  • lowerKey(), higherKey()
  • descendingMap()
  • Child Classes: TreeMap

Java Collection Framework: Key Differences

1. Differences Between Collection and Map

Aspect Collection Map
Definition Represents a group of individual objects as a single entity. Represents a group of key-value pairs.
Hierarchy Rooted at the Collection interface. Not part of the Collection hierarchy.
Duplicates May or may not allow duplicates (depending on implementation). Does not allow duplicate keys; values can be duplicated.
Ordering Depends on the specific implementation (e.g., List preserves insertion order). Keys can be ordered if using SortedMap or TreeMap.
Examples List, Set, Queue HashMap, TreeMap, LinkedHashMap

2. Differences Between List, Set, and Queue

Aspect List Set Queue
Definition Represents a group of objects where duplicates are allowed, and insertion order is preserved. Represents a group of unique objects with no duplicates allowed. Represents a group of objects arranged in the order of processing.
Duplicates Allowed. Not allowed. Allowed, depending on the implementation.
Order Insertion order is preserved. Order is not preserved (except in LinkedHashSet). Order depends on the queue type (e.g., FIFO for Queue, priority order for PriorityQueue).
Key Methods add(), get(), remove(), indexOf() add(), remove(), contains() offer(), poll(), peek()
Examples ArrayList, LinkedList, Vector, Stack HashSet, LinkedHashSet, TreeSet LinkedList, PriorityQueue, ArrayDeque

List Implementations in Java

The Java List interface represents an ordered collection (also known as a sequence). It allows duplicate elements and provides methods to access elements by their position in the list. Below are descriptions of key list implementations in Java:

1. ArrayList

The ArrayList class uses a dynamic array to store the elements. It is part of the java.util package and implements the List interface. It allows duplicates, preserves insertion order, and allows null values.

Example: ArrayList


  import java.util.ArrayList;
  
  public class ArrayListExample {
      public static void main(String[] args) {
          ArrayList list = new ArrayList<>();
          list.add("Apple");
          list.add("Banana");
          list.add("Cherry");
  
          // Displaying elements
          for (String fruit : list) {
              System.out.println(fruit);
          }
      }
  }
        

2. LinkedList

The LinkedList class uses a doubly linked list to store elements. It implements both the List and Deque interfaces. It allows duplicates, preserves insertion order, and allows null values.

Example: LinkedList


  import java.util.LinkedList;
  
  public class LinkedListExample {
      public static void main(String[] args) {
          LinkedList list = new LinkedList<>();
          list.add("Apple");
          list.add("Banana");
          list.add("Cherry");
  
          // Displaying elements
          for (String fruit : list) {
              System.out.println(fruit);
          }
      }
  }
        

3. Vector

The Vector class is similar to ArrayList but with synchronized methods. It is a legacy class in the java.util package and also implements the List interface. Vectors are thread-safe but tend to have performance issues due to synchronization.

Example: Vector


  import java.util.Vector;
  
  public class VectorExample {
      public static void main(String[] args) {
          Vector vector = new Vector<>();
          vector.add("Apple");
          vector.add("Banana");
          vector.add("Cherry");
  
          // Displaying elements
          for (String fruit : vector) {
              System.out.println(fruit);
          }
      }
  }
        

4. Stack

The Stack class represents a last-in, first-out (LIFO) stack of objects. It extends Vector and provides methods like push(), pop(), peek(), and empty() for stack operations. Like Vector, it is synchronized.

Example: Stack


  import java.util.Stack;
  
  public class StackExample {
      public static void main(String[] args) {
          Stack stack = new Stack<>();
          stack.push("Apple");
          stack.push("Banana");
          stack.push("Cherry");
  
          // Displaying elements using LIFO order
          while (!stack.isEmpty()) {
              System.out.println(stack.pop());
          }
      }
  }
        

Differences Between List Implementations

Differences Between List Implementations

Feature ArrayList Vector LinkedList Stack
Data Structure Resizable Array Resizable Array Doubly Linked List Stack (LIFO)
Allows Duplicates Yes Yes Yes Yes
Insertion Order Yes Yes Yes Yes
Null Insertion Yes Yes Yes Yes
Supports Heterogeneous Data Yes Yes Yes Yes
Best For Retrieval Operations Retrieval Operations Frequent Modifications Last-In-First-Out Operations
Thread-Safety No Yes (but slower) No Yes (but slower)
Legacy Class No Yes No Yes

Difference Between ArrayList and Vector

Aspect ArrayList Vector
Data Structure Resizable array Resizable array
Thread Safety Not synchronized Synchronized (thread-safe)
Performance Faster for most operations Slower due to synchronization overhead
Growth Factor Increases by 50% when full Increases by doubling its size when full

Difference Between ArrayList and LinkedList

Aspect ArrayList LinkedList
Data Structure Resizable array Doubly linked list
Insertion/Deletion Slower (due to shifting elements) Faster (due to linked list structure)
Access Time Faster (random access with index) Slower (no index-based access)
Memory Consumption Less memory overhead More memory overhead due to node pointers

Set Implementations in Java

The Java Set interface represents a collection of unique elements. It does not allow duplicate elements and provides methods for accessing elements, but does not guarantee order. The following are key `Set` implementations in Java:

1. HashSet

The HashSet class uses a hash table for storage. It does not allow duplicates, and the insertion order is not preserved. It allows null insertion and supports heterogeneous data types.

Example: HashSet


  import java.util.HashSet;
  
  public class HashSetExample {
      public static void main(String[] args) {
          HashSet set = new HashSet<>();
          set.add("Apple");
          set.add("Banana");
          set.add("Cherry");
  
          // Displaying elements
          for (String fruit : set) {
              System.out.println(fruit);
          }
      }
  }
        

2. LinkedHashSet

The LinkedHashSet class is similar to HashSet, but it also maintains a doubly linked list of elements to preserve the insertion order. It does not allow duplicates and allows null insertion, and it supports heterogeneous data types.

Example: LinkedHashSet


  import java.util.LinkedHashSet;
  
  public class LinkedHashSetExample {
      public static void main(String[] args) {
          LinkedHashSet set = new LinkedHashSet<>();
          set.add("Apple");
          set.add("Banana");
          set.add("Cherry");
  
          // Displaying elements
          for (String fruit : set) {
              System.out.println(fruit);
          }
      }
  }
        

3. TreeSet

The TreeSet class implements the Set interface using a balanced tree. It does not allow duplicates and preserves elements according to a natural order or a custom comparator. However, it does not allow null insertion and supports only homogeneous data types (all elements must be of the same type).

Example: TreeSet


  import java.util.TreeSet;
  
  public class TreeSetExample {
      public static void main(String[] args) {
          TreeSet set = new TreeSet<>();
          set.add("Apple");
          set.add("Banana");
          set.add("Cherry");
  
          // Displaying elements
          for (String fruit : set) {
              System.out.println(fruit);
          }
      }
  }
        

Differences Between Set Implementations

Difference Between HashSet, LinkedHashSet, and TreeSet

Aspect HashSet LinkedHashSet TreeSet
Data Structure Hash Table Hash Table + Doubly Linked List Balanced Tree
Duplicates Does not allow duplicates Does not allow duplicates Does not allow duplicates
Insertion Order Not preserved Preserved Not preserved (sorted order)
Null Insertion Allowed Allowed Not allowed
Supports Heterogeneous Data Yes Yes No (Homogeneous data only)
Sorting Order Not sorted Not sorted Sorted by natural order or custom comparator
Best For When insertion order doesn't matter and fast operations are required When insertion order needs to be preserved When sorted order is required for elements

Queue Implementations in Java

The Queue interface in Java represents a collection designed for holding elements prior to processing. It follows the First-In-First-Out (FIFO) principle. Here are some key queue implementations in Java:

1. Queue

The Queue interface represents a collection of elements, where elements are processed in a FIFO order. It is part of the java.util package and provides methods such as offer(), poll(), and peek().

2. PriorityQueue

The PriorityQueue class implements the Queue interface and orders elements based on their natural order or according to a specified comparator. It does not guarantee FIFO order, as elements are dequeued in priority order rather than the order of insertion.

Example: PriorityQueue


  import java.util.PriorityQueue;
  
  public class PriorityQueueExample {
      public static void main(String[] args) {
          PriorityQueue queue = new PriorityQueue<>();
          queue.add(10);
          queue.add(5);
          queue.add(20);
  
          // Displaying elements (will display in order of priority)
          while (!queue.isEmpty()) {
              System.out.println(queue.poll());
          }
      }
  }
        

3. LinkedList

The LinkedList class implements the Queue interface as well, and it provides a doubly linked list-based implementation. It maintains the FIFO order for processing elements and allows for both efficient insertion and removal from both ends.

Example: LinkedList as Queue


  import java.util.LinkedList;
  import java.util.Queue;
  
  public class LinkedListQueueExample {
      public static void main(String[] args) {
          Queue queue = new LinkedList<>();
          queue.offer("Apple");
          queue.offer("Banana");
          queue.offer("Cherry");
  
          // Displaying elements (FIFO order)
          while (!queue.isEmpty()) {
              System.out.println(queue.poll());
          }
      }
  }
        

Differences Between Queue Implementations

Difference Between PriorityQueue and LinkedList

Aspect PriorityQueue LinkedList
Data Structure Binary Heap (Priority-based ordering) Doubly Linked List (FIFO order)
Preserves Insertion Order No (ordered by priority) Yes (FIFO order)
Null Insertion No Yes
Element Removal Based on priority FIFO order (from the head)
Best For Priority-based processing FIFO processing, efficient insertion and removal from both ends

Map Implementations in Java

The Map interface in Java represents a collection of key-value pairs, where each key is unique. Unlike other collections like List or Set, a Map allows the storage of data in the form of key-value associations. Below are some key implementations of the Map interface in Java:

1. Map

The Map interface represents a collection of key-value pairs where each key maps to exactly one value. It allows fast lookups, updates, and deletions of key-value pairs. While duplicate keys are not allowed, duplicate values are permitted.

2. HashMap

The HashMap class implements the Map interface using a hash table. It provides constant-time performance for most operations (insertion, deletion, retrieval) assuming the hash function distributes the elements properly.

Example: HashMap


  import java.util.HashMap;
  
  public class HashMapExample {
      public static void main(String[] args) {
          HashMap map = new HashMap<>();
          map.put(1, "Apple");
          map.put(2, "Banana");
          map.put(3, "Cherry");
  
          // Displaying key-value pairs
          map.forEach((key, value) -> System.out.println(key + ": " + value));
      }
  }
        

3. LinkedHashMap

The LinkedHashMap class extends HashMap and maintains the insertion order of key-value pairs. It uses a doubly linked list to maintain the order in which elements were inserted, while still providing the fast lookup performance of a hash table.

Example: LinkedHashMap


  import java.util.LinkedHashMap;
  
  public class LinkedHashMapExample {
      public static void main(String[] args) {
          LinkedHashMap map = new LinkedHashMap<>();
          map.put(1, "Apple");
          map.put(2, "Banana");
          map.put(3, "Cherry");
  
          // Displaying key-value pairs (insertion order is preserved)
          map.forEach((key, value) -> System.out.println(key + ": " + value));
      }
  }
        

4. TreeMap

The TreeMap class implements the Map interface using a Red-Black tree. It orders the key-value pairs based on the natural ordering of the keys, or a specified comparator, and does not allow duplicate keys. TreeMap does not allow null keys, and only supports homogeneous data for keys.

Example: TreeMap


  import java.util.TreeMap;
  
  public class TreeMapExample {
      public static void main(String[] args) {
          TreeMap map = new TreeMap<>();
          map.put(3, "Apple");
          map.put(1, "Banana");
          map.put(2, "Cherry");
  
          // Displaying key-value pairs (sorted by key)
          map.forEach((key, value) -> System.out.println(key + ": " + value));
      }
  }
        

Differences Between Map Implementations

Difference Between HashMap, LinkedHashMap, and TreeMap

Aspect HashMap LinkedHashMap TreeMap
Data Structure Hash Table Hash Table + Doubly Linked List Red-Black Tree
Preserves Insertion Order No Yes No (sorted by key)
Null Insertion Yes (one null key, multiple null values) Yes (one null key, multiple null values) No (keys cannot be null)
Sorting of Keys No sorting No sorting (preserves insertion order) Sorted by natural order or custom comparator
Supports Heterogeneous Data Yes (for both keys and values) Yes (for both keys and values) No (supports only homogeneous data for keys)
Best For Fast lookups and updates with no ordering guarantee Preserving insertion order with fast lookups Automatic sorting of keys (natural or custom)

Hash Tables, Dictionaries, and Properties in Java

In Java, hash-based data structures such as HashTable, Dictionary, and Properties are used to store key-value pairs efficiently. Here's a detailed explanation:

1. HashTable

HashTable is a synchronized implementation of a hash table that maps keys to values.

Example: Using HashTable


  import java.util.Hashtable;
  
  public class HashTableExample {
      public static void main(String[] args) {
          Hashtable table = new Hashtable<>();
          table.put(1, "Apple");
          table.put(2, "Banana");
          table.put(3, "Cherry");
  
          System.out.println("HashTable: " + table);
          System.out.println("Value for key 2: " + table.get(2));
      }
  }
        

2. Dictionary

Dictionary is an abstract class that represents a key-value pair data structure. It is the parent class of HashTable.

Example: Using Dictionary


  import java.util.Dictionary;
  import java.util.Hashtable;
  
  public class DictionaryExample {
      public static void main(String[] args) {
          Dictionary dictionary = new Hashtable<>();
          dictionary.put(1, "Alice");
          dictionary.put(2, "Bob");
          dictionary.put(3, "Charlie");
  
          System.out.println("Dictionary: " + dictionary);
          System.out.println("Value for key 1: " + dictionary.get(1));
      }
  }
        

3. Properties

Properties is a subclass of Hashtable that is used to maintain a list of key-value pairs, where both the key and the value are strings. It is often used for configuration purposes.

Example: Using Properties


  import java.io.FileOutputStream;
  import java.io.IOException;
  import java.util.Properties;
  
  public class PropertiesExample {
      public static void main(String[] args) {
          Properties properties = new Properties();
          properties.setProperty("username", "admin");
          properties.setProperty("password", "12345");
  
          System.out.println("Properties: " + properties);
  
          try (FileOutputStream fos = new FileOutputStream("config.properties")) {
              properties.store(fos, "Configuration Settings");
              System.out.println("Properties saved to file.");
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
  }
        

Loading Properties from a File


  import java.io.FileInputStream;
  import java.io.IOException;
  import java.util.Properties;
  
  public class LoadPropertiesExample {
      public static void main(String[] args) {
          Properties properties = new Properties();
          try (FileInputStream fis = new FileInputStream("config.properties")) {
              properties.load(fis);
              System.out.println("Properties loaded: " + properties);
          } catch (IOException e) {
              e.printStackTrace();
          }
      }
  }
        

Comparison: HashTable vs HashMap

Both HashTable and HashMap are implementations of the Map interface in Java, but they have significant differences. Here's a detailed comparison:

Aspect HashTable HashMap
Synchronization Thread-safe and synchronized. Multiple threads cannot access it simultaneously. Not synchronized. It is faster but requires external synchronization for thread safety.
Null Keys and Values Does not allow null keys or null values. Allows one null key and multiple null values.
Performance Slower due to synchronization overhead. Faster because it is not synchronized.
Legacy vs Modern Part of the older java.util legacy classes. Introduced in Java 1.2 as part of the modern java.util collections framework.
Usage in Concurrent Environments Suitable for multithreaded environments due to built-in synchronization. Requires explicit synchronization or use of Collections.synchronizedMap() for multithreaded environments.
Default Capacity 11 (resized by multiplying capacity by 2 and adding 1). 16 (resized by doubling the capacity).