0% found this document useful (0 votes)
25 views51 pages

M1

Module 1 covers the Java Collections Framework, detailing various collection types such as List, Set, Map, and Queue, along with their key characteristics and operations. It emphasizes the advantages of using collections for efficiency, reusability, and interoperability, and provides examples of common implementations like ArrayList, HashSet, and HashMap. The document also includes sample programs demonstrating the usage of these collections and their operations.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
25 views51 pages

M1

Module 1 covers the Java Collections Framework, detailing various collection types such as List, Set, Map, and Queue, along with their key characteristics and operations. It emphasizes the advantages of using collections for efficiency, reusability, and interoperability, and provides examples of common implementations like ArrayList, HashSet, and HashMap. The document also includes sample programs demonstrating the usage of these collections and their operations.
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 51

Module 1

The collections and Framework: Collections Overview, The Collection Interfaces, The Collection
Classes, accessing a collection Via an Iterator, Storing User Defined Classes in Collections, The
Random Access Interface, Working with Maps, Comparators, The Collection Algorithms, Arrays, The
legacy Classes and Interfaces, Parting Thoughts on Collections.

Text Book 1: Ch. 18

Collections Overview

What is a Collection?
The term "Collection" in Java refers to a group of objects, known as elements, that are grouped
together. It represents a general concept of a container for objects.

Key Components of the Java Collections Framework


The Java Collections Framework (JCF) is a comprehensive set of classes and interfaces that provide
reusable, type-safe, and efficient data structures to store and manipulate groups of objects.

Interfaces: These define the types of collections and the operations you can perform on
them. They are like blueprints or contracts that specify what methods a collection must have.
Collection: The root interface that represents a group of objects.

Advantages of Collection Frameworks

Efficiency: It provides optimized and well-tested data structures and algorithms, making your
code more efficient.

Reusability: You can use the same collection interfaces and classes for different types of data,
which makes your code more reusable.

Interoperability: Since collections in Java are standardized, they can easily work together and
with other parts of the Java API.

Reduced Effort: The framework provides ready-to-use implementations, so you don't need to
write your own data structures from scratch.
List: An ordered collection (like an array but dynamic in size). Examples: ArrayList, LinkedList.
 Ordered collection
 Allows duplicates
 Indexed access
 Methods: get(), set(), add(index, element)
Set: A collection that does not allow duplicate elements. Examples: HashSet, TreeSet.
 No duplicates allowed
 Unordered collection
 Useful for uniqueness checks
Queue: A collection designed to hold elements before processing. It typically follows the FIFO
(First-In-First-Out) principle. Examples: LinkedList, PriorityQueue.
 Methods: offer(), poll(), peek()

Map: A collection of key-value pairs, where each key is unique. Examples: HashMap,TreeMap.
 Key-value pairs
 Unique keys
 Methods: put(), get(), remove()

a) ArrayList:
ArrayList<String> list = new ArrayList<>();
list.add("Apple");
list.add("Banana");
list.get(0); // Returns "Apple"

Key Characteristics:

Dynamic array implementation


Fast random access
Slow insertions/deletions in middle
Best for: Random access and iteration

b) LinkedList:

LinkedList<Integer> numbers = new LinkedList<>();


numbers.addFirst(1);
numbers.addLast(3);
Key Characteristics:

Double linked list implementation


Fast insertions/deletions
Slow random access
Best for: Frequent insertions/deletions

c) HashSet:

HashSet<String> set = new HashSet<>();


set.add("One");
set.add("One"); // Duplicate not added

Key Characteristics:

Hash table implementation


Very fast access/search
Unordered
Best for: Uniqueness checking

d) HashMap:

HashMap<String, Integer> map = new HashMap<>();


// key-value pair
map.put("One", 1);
map.put("Two", 2);
map.get("One"); // Returns 1

Key Characteristics:

Not ordered:
Thread-unsafe

e) TreeSet:

Set<String> ts = new TreeSet<>();


// Elements are added using add() method
ts.add("I");
ts.add("Love");
ts.add("India");
System.out.println(ts); // returns [I, Love, India]
Key Characteristics:

 Ordered
 Slower than HashSet
 Best for: Sorted unique elements

Common Operations:

a) Iteration:
// Using for-each
for (String item : list) {
System.out.println(item);
}

// Using Iterator
Iterator<String> it = list.iterator();
while (it.hasNext()) {
System.out.println(it.next());
}

b) Sorting:
// For Lists
Collections.sort(list);

Utility Methods (Collections class):

Collections.sort(list); // Sort
Collections.shuffle(list); // Random order
Collections.reverse(list); // Reverse order
Collections.binarySearch(list, key); // Binary search
Collections.max(list); // Find maximum
Collections.min(list); // Find minimum
Collections.frequency(list, element); // Count occurrences

Performance Considerations:

ArrayList: O(1) for access, O(n) for insertions/deletions


LinkedList: O(1) for insertions/deletions, O(n) for access
HashSet/HashMap: O(1) average case for all operations
TreeSet/TreeMap: O(log n) for most operations
Program

package Collections;

import java.util.*;

// program for demonstrating List Interface


public class ListInterfaceDemo {
public static void main(String[] args) {

// Creating different types of Lists


List<String> arrayList = new ArrayList<>();
List<String> linkedList = new LinkedList<>();

System.out.println("=== ArrayList Demonstrations ===");

// 1. Adding elements
arrayList.add("Apple");
arrayList.add("Banana");
arrayList.add("Orange");
System.out.println("After adding elements: " + arrayList);

// 2. Adding element at specific index


arrayList.add(1, "Mango");
System.out.println("After adding Mango at index 1: " + arrayList);

// 3. Adding multiple elements


arrayList.addAll(Arrays.asList("Grape", "Pineapple"));
System.out.println("After adding multiple elements: " + arrayList);

// 4. Accessing elements
String firstFruit = arrayList.get(0);
System.out.println("First fruit: " + firstFruit);

// 5. Updating elements
arrayList.set(1, "Kiwi");
System.out.println("After updating index 1 to Kiwi: " + arrayList);

// 6. Removing elements
arrayList.remove("Orange"); // Remove by object
arrayList.remove(0); // Remove by index
System.out.println("After removing elements: " + arrayList);

// 7. Searching elements
boolean containsKiwi = arrayList.contains("Kiwi");
int grapeIndex = arrayList.indexOf("Grape");
System.out.println("Contains Kiwi? " + containsKiwi);
System.out.println("Index of Grape: " + grapeIndex);
// 8. List size
System.out.println("List size: " + arrayList.size());

// 9. Iterating over list


System.out.println("\nIterating using for-each loop:");
for (String fruit : arrayList) {
System.out.println(fruit);
}

// 10. Using Iterator


System.out.println("\nIterating using Iterator:");
Iterator<String> iterator = arrayList.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}

// 11. Sorting
Collections.sort(arrayList);
System.out.println("After sorting: " + arrayList);

// 12. Sublist
List<String> subList = arrayList.subList(0, 2);
System.out.println("Sublist (0-2): " + subList);

// 13. Clearing the list


arrayList.clear();
System.out.println("After clearing: " + arrayList);

// LinkedList specific operations


System.out.println("\n=== LinkedList Demonstrations ===");
LinkedList<String> fruits = new LinkedList<>();
fruits.addFirst("Apple");
fruits.addLast("Banana");
fruits.add("Orange");

System.out.println("LinkedList: " + fruits);


System.out.println("First element: " + fruits.getFirst());
System.out.println("Last element: " + fruits.getLast());

fruits.removeFirst();
fruits.removeLast();
System.out.println("After removing first and last: " + fruits);
}
}

o/p
=== ArrayList Demonstrations ===
After adding elements: [Apple, Banana, Orange]
After adding Mango at index 1: [Apple, Mango, Banana, Orange]
After adding multiple elements: [Apple, Mango, Banana, Orange, Grape, Pineapple]
First fruit: Apple
After updating index 1 to Kiwi: [Apple, Kiwi, Banana, Orange, Grape, Pineapple]
After removing elements: [Kiwi, Banana, Grape, Pineapple]
Contains Kiwi? true
Index of Grape: 2
List size: 4

Iterating using for-each loop:


Kiwi
Banana
Grape
Pineapple

Iterating using Iterator:


Kiwi
Banana
Grape
Pineapple
After sorting: [Banana, Grape, Kiwi, Pineapple]
Sublist (0-2): [Banana, Grape]
After clearing: []

=== LinkedList Demonstrations ===


LinkedList: [Apple, Banana, Orange]
First element: Apple
Last element: Orange
After removing first and last: [Banana]

Set interface
This program demonstrates the key features of the Set interface in Java. Here are the main concepts
covered:
1. Different Set Implementations:
 HashSet (unordered, no duplicates)
 TreeSet (sorted naturally)
 LinkedHashSet (maintains insertion order)
2. Basic Operations:
 Adding elements (add)
 Removing elements (remove)
 Checking existence (contains)
 Clearing the set (clear)
3. Set Properties:
 No duplicate elements
 Different ordering behaviors based on implementation
 Size and empty status checking
4. Set Operations:
 Union (addAll)
 Intersection (retainAll)
 Difference (removeAll)
5. Iteration Methods:
 Using for-each loop
 Using Iterator interface
6. Bulk Operations:
 Adding multiple elements
 Converting to array
 Working with collections

Program

package Collections;

import java.util.*;

public class SetInterfaceDemo {


public static void main(String[] args) {
// HashSet Demo
System.out.println("=== HashSet Demonstration ===");
Set<String> hashSet = new HashSet<>();

// 1. Adding elements
System.out.println("\nAdding elements to HashSet:");
hashSet.add("Apple");
hashSet.add("Banana");
hashSet.add("Orange");
hashSet.add("Apple"); // Duplicate - will not be added
System.out.println("HashSet after adding elements: " + hashSet);

// 2. Checking size and empty status


System.out.println("Size of HashSet: " + hashSet.size());
System.out.println("Is HashSet empty? " + hashSet.isEmpty());

// 3. Checking element existence


System.out.println("Contains 'Apple'? " + hashSet.contains("Apple"));
System.out.println("Contains 'Grape'? " + hashSet.contains("Grape"));

// 4. Removing elements
hashSet.remove("Banana");
System.out.println("After removing 'Banana': " + hashSet);

// TreeSet Demo (Sorted Set)


System.out.println("\n=== TreeSet Demonstration ===");
Set<String> treeSet = new TreeSet<>();

// 5. Adding elements to TreeSet


treeSet.add("Zebra");
treeSet.add("Lion");
treeSet.add("Tiger");
treeSet.add("Elephant");
System.out.println("TreeSet (automatically sorted): " + treeSet);

// 6. LinkedHashSet Demo (Maintains insertion order)


System.out.println("\n=== LinkedHashSet Demonstration ===");
Set<String> linkedHashSet = new LinkedHashSet<>();
linkedHashSet.add("Red");
linkedHashSet.add("Green");
linkedHashSet.add("Blue");
System.out.println("LinkedHashSet (maintains insertion order): " + linkedHashSet);

// 7. Set Operations
System.out.println("\n=== Set Operations Demonstration ===");
Set<Integer> set1 = new HashSet<>(Arrays.asList(1, 2, 3, 4, 5));
Set<Integer> set2 = new HashSet<>(Arrays.asList(4, 5, 6, 7, 8));

// Union
Set<Integer> union = new HashSet<>(set1);
union.addAll(set2);
System.out.println("Union of sets: " + union);

// Intersection
Set<Integer> intersection = new HashSet<>(set1);
intersection.retainAll(set2);
System.out.println("Intersection of sets: " + intersection);

// Difference
Set<Integer> difference = new HashSet<>(set1);
difference.removeAll(set2);
System.out.println("Difference of sets (set1 - set2): " + difference);

// 8. Iteration Methods
System.out.println("\n=== Iteration Demonstration ===");
System.out.println("Using for-each loop:");
for (String element : hashSet) {
System.out.println(element);
}
System.out.println("\nUsing Iterator:");
Iterator<String> iterator = hashSet.iterator();
while (iterator.hasNext()) {
System.out.println(iterator.next());
}

// 9. Bulk Operations
Set<String> set3 = new HashSet<>();
set3.addAll(Arrays.asList("Apple", "Orange", "Grape"));
System.out.println("\nBulk addition: " + set3);

// 10. Converting Set to Array


String[] array = hashSet.toArray(new String[0]);
System.out.println("Set converted to array: " + Arrays.toString(array));

// 11. Clearing the set


hashSet.clear();
System.out.println("\nAfter clearing HashSet: " + hashSet);
}
}

o/p

=== HashSet Demonstration ===

Adding elements to HashSet:


HashSet after adding elements: [Apple, Orange, Banana]
Size of HashSet: 3
Is HashSet empty? false
Contains 'Apple'? true
Contains 'Grape'? false
After removing 'Banana': [Apple, Orange]

=== TreeSet Demonstration ===


TreeSet (automatically sorted): [Elephant, Lion, Tiger, Zebra]

=== LinkedHashSet Demonstration ===


LinkedHashSet (maintains insertion order): [Red, Green, Blue]

=== Set Operations Demonstration ===


Union of sets: [1, 2, 3, 4, 5, 6, 7, 8]
Intersection of sets: [4, 5]
Difference of sets (set1 - set2): [1, 2, 3]

=== Iteration Demonstration ===


Using for-each loop:
Apple
Orange
Using Iterator:
Apple
Orange

Bulk addition: [Apple, Grape, Orange]


Set converted to array: [Apple, Orange]

After clearing HashSet: []

Queue interface
This program demonstrates the key features of the Queue interface in Java. Here are the main concepts
covered:
1. Basic Queue Operations (FIFO):
 offer() - adds element
 poll() - removes and returns head
 peek() - views head without removal
2. ArrayDeque Features:
 Supports both stack and queue operations
 Can add/remove from both ends
 More efficient than LinkedList for most operations
3. PriorityQueue:
 Natural ordering
 Custom ordering with Comparator
 Maintains elements in priority order
4. Exception Handling:
 Demonstrates difference between safe operations (offer/poll/peek) and throwing
operations (add/remove/element)
5. Additional Features:
 Bulk operations
 Array conversion
 Size checking
 Clear operation

Program

package Collections;

import java.util.*;

public class QueueInterfaceDemo {


public static void main(String[] args) {
// LinkedList as Queue demonstration
System.out.println("=== LinkedList as Queue Demonstration ===");
Queue<String> queue = new LinkedList<>();

// 1. Basic Queue Operations


System.out.println("\nBasic Queue Operations (FIFO):");
// Adding elements
queue.offer("First");
queue.offer("Second");
queue.offer("Third");
System.out.println("Queue after adding elements: " + queue);

// Examining the head of queue


String head = queue.peek();
System.out.println("Head of queue (peek): " + head);

// Removing elements
String removed = queue.poll();
System.out.println("Removed element (poll): " + removed);
System.out.println("Queue after removal: " + queue);

// 2. ArrayDeque demonstration
System.out.println("\n=== ArrayDeque Demonstration ===");
ArrayDeque<String> arrayDeque = new ArrayDeque<>();

// Adding elements
arrayDeque.offer("First");
arrayDeque.offer("Second");
arrayDeque.offerLast("Last");
arrayDeque.offerFirst("New First");
System.out.println("ArrayDeque: " + arrayDeque);

// Examining elements
System.out.println("First element: " + arrayDeque.peekFirst());
System.out.println("Last element: " + arrayDeque.peekLast());

// Removing elements
System.out.println("Removed first: " + arrayDeque.pollFirst());
System.out.println("Removed last: " + arrayDeque.pollLast());
System.out.println("ArrayDeque after removal: " + arrayDeque);

// 3. PriorityQueue demonstration
System.out.println("\n=== PriorityQueue Demonstration ===");
// Natural ordering
PriorityQueue<Integer> priorityQueue = new PriorityQueue<>();

// Adding elements
priorityQueue.offer(5);
priorityQueue.offer(1);
priorityQueue.offer(3);
priorityQueue.offer(2);
priorityQueue.offer(4);

System.out.println("PriorityQueue: " + priorityQueue);


System.out.println("\nRemoving elements from PriorityQueue (notice the order):");
while (!priorityQueue.isEmpty()) {
System.out.println("Polling: " + priorityQueue.poll());
}

// 4. Custom Priority Queue with Comparator


System.out.println("\n=== Custom PriorityQueue with Comparator ===");
PriorityQueue<String> customPriorityQueue = new PriorityQueue<>(
(s1, s2) -> s2.length() - s1.length() // Longer strings get higher priority
);

customPriorityQueue.offer("A");
customPriorityQueue.offer("BBB");
customPriorityQueue.offer("CC");
customPriorityQueue.offer("DDDD");

System.out.println("Custom PriorityQueue (ordered by string length):");


while (!customPriorityQueue.isEmpty()) {
System.out.println("Polling: " + customPriorityQueue.poll());
}

// 5. Queue operations with exception handling


System.out.println("\n=== Queue Operations with Exception Handling ===");
Queue<String> exceptionQueue = new LinkedList<>();

try {
// element() throws exception if queue is empty
System.out.println("Trying to get element from empty queue:");
exceptionQueue.element();
} catch (NoSuchElementException e) {
System.out.println("Exception caught: Queue is empty");
}

// 6. Bulk Operations
System.out.println("\n=== Bulk Operations ===");
Queue<String> bulkQueue = new LinkedList<>();
bulkQueue.addAll(Arrays.asList("One", "Two", "Three"));
System.out.println("After bulk addition: " + bulkQueue);

// 7. Queue to Array conversion


Object[] array = bulkQueue.toArray();
System.out.println("Queue converted to array: " + Arrays.toString(array));

// 8. Demonstrating size and clear operations


System.out.println("\n=== Size and Clear Operations ===");
System.out.println("Queue size: " + bulkQueue.size());
bulkQueue.clear();
System.out.println("After clearing queue, size: " + bulkQueue.size());
System.out.println("Is queue empty? " + bulkQueue.isEmpty());
}
}

o/p

=== LinkedList as Queue Demonstration ===

Basic Queue Operations (FIFO):


Queue after adding elements: [First, Second, Third]
Head of queue (peek): First
Removed element (poll): First
Queue after removal: [Second, Third]

=== ArrayDeque Demonstration ===


ArrayDeque: [New First, First, Second, Last]
First element: New First
Last element: Last
Removed first: New First
Removed last: Last
ArrayDeque after removal: [First, Second]

=== PriorityQueue Demonstration ===


PriorityQueue: [1, 2, 3, 5, 4]

Removing elements from PriorityQueue (notice the order):


Polling: 1
Polling: 2
Polling: 3
Polling: 4
Polling: 5

=== Custom PriorityQueue with Comparator ===


Custom PriorityQueue (ordered by string length):
Polling: DDDD
Polling: BBB
Polling: CC
Polling: A

=== Queue Operations with Exception Handling ===


Trying to get element from empty queue:
Exception caught: Queue is empty

=== Bulk Operations ===


After bulk addition: [One, Two, Three]
Queue converted to array: [One, Two, Three]

=== Size and Clear Operations ===


Queue size: 3
After clearing queue, size: 0
Is queue empty? true

Map Interface

This program demonstrates the key features of the Map interface in Java. Here are the main concepts
covered:
1. Different Map Implementations:
 HashMap (unordered)
 TreeMap (sorted by keys)
 LinkedHashMap (maintains insertion order)
2. Basic Operations:
 put() and putIfAbsent()
 get() and getOrDefault()
 remove() with key and key-value pair
 clear()
3. Advanced Features:
 Entry set iteration
 Key set and values collection
 Compute operations
 Merge operations
4. Map Properties:
 Size checking
 Empty checking
 Key/Value existence checking
5. Special Features:
 LinkedHashMap with access order
 TreeMap's natural ordering
 Bulk operations
 Custom merge strategies
Program

package Collections;

import java.util.*;

public class MapInterfaceDemo {


public static void main(String[] args) {
// HashMap Demonstration
System.out.println("=== HashMap Demonstration ===");
Map<String, Integer> hashMap = new HashMap<>();

// 1. Basic Operations
// Adding elements
hashMap.put("Apple", 1);
hashMap.put("Banana", 2);
hashMap.put("Orange", 3);
System.out.println("Initial HashMap: " + hashMap);

// Updating value
hashMap.put("Apple", 5); // Updates existing value
System.out.println("After updating Apple's value: " + hashMap);

// putIfAbsent
hashMap.putIfAbsent("Apple", 10); // Won't update as key exists
hashMap.putIfAbsent("Grape", 4); // Will add new entry
System.out.println("After putIfAbsent operations: " + hashMap);

// 2. Accessing Elements
System.out.println("\nAccessing Elements:");
System.out.println("Value for Apple: " + hashMap.get("Apple"));
System.out.println("Value for missing key: " + hashMap.get("Mango"));
System.out.println("Value for missing key with default: " + hashMap.getOrDefault("Mango", 0));

// 3. Removing Elements
hashMap.remove("Banana");
System.out.println("After removing Banana: " + hashMap);

// Conditional remove
hashMap.remove("Apple", 5); // Removes only if value matches
System.out.println("After conditional remove: " + hashMap);

// 4. TreeMap Demonstration (Sorted Map)


System.out.println("\n=== TreeMap Demonstration ===");

TreeMap<String, Integer> scores = new TreeMap<>();

// 5 Adding elements (put operation)


scores.put("Alice", 95);
scores.put("Bob", 82);
scores.put("Charlie", 90);
scores.put("David", 78);
scores.put("Eva", 88);

// Display the TreeMap (naturally sorted by keys)


System.out.println("TreeMap contents: " + scores);
// 6 Accessing elements (get operation)
System.out.println("Charlie's score: " + scores.get("Charlie"));

// 7. removing elements
scores.remove("David");
System.out.println("After removing David: " + scores);

// 8 Navigation operations (TreeMap-specific)


// First (lowest) and last (highest) entries
System.out.println("First entry: " + scores.firstEntry());
System.out.println("Last entry: " + scores.lastEntry());

// 8 naturally ordered by keys

Map<String, Integer> treeMap = new TreeMap<>();


treeMap.put("Zebra", 1);
treeMap.put("Lion", 2);
treeMap.put("Elephant", 3);
System.out.println("TreeMap (naturally ordered by keys): " + treeMap);

// 9. eldest entry if size exceeds 3

System.out.println("\n=== LinkedHashMap Demonstration ===");


Map<String, Integer> linkedHashMap = new LinkedHashMap<>() {
@Override
protected boolean removeEldestEntry(Map.Entry<String, Integer> eldest) {
return size() > 3; // Remove eldest entry if size exceeds 3
}
};

linkedHashMap.put("A", 1);
linkedHashMap.put("B", 2);
linkedHashMap.put("C", 3);
System.out.println("Initial LinkedHashMap: " + linkedHashMap);
linkedHashMap.put("D", 4); // Will remove eldest entry
System.out.println("After adding D (notice removal of eldest): " + linkedHashMap);

// 10. Iterating Over Map


System.out.println("\n=== Map Iteration ===");

// Iterating over entries


System.out.println("Iterating over entries:");
for (Map.Entry<String, Integer> entry : hashMap.entrySet()) {
System.out.println(entry.getKey() + " -> " + entry.getValue());
}

// Iterating over keys


System.out.println("\nIterating over keys:");
for (String key : hashMap.keySet()) {
System.out.println("Key: " + key);
}

// Iterating over values


System.out.println("\nIterating over values:");
for (Integer value : hashMap.values()) {
System.out.println("Value: " + value);
}

// 11. Map Operations


System.out.println("\n=== Map Operations ===");
Map<String, Integer> map1 = new HashMap<>();
map1.put("A", 1);
map1.put("B", 2);

// Merging maps
Map<String, Integer> map2 = new HashMap<>();
map2.put("B", 3);
map2.put("C", 4);

// Merge with custom remapping function


map2.forEach((key, value) ->
map1.merge(key, value, (v1, v2) -> v1 + v2));
System.out.println("After merging maps: " + map1);

// 12. Compute Operations


System.out.println("\n=== Compute Operations ===");
map1.compute("A", (k, v) -> (v == null) ? 1 : v * 2);
System.out.println("After computing A: " + map1);

map1.computeIfPresent("B", (k, v) -> v * 3);


System.out.println("After computeIfPresent B: " + map1);

map1.computeIfAbsent("D", k -> 10);


System.out.println("After computeIfAbsent D: " + map1);

// 13. Bulk Operations


System.out.println("\n=== Bulk Operations ===");
Map<String, Integer> newMap = new HashMap<>();
newMap.putAll(map1);
System.out.println("After putAll: " + newMap);

// Clear the map


newMap.clear();
System.out.println("After clearing: " + newMap);

// 14. Checking Operations


System.out.println("\n=== Checking Operations ===");
System.out.println("Is map empty? " + newMap.isEmpty());
System.out.println("Map size: " + map1.size());
System.out.println("Contains key 'A'? " + map1.containsKey("A"));
System.out.println("Contains value 1? " + map1.containsValue(1));
}
}
o/p

=== HashMap Demonstration ===


Initial HashMap: {Apple=1, Orange=3, Banana=2}
After updating Apple's value: {Apple=5, Orange=3, Banana=2}
After putIfAbsent operations: {Apple=5, Grape=4, Orange=3, Banana=2}

Accessing Elements:
Value for Apple: 5
Value for missing key: null
Value for missing key with default: 0
After removing Banana: {Apple=5, Grape=4, Orange=3}
After conditional remove: {Grape=4, Orange=3}

=== TreeMap Demonstration ===


TreeMap contents: {Alice=95, Bob=82, Charlie=90, David=78, Eva=88}
Charlie's score: 90
After removing David: {Alice=95, Bob=82, Charlie=90, Eva=88}
First entry: Alice=95
Last entry: Eva=88
TreeMap (naturally ordered by keys): {Elephant=3, Lion=2, Zebra=1}

=== LinkedHashMap Demonstration ===


Initial LinkedHashMap: {A=1, B=2, C=3}
After adding D (notice removal of eldest): {B=2, C=3, D=4}

=== Map Iteration ===


Iterating over entries:
Grape -> 4
Orange -> 3

Iterating over keys:


Key: Grape
Key: Orange

Iterating over values:


Value: 4
Value: 3

=== Map Operations ===


After merging maps: {A=1, B=5, C=4}

=== Compute Operations ===


After computing A: {A=2, B=5, C=4}
After computeIfPresent B: {A=2, B=15, C=4}
After computeIfAbsent D: {A=2, B=15, C=4, D=10}

=== Bulk Operations ===


After putAll: {A=2, B=15, C=4, D=10}
After clearing: {}

=== Checking Operations ===


Is map empty? true
Map size: 4
Contains key 'A'? true
Contains value 1? false

Various Collection classes and their features

This comprehensive program demonstrates various Collection classes and their features. Here's a
breakdown of the key concepts covered:

List Interface Implementations:

ArrayList: Dynamic array implementation


LinkedList: Doubly-linked list implementation
Operations like sorting, adding, removing elements

Set Interface Implementations:

HashSet: Hash table implementation, no duplicates


TreeSet: Sorted tree implementation
Custom object storage with proper equals() and hashCode()

Queue Implementation:

PriorityQueue with custom comparator


Poll operations showing priority ordering

Map Implementation:

HashMap with custom objects


Key-value pair operations

Collections Utility Methods:

Sorting
Binary search
Min/max operations
Frequency counting
Shuffling

Special Collection Types:

Unmodifiable collections
Synchronized collections

Custom Class Implementation:

Comparable interface
equals() and hashCode()
Custom comparators

Differences between Iterator and for-each loop


This program demonstrates the key differences between Iterator and for-each loop. Here are the main
points illustrated:
1. Iterator Advantages:
 Supports element removal during iteration (iterator.remove())
 Allows element modification (using ListIterator)
 Provides bidirectional traversal (using ListIterator)
 Offers more control over the iteration process
2. For-each Loop Advantages:
 More concise and readable syntax
 Less error-prone for simple iterations
 Generally preferred for read-only operations
 Cleaner syntax for nested iterations
3. Key Limitations:
 For-each loop cannot modify the collection during iteration
 For-each loop doesn't provide index information
 For-each loop doesn't support backward iteration
4. Special Features:
 ListIterator's bidirectional capabilities
 Safe modification methods with Iterator
 Performance comparison
 Nested iteration patterns
The program shows how:
1. Iterator is better when you need to:
 Modify the collection while iterating
 Need bidirectional traversal
 Require more control over the iteration process
2. For-each loop is better when you:
 Just need to read elements
 Want cleaner, more readable code
 Don't need to modify the collection
 Are working with nested collections

Example program

package Collections;

import java.util.*;

class IterationComparisionDemo {
public static void main(String[] args) {
// Creating a list of numbers for demonstration
List<Integer> numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));

System.out.println("Original list: " + numbers);

// 1. Iterator with removal


System.out.println("\n=== Iterator with element removal ===");
Iterator<Integer> iterator = numbers.iterator();
System.out.println("Removing even numbers using Iterator:");
while (iterator.hasNext()) {
int num = iterator.next();
if (num % 2 == 0) {
iterator.remove(); // Safe removal during iteration
System.out.println("Removed: " + num);
}
}
System.out.println("List after removing even numbers: " + numbers);

// Reset list
numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));

// 2. For-each loop attempt to remove (will throw exception)


System.out.println("\n=== For-each loop with attempted removal ===");
try {
System.out.println("Attempting to remove elements using for-each loop:");
for (int num : numbers) {
if (num % 2 == 0) {
numbers.remove(Integer.valueOf(num)); // Will throw ConcurrentModificationException
}
}
} catch (ConcurrentModificationException e) {
System.out.println("ConcurrentModificationException caught: Cannot modify list during for-
each loop");
}

// Reset list
numbers = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5, 6, 7, 8, 9, 10));

// 3. Iterator with modification


System.out.println("\n=== Iterator with ListIterator modification ===");
ListIterator<Integer> listIterator = numbers.listIterator();
System.out.println("Multiplying odd numbers by 10 using ListIterator:");
while (listIterator.hasNext()) {
int num = listIterator.next();
if (num % 2 != 0) {
listIterator.set(num * 10); // Modifying elements during iteration
System.out.println("Modified: " + num + " to " + (num * 10));
}
}
System.out.println("List after modification: " + numbers);

// 4. For-each loop for simple iteration


System.out.println("\n=== For-each loop for simple iteration ===");
System.out.println("Simply printing elements using for-each loop:");
for (int num : numbers) {
System.out.print(num + " ");
}
System.out.println();

// 5. Demonstrating Iterator's directional capabilities


System.out.println("\n=== ListIterator's bidirectional capabilities ===");
ListIterator<Integer> biDirIterator = numbers.listIterator(numbers.size());
System.out.println("Backwards iteration using ListIterator:");
while (biDirIterator.hasPrevious()) {
System.out.print(biDirIterator.previous() + " ");
}
System.out.println();

// 6. Performance comparison
System.out.println("\n=== Performance Comparison ===");
List<Integer> largeList = new ArrayList<>();
for (int i = 0; i < 1000000; i++) {
largeList.add(i);
}

// Time for Iterator


long startTime = System.nanoTime();
Iterator<Integer> perfIterator = largeList.iterator();
while (perfIterator.hasNext()) {
perfIterator.next();
}
long iteratorTime = System.nanoTime() - startTime;

// Time for for-each


startTime = System.nanoTime();
for (int num : largeList) {
// do nothing
}
long forEachTime = System.nanoTime() - startTime;

System.out.println("Iterator time (ns): " + iteratorTime);


System.out.println("For-each time (ns): " + forEachTime);

// 7. Nested iteration example


System.out.println("\n=== Nested Iteration Example ===");
List<List<Integer>> nestedList = Arrays.asList(
Arrays.asList(1, 2, 3),
Arrays.asList(4, 5, 6),
Arrays.asList(7, 8, 9)
);

System.out.println("Using nested for-each loops (more readable):");


for (List<Integer> subList : nestedList) {
for (int num : subList) {
System.out.print(num + " ");
}
System.out.println();
}

System.out.println("\nUsing nested iterators (more control):");


Iterator<List<Integer>> outerIterator = nestedList.iterator();
while (outerIterator.hasNext()) {
Iterator<Integer> innerIterator = outerIterator.next().iterator();
while (innerIterator.hasNext()) {
System.out.print(innerIterator.next() + " ");
}
System.out.println();
}
}
}

o/p

Original list: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

=== Iterator with element removal ===


Removing even numbers using Iterator:
Removed: 2
Removed: 4
Removed: 6
Removed: 8
Removed: 10
List after removing even numbers: [1, 3, 5, 7, 9]

=== For-each loop with attempted removal ===


Attempting to remove elements using for-each loop:
ConcurrentModificationException caught: Cannot modify list during for-each loop

=== Iterator with ListIterator modification ===


Multiplying odd numbers by 10 using ListIterator:
Modified: 1 to 10
Modified: 3 to 30
Modified: 5 to 50
Modified: 7 to 70
Modified: 9 to 90
List after modification: [10, 30, 4, 50, 6, 70, 8, 90, 10]

=== For-each loop for simple iteration ===


Simply printing elements using for-each loop:
10 30 4 50 6 70 8 90 10

=== ListIterator's bidirectional capabilities ===


Backwards iteration using ListIterator:
10 90 8 70 6 50 4 30 10

=== Performance Comparison ===


Iterator time (ns): 20632355
For-each time (ns): 22921813

=== Nested Iteration Example ===


Using nested for-each loops (more readable):
123
456
789

Using nested iterators (more control):


123
456
789

User-defined classes in collections

This program demonstrates the essential concepts for storing user-defined classes in collections. Here
are the key points:
Essential Method Overrides:

equals(): For proper object comparison


hashCode(): For hash-based collections
compareTo(): For natural ordering
toString(): For readable output

Collection Types Demonstrated:

ArrayList: Simple list storage


HashSet: Duplicate elimination
TreeSet: Sorted storage (natural and custom)
HashMap: Key-value storage
PriorityQueue: Priority-based storage

Important Features:

Duplicate handling
Custom sorting
Grouping objects
Using custom comparators

Best Practices:

Implementing Comparable interface


Proper equals and hashCode implementation
Using appropriate collection types
Using comparators for flexible sorting

Example Program for User-defined classes in collections

The code shows how to:

 Properly implement required methods for collection storage


 Handle duplicates in different collection types
 Create custom sorting orders
 Group and organize objects
 Use different collection types effectively

package Collections;

import java.util.*;

//Custom Employee class


class Employee implements Comparable<Employee> {
private int id;
private String name;
private double salary;
private String department;

public Employee(int id, String name, double salary, String department) {


this.id = id;
this.name = name;
this.salary = salary;
this.department = department;
}

// Getters
public int getId() { return id; }
public String getName() { return name; }
public double getSalary() { return salary; }
public String getDepartment() { return department; }

// Override equals method for proper object comparison


@Override
public boolean equals(Object o) {
if (this == o) return true;

Employee employee = (Employee) o;


return id == employee.id &&
Objects.equals(name, employee.name) &&
Objects.equals(department, employee.department);
}

// Override hashCode for proper hash-based collections (HashSet, HashMap)


@Override
public int hashCode() {
return Objects.hash(id, name, department);
}

// Implement compareTo for natural ordering (used by TreeSet, TreeMap)


@Override
public int compareTo(Employee other) {
return Integer.compare(this.id, other.id);
}

@Override
public String toString() {
return String.format("Employee{id=%d, name='%s', salary=%.2f, department='%s'}",
id, name, salary, department);
}
}
public class UserDefinedCollectionsDemo {
public static void main(String[] args) {
// Create some employee objects
Employee emp1 = new Employee(101, "John Doe", 50000, "IT");
Employee emp2 = new Employee(102, "Jane Smith", 60000, "HR");
Employee emp3 = new Employee(103, "Bob Wilson", 55000, "IT");
Employee emp4 = new Employee(101, "John Doe", 50000, "IT"); // Duplicate of emp1
Employee emp5 = new Employee(104, "Alice Brown", 65000, "Finance");

// 1. ArrayList Demo
System.out.println("=== ArrayList Demonstration ===");
List<Employee> employeeList = new ArrayList<>();
employeeList.add(emp1);
employeeList.add(emp2);
employeeList.add(emp3);

System.out.println("Employees in ArrayList:");
employeeList.forEach(System.out::println);

// 2. HashSet Demo (eliminates duplicates based on equals() and hashCode())


System.out.println("\n=== HashSet Demonstration ===");
Set<Employee> employeeSet = new HashSet<>();
employeeSet.add(emp1);
employeeSet.add(emp2);
employeeSet.add(emp3);
employeeSet.add(emp4); // Should not add (duplicate of emp1)

System.out.println("Employees in HashSet (no duplicates):");


employeeSet.forEach(System.out::println);

// 3. TreeSet Demo (sorted based on compareTo())


System.out.println("\n=== TreeSet Demonstration ===");
Set<Employee> employeeTreeSet = new TreeSet<>();
employeeTreeSet.addAll(employeeSet);
employeeTreeSet.add(emp5);

System.out.println("Employees in TreeSet (sorted by ID):");


employeeTreeSet.forEach(System.out::println);

// 4. TreeSet with custom Comparator


System.out.println("\n=== TreeSet with Custom Comparator ===");
Set<Employee> salaryTreeSet = new TreeSet<>(
Comparator.comparingDouble(Employee::getSalary).reversed()
);
salaryTreeSet.addAll(employeeSet);
salaryTreeSet.add(emp5);

System.out.println("Employees in TreeSet (sorted by salary descending):");


salaryTreeSet.forEach(System.out::println);
// 5. HashMap Demo
System.out.println("\n=== HashMap Demonstration ===");
Map<Integer, Employee> employeeMap = new HashMap<>();
employeeMap.put(emp1.getId(), emp1);
employeeMap.put(emp2.getId(), emp2);
employeeMap.put(emp3.getId(), emp3);
employeeMap.put(emp4.getId(), emp4); // Should override emp1

System.out.println("Employees in HashMap:");
employeeMap.forEach((id, emp) ->
System.out.println("Key: " + id + ", Value: " + emp));

// 6. PriorityQueue Demo
System.out.println("\n=== PriorityQueue Demonstration ===");
Queue<Employee> employeePQ = new PriorityQueue<>(
Comparator.comparingDouble(Employee::getSalary)
);
employeePQ.addAll(employeeSet);
employeePQ.add(emp5);

System.out.println("Employees in PriorityQueue (by salary):");


while (!employeePQ.isEmpty()) {
System.out.println(employeePQ.poll());
}
}
}

o/p
=== ArrayList Demonstration ===
Employees in ArrayList:
Employee{id=101, name='John Doe', salary=50000.00, department='IT'}
Employee{id=102, name='Jane Smith', salary=60000.00, department='HR'}
Employee{id=103, name='Bob Wilson', salary=55000.00, department='IT'}

=== HashSet Demonstration ===


Employees in HashSet (no duplicates):
Employee{id=102, name='Jane Smith', salary=60000.00, department='HR'}
Employee{id=103, name='Bob Wilson', salary=55000.00, department='IT'}
Employee{id=101, name='John Doe', salary=50000.00, department='IT'}

=== TreeSet Demonstration ===


Employees in TreeSet (sorted by ID):
Employee{id=101, name='John Doe', salary=50000.00, department='IT'}
Employee{id=102, name='Jane Smith', salary=60000.00, department='HR'}
Employee{id=103, name='Bob Wilson', salary=55000.00, department='IT'}
Employee{id=104, name='Alice Brown', salary=65000.00, department='Finance'}
=== TreeSet with Custom Comparator ===
Employees in TreeSet (sorted by salary descending):
Employee{id=104, name='Alice Brown', salary=65000.00, department='Finance'}
Employee{id=102, name='Jane Smith', salary=60000.00, department='HR'}
Employee{id=103, name='Bob Wilson', salary=55000.00, department='IT'}
Employee{id=101, name='John Doe', salary=50000.00, department='IT'}

=== HashMap Demonstration ===


Employees in HashMap:
Key: 101, Value: Employee{id=101, name='John Doe', salary=50000.00, department='IT'}
Key: 102, Value: Employee{id=102, name='Jane Smith', salary=60000.00, department='HR'}
Key: 103, Value: Employee{id=103, name='Bob Wilson', salary=55000.00, department='IT'}

=== PriorityQueue Demonstration ===


Employees in PriorityQueue (by salary):
Employee{id=101, name='John Doe', salary=50000.00, department='IT'}
Employee{id=103, name='Bob Wilson', salary=55000.00, department='IT'}
Employee{id=102, name='Jane Smith', salary=60000.00, department='HR'}
Employee{id=104, name='Alice Brown', salary=65000.00, department='Finance'}

Comparators

Comparators in Java are used to define custom sorting logic for objects. The Comparator interface has a
single method compare(T o1, T o2) that needs to be implemented.
The compare() method returns:

A negative value if o1 < o2


Zero if o1 equals o2
A positive value if o1 > o2

In the example, I've shown three different ways to create comparators:

Using an anonymous class (age comparator)


Using a lambda expression (GPA comparator)
Using the Comparator.comparing() utility method (name comparator)

The example also demonstrates comparator chaining using thenComparing(), which allows you to
specify a secondary sorting criterion when the primary criterion results in a tie.

When you run this program, it will show the students sorted in different ways:

By age (ascending)
By GPA (descending)
By name (alphabetically)
By age and then GPA (combined sorting)

Some important points about Comparators:

They provide more flexibility than implementing Comparable interface, as you can have multiple
sorting criteria
They can be passed to collection sorting methods like Collections.sort() or List.sort()
They can be stored in variables and reused

Program for student sorting using comparators

package Comparators;

import java.util.*;

//Student class to demonstrate sorting using comparators


class Student {
private String name;
private int age;
private double gpa;

public Student(String name, int age, double gpa) {


this.name = name;
this.age = age;
this.gpa = gpa;
}

// Getters
public String getName() { return name; }
public int getAge() { return age; }
public double getGpa() { return gpa; }

@Override
public String toString() {
return "Student{name='" + name + "', age=" + age + ", gpa=" + gpa + "}";
}
}

//Main class demonstrating different comparators


public class StudentSorting {
public static void main(String[] args) {
List<Student> students = new ArrayList<>();
students.add(new Student("Alice", 20, 3.8));
students.add(new Student("Bob", 19, 3.5));
students.add(new Student("Charlie", 21, 3.9));
students.add(new Student("David", 20, 3.7));
students.add(new Student("Larance", 20, 3.9));
// Comparator using anonymous class
Comparator<Student> byAge = new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
return Integer.compare(s1.getAge(), s2.getAge());
}
};

// Comparator using anonymous class


Comparator<Student> byGpa = new Comparator<Student>() {
@Override
public int compare(Student s1, Student s2) {
return Double.compare(s2.getGpa(), s1.getGpa());
}
};

// Comparator using Comparator.comparing()


Comparator<Student> byName = Comparator.comparing(Student::getName);

// Sort by age and print


System.out.println("Sorted by age:");
students.sort(byAge);
students.forEach(System.out::println);

// Sort by GPA and print


System.out.println("\nSorted by GPA (descending):");
students.sort(byGpa);
students.forEach(System.out::println);

// Sort by name and print


System.out.println("\nSorted by name:");
students.sort(byName);
students.forEach(System.out::println);

// Chaining comparators (sort by age, then GPA)


Comparator<Student> byAgeAndGpa = byAge.thenComparing(byGpa);
System.out.println("\nSorted by age, then GPA:");
students.sort(byAgeAndGpa);
students.forEach(System.out::println);
}
}

o/p

Sorted by age:
Student{name='Bob', age=19, gpa=3.5}
Student{name='Alice', age=20, gpa=3.8}
Student{name='David', age=20, gpa=3.7}
Student{name='Larance', age=20, gpa=3.9}
Student{name='Charlie', age=21, gpa=3.9}

Sorted by GPA (descending):


Student{name='Larance', age=20, gpa=3.9}
Student{name='Charlie', age=21, gpa=3.9}
Student{name='Alice', age=20, gpa=3.8}
Student{name='David', age=20, gpa=3.7}
Student{name='Bob', age=19, gpa=3.5}

Sorted by name:
Student{name='Alice', age=20, gpa=3.8}
Student{name='Bob', age=19, gpa=3.5}
Student{name='Charlie', age=21, gpa=3.9}
Student{name='David', age=20, gpa=3.7}
Student{name='Larance', age=20, gpa=3.9}

Sorted by age, then GPA:


Student{name='Bob', age=19, gpa=3.5}
Student{name='Larance', age=20, gpa=3.9}
Student{name='Alice', age=20, gpa=3.8}
Student{name='David', age=20, gpa=3.7}
Student{name='Charlie', age=21, gpa=3.9}

Sorting Algorithms in Collections:

 Collections.sort(): Sorts elements in natural order


 Collections.reverse(): Reverses the order of elements
 Collections.shuffle(): Randomly permutes elements

Searching:

Collections.binarySearch(): Efficiently finds elements in sorted lists


Note: List must be sorted before binary search

Extreme Values:

Collections.min(): Finds the smallest element


Collections.max(): Finds the largest element

Frequency and Set Operations:

Collections.frequency(): Counts occurrences of an element


Collections.disjoint(): Checks if two collections share elements
Filling and Copying:

Collections.fill(): Replaces all elements with specified value


Collections.copy(): Copies elements from source to destination list

Rotation and Swapping:

Collections.rotate(): Rotates elements by specified distance


Collections.swap(): Exchanges elements at specified positions

Collection Wrappers:

Collections.unmodifiableList(): Creates read-only view


Collections.synchronizedList(): Creates thread-safe view

Collection Composition:

addAll(): Combines collections


Various other methods for union, intersection, etc.

Replacement Operations:

Collections.replaceAll(): Replaces all occurrences of specified element

Important points to remember:

Many algorithms modify collections in-place


Some operations require sorted collections (like binary search)
Thread-safety considerations with synchronized collections
Unmodifiable collections throw exceptions on modification attempts
Performance varies (e.g., binary search is O(log n) but requires sorted input)

Program VI A,C

package Collections;

import java.util.*;

class CollectionsAlgorithmsDemo {
public static void main(String[] args) {
// Create sample lists for demonstration
List<Integer> numbers = new ArrayList<>(Arrays.asList(3, 7, 1, 9, 4, 6, 2, 8, 5));
List<Integer> targetList = new ArrayList<>(Arrays.asList(10, 20, 30, 40, 50));

System.out.println("Original list: " + numbers);

// 1. Sorting Algorithms
Collections.sort(numbers);
System.out.println("\n1. After sorting: " + numbers);

Collections.reverse(numbers);
System.out.println(" After reverse: " + numbers);

Collections.shuffle(numbers);
System.out.println(" After shuffle: " + numbers);

// 2. Searching Algorithms
Collections.sort(numbers); // Must sort before binary search
int searchKey = 6;
int position = Collections.binarySearch(numbers, searchKey);
System.out.println("\n2. Binary search for " + searchKey + ": found at index " + position);

// 3. Finding Extreme Values


Integer min = Collections.min(numbers);
Integer max = Collections.max(numbers);
System.out.println("\n3. Minimum value: " + min);
System.out.println(" Maximum value: " + max);

// 4. Frequency and Disjoint Operations


List<Integer> list1 = Arrays.asList(1, 2, 3, 2, 4, 2);
int frequency = Collections.frequency(list1, 2);
System.out.println("\n4. Frequency of 2: " + frequency);

List<Integer> list2 = Arrays.asList(5, 6, 7);


boolean isDisjoint = Collections.disjoint(list1, list2);
System.out.println(" Are lists disjoint? " + isDisjoint);

// 5. Filling and Copying


List<Integer> filledList = new ArrayList<>(Arrays.asList(1, 2, 3, 4, 5));
Collections.fill(filledList, 10);
System.out.println("\n5. After filling with 10: " + filledList);

Collections.copy(targetList, numbers.subList(0, 5));


System.out.println("After copying first 5 elements: " + targetList);

// 6. Rotation and Swapping


Collections.rotate(numbers, 2);
System.out.println("\n6. After rotating right by 2: " + numbers);
Collections.swap(numbers, 0, numbers.size() - 1);
System.out.println(" After swapping first and last elements: " + numbers);

// 7. Unmodifiable and Synchronized Collections


List<Integer> unmodifiableList = Collections.unmodifiableList(numbers);
List<Integer> synchronizedList = Collections.synchronizedList(new ArrayList<>(numbers));
System.out.println("\n7. Unmodifiable list created: " + unmodifiableList);
// unmodifiableList.add(12); //it shows Unsupported Operation Exception
System.out.println(" Synchronized list created: " + synchronizedList);

// 8. Collection Composition
List<Integer> list3 = Arrays.asList(1, 2, 3);
List<Integer> list4 = Arrays.asList(4, 5, 6);
boolean addAll = new ArrayList<>(list3).addAll(list4);
System.out.println("\n8. After combining lists: " + addAll);

// 9. Replacement and Removal


List<String> words = new ArrayList<>(Arrays.asList("apple", "banana", "apple", "cherry"));
Collections.replaceAll(words, "apple", "orange");
System.out.println("\n9. After replacing 'apple' with 'orange': " + words);
}
}

o/p

Original list: [3, 7, 1, 9, 4, 6, 2, 8, 5]

1. After sorting: [1, 2, 3, 4, 5, 6, 7, 8, 9]


After reverse: [9, 8, 7, 6, 5, 4, 3, 2, 1]
After shuffle: [5, 2, 4, 3, 8, 1, 7, 6, 9]

2. Binary search for 6: found at index 5

3. Minimum value: 1
Maximum value: 9

4. Frequency of 2: 3
Are lists disjoint? true

5. After filling with 10: [10, 10, 10, 10, 10]


After copying first 5 elements: [1, 2, 3, 4, 5]

6. After rotating right by 2: [8, 9, 1, 2, 3, 4, 5, 6, 7]


After swapping first and last elements: [7, 9, 1, 2, 3, 4, 5, 6, 8]

7. Unmodifiable list created: [7, 9, 1, 2, 3, 4, 5, 6, 8]


Synchronized list created: [7, 9, 1, 2, 3, 4, 5, 6, 8]
8. After combining lists: true

9. After replacing 'apple' with 'orange': [orange, banana, orange, cherry]

RandomAccess interface:

Purpose:

RandomAccess is a marker interface (has no methods)


Indicates that a list supports fast random access operations
Used to optimize algorithms based on access patterns

Key Characteristics:

Constant-time positional access (O(1))


Implemented by ArrayList but not LinkedList
Used by Collections utility methods to choose optimal algorithms

When to Use:

For lists that will be frequently accessed by index


When binary search operations are common
In algorithms that require frequent random access

Performance Implications:

ArrayList: Fast random access, implements RandomAccess


LinkedList: Slow random access, doesn't implement RandomAccess

Affects performance of operations like:

Binary search
Random access iterations
Sorting algorithms

Best Practices:

Check for RandomAccess before choosing access strategy


Use iterator for non-RandomAccess lists
Use indexed for-loop for RandomAccess lists
Consider RandomAccess for custom List implementations
The example demonstrates:

Performance comparison between random and sequential access


How to check if a list implements RandomAccess
Optimal access strategy selection
Impact on binary search performance
Practical usage in real-world scenarios

Key Observations from the Program:

ArrayList shows better performance with random access


LinkedList performs better with iterator access
Binary search is significantly faster on ArrayList

The interface helps in selecting the optimal access strategy

Program VI B

package Collections;

import java.util.*;

class RandomAccessDemo {
// Utility method to measure access time using for loop (random access)
private static long measureRandomAccessTime(List<Integer> list) {
long startTime = System.nanoTime();

// Perform random access operations


for (int i = 0; i < list.size(); i++) {
int element = list.get(i);
}

return System.nanoTime() - startTime;


}

// Utility method to measure access time using iterator


private static long measureIteratorAccessTime(List<Integer> list) {
long startTime = System.nanoTime();

// Perform sequential access using iterator


Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
int element = iterator.next();
}

return System.nanoTime() - startTime;


}
// Method to demonstrate optimal access strategy based on RandomAccess interface
private static void accessList(List<Integer> list) {
if (list instanceof RandomAccess) {
System.out.println("Using Random Access strategy (for loop)");
for (int i = 0; i < list.size(); i++) {
int element = list.get(i);
}
} else {
System.out.println("Using Sequential Access strategy (iterator)");
Iterator<Integer> iterator = list.iterator();
while (iterator.hasNext()) {
int element = iterator.next();
}
}
}

public static void main(String[] args) {


// Create ArrayList (implements RandomAccess)
List<Integer> arrayList = new ArrayList<>();
// Create LinkedList (does not implement RandomAccess)
List<Integer> linkedList = new LinkedList<>();

// Populate both lists with same data


for (int i = 0; i < 100000; i++) {
arrayList.add(i);
linkedList.add(i);
}

System.out.println("Testing ArrayList (implements RandomAccess):");


System.out.println("Is RandomAccess? " + (arrayList instanceof RandomAccess));

// Measure access times for ArrayList


long arrayListRandomTime = measureRandomAccessTime(arrayList);
long arrayListIteratorTime = measureIteratorAccessTime(arrayList);

System.out.println("Random access time: " + arrayListRandomTime + " ns");


System.out.println("Iterator access time: " + arrayListIteratorTime + " ns");

System.out.println("\nTesting LinkedList (does not implement RandomAccess):");


System.out.println("Is RandomAccess? " + (linkedList instanceof RandomAccess));

// Measure access times for LinkedList


long linkedListRandomTime = measureRandomAccessTime(linkedList);
long linkedListIteratorTime = measureIteratorAccessTime(linkedList);

System.out.println("Random access time: " + linkedListRandomTime + " ns");


System.out.println("Iterator access time: " + linkedListIteratorTime + " ns");
System.out.println("\nDemonstrating optimal access strategy:");
System.out.println("For ArrayList:");
accessList(arrayList);
System.out.println("For LinkedList:");
accessList(linkedList);

// Demonstrate practical usage with Collections method


System.out.println("\nBinary Search Performance:");
// Sort lists first (required for binary search)
Collections.sort(arrayList);
Collections.sort(linkedList);

long startTime = System.nanoTime();


Collections.binarySearch(arrayList, 50000);
System.out.println("ArrayList binary search time: " +
(System.nanoTime() - startTime) + " ns");

startTime = System.nanoTime();
Collections.binarySearch(linkedList, 50000);
System.out.println("LinkedList binary search time: " +
(System.nanoTime() - startTime) + " ns");
}
}

o/p

Testing ArrayList (implements RandomAccess):


Is RandomAccess? true
Random access time: 14488283 ns
Iterator access time: 15545606 ns

Testing LinkedList (does not implement RandomAccess):


Is RandomAccess? false
Random access time: 6491496468 ns
Iterator access time: 18176749 ns

Demonstrating optimal access strategy:


For ArrayList:
Using Random Access strategy (for loop)
For LinkedList:
Using Sequential Access strategy (iterator)

Binary Search Performance:


ArrayList binary search time: 211436 ns
LinkedList binary search time: 6191515 ns
Legacy classes and interfaces

Vector:

Thread-safe implementation of a dynamic array


Similar to ArrayList but synchronized
Key methods:

addElement(): Adds element


elements(): Returns Enumeration
capacity(): Returns current capacity
trimToSize(): Trims capacity to size

Hashtable:

Thread-safe implementation of a hash table


Similar to HashMap but synchronized
Does not allow null keys or values
Key methods:

put(): Adds key-value pair


get(): Retrieves value
keys(): Returns Enumeration of keys

Dictionary:

Abstract class that maps keys to values


Superclass of Hashtable
Largely replaced by Map interface
Key methods:

put(): Adds mapping


get(): Retrieves value
keys(): Returns key Enumeration
Stack:

LIFO (Last-In-First-Out) data structure


Extends Vector
Key methods:

push(): Adds element


pop(): Removes and returns top element
peek(): Views top element

Properties:

Extends Hashtable
Used for storing configuration settings
Key methods:

setProperty(): Sets property


getProperty(): Gets property value
propertyNames(): Returns property names

BitSet:

Vector of bits that grows as needed


Used for flags and small sets
Key methods:

set(): Sets bit


clear(): Clears bit
get(): Checks bit status

Enumeration Interface:

Legacy iterator interface


Replaced by Iterator interface
Key methods:

hasMoreElements(): Checks for more elements


nextElement(): Gets next element
Important Notes:

These classes are considered legacy but still supported for backward compatibility
Modern alternatives are generally preferred:

ArrayList instead of Vector


HashMap instead of Hashtable
Iterator instead of Enumeration
LinkedList or ArrayDeque instead of Stack

Key differences from modern collections:

Legacy classes are synchronized (thread-safe)


Generally have poorer performance
Less functionality than modern alternatives
More restrictive (e.g., no null values in Hashtable)

Program

package Collections;

import java.util.*;

public class LegacyCollectionsDemo {


public static void main(String[] args) {
// 1. Vector demonstration
System.out.println("1. Vector Example:");
Vector<String> vector = new Vector<>();
vector.addElement("First");
vector.addElement("Second");
vector.addElement("Third");

// Using Enumeration to iterate Vector


System.out.println("\nUsing Enumeration to iterate Vector:");
Enumeration<String> enumeration = vector.elements();
while (enumeration.hasMoreElements()) {
System.out.println(enumeration.nextElement());
}

// Vector specific methods


System.out.println("\nVector size: " + vector.size());
System.out.println("Vector capacity: " + vector.capacity());
vector.trimToSize();
System.out.println("Vector capacity after trim: " + vector.capacity());

// 2. Hashtable demonstration
System.out.println("\n2. Hashtable Example:");
Hashtable<Integer, String> hashtable = new Hashtable<>();
hashtable.put(1, "One");
hashtable.put(2, "Two");
hashtable.put(3, "Three");

// Using Enumeration to iterate Hashtable keys


System.out.println("\nHashtable keys using Enumeration:");
Enumeration<Integer> keys = hashtable.keys();
while (keys.hasMoreElements()) {
Integer key = keys.nextElement();
System.out.println("Key: " + key + ", Value: " + hashtable.get(key));
}

// 3. Dictionary demonstration (Hashtable extends Dictionary)


System.out.println("\n3. Dictionary Example:");
Dictionary<Integer, String> dictionary = new Hashtable<>();
dictionary.put(1, "First");
dictionary.put(2, "Second");

System.out.println("Dictionary elements:");
Enumeration<Integer> dictKeys = dictionary.keys();
while (dictKeys.hasMoreElements()) {
Integer key = dictKeys.nextElement();
System.out.println("Key: " + key + ", Value: " + dictionary.get(key));
}

// 4. Stack demonstration
System.out.println("\n4. Stack Example:");
Stack<Integer> stack = new Stack<>();
stack.push(1);
stack.push(2);
stack.push(3);

System.out.println("Stack elements:");
while (!stack.empty()) {
System.out.println("Popped: " + stack.pop());
}

// 5. Properties demonstration
System.out.println("\n5. Properties Example:");
Properties properties = new Properties();
properties.setProperty("database.url", "jdbc:mysql://localhost:3306/db");
properties.setProperty("database.user", "admin");
properties.setProperty("database.password", "password");
System.out.println("Properties elements:");
Enumeration<?> propertyNames = properties.propertyNames();
while (propertyNames.hasMoreElements()) {
String propertyName = (String) propertyNames.nextElement();
System.out.println(propertyName + " = " + properties.getProperty(propertyName));
}

// 6. BitSet demonstration
System.out.println("\n6. BitSet Example:");
BitSet bitSet = new BitSet(8);
bitSet.set(0); // Set first bit
bitSet.set(2); // Set third bit
bitSet.set(4); // Set fifth bit

System.out.println("BitSet elements:");
for (int i = 0; i < 8; i++) {
System.out.println("Bit " + i + " is set: " + bitSet.get(i));
}
}
}

o/p

1. Vector Example:

Using Enumeration to iterate Vector:


First
Second
Third

Vector size: 3
Vector capacity: 10
Vector capacity after trim: 3

2. Hashtable Example:

Hashtable keys using Enumeration:


Key: 3, Value: Three
Key: 2, Value: Two
Key: 1, Value: One

3. Dictionary Example:
Dictionary elements:
Key: 2, Value: Second
Key: 1, Value: First

4. Stack Example:
Stack elements:
Popped: 3
Popped: 2
Popped: 1

5. Properties Example:
Properties elements:
database.password = password
database.user = admin
database.url = jdbc:mysql://localhost:3306/db

6. BitSet Example:
BitSet elements:
Bit 0 is set: true
Bit 1 is set: false
Bit 2 is set: true
Bit 3 is set: false
Bit 4 is set: true
Bit 5 is set: false
Bit 6 is set: false
Bit 7 is set: false

Comparable vs Comparator in Java

Both Comparable and Comparator are interfaces in Java used to sort objects, but they serve different
purposes and are implemented differently.

Comparable

The Comparable interface is implemented by a class to define its "natural ordering." It has a single
method:

public int compareTo(T o);

Key characteristics:

It's in the java.lang package


Objects implement this to compare themselves with another object
Used with Collections.sort() and Arrays.sort() without additional parameters

Example

package Comparable;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;

public class Student implements Comparable<Student> {


private String name;
private int rollNumber;

public Student(String name, int rollNumber) {


this.name = name;
this.rollNumber = rollNumber;
}

// Natural ordering based on roll number


@Override
public int compareTo(Student other) {
return this.rollNumber - other.rollNumber;
}

// Add toString method to properly display the object


@Override
public String toString() {
return "Student{name='" + name + "', rollNumber=" + rollNumber + "'}";
}

public static void main(String[] args) {


List<Student> students = new ArrayList<>();
students.add(new Student("Alice", 103));
students.add(new Student("Bob", 101));
students.add(new Student("Charlie", 102));

// Sort using natural ordering (by roll number)


Collections.sort(students);

for (Student s : students) {


System.out.println(s);
}
}
}

o/p

Student{name='Bob', rollNumber=101'}
Student{name='Charlie', rollNumber=102'}
Student{name='Alice', rollNumber=103'}

Comparator

The Comparator interface is external to the class being compared and defines custom ordering. It has a
primary method:

public int compare(T o1, T o2);

Key characteristics:
It's in the java.util package
Allows defining multiple different comparison strategies for the same class
Used with sort methods that accept a Comparator parameter

Example

package Comparator;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Comparator; // Missing import for Comparator
import java.util.List;

public class Student {


private String name;
private int rollNumber;
private double gpa;

public Student(String name, int rollNumber, double gpa) {


this.name = name;
this.rollNumber = rollNumber;
this.gpa = gpa;
}

// Missing getters needed by the comparators


public String getName() {
return name;
}

public int getRollNumber() {


return rollNumber;
}

public double getGpa() {


return gpa;
}

@Override
public String toString() {
return "Student{name='" + name + "', rollNumber=" + rollNumber + ", gpa=" + gpa + "}";
}

public static void main(String[] args) {


List<Student> students = new ArrayList<>();
students.add(new Student("Alice", 103, 3.8));
students.add(new Student("Bob", 101, 3.9));
students.add(new Student("Charlie", 102, 3.7));

// Sort by name
Collections.sort(students, new NameComparator());
System.out.println("Sorted by name:"); // Missing print statement
for (Student s : students) {
System.out.println(s);
}

// Sort by GPA
Collections.sort(students, new GpaComparator());
System.out.println("\nSorted by GPA:");
for (Student s : students) {
System.out.println(s);
}
}
}

// Comparator for sorting by name


class NameComparator implements Comparator<Student> {
@Override // Missing @Override annotation
public int compare(Student s1, Student s2) {
return s1.getName().compareTo(s2.getName());
}
}

// Comparator for sorting by GPA (descending)


class GpaComparator implements Comparator<Student> {
@Override // Missing @Override annotation
public int compare(Student s1, Student s2) {
// Sort in descending order
return Double.compare(s2.getGpa(), s1.getGpa());
}
}

o/p

Sorted by name:
Student{name='Alice', rollNumber=103, gpa=3.8}
Student{name='Bob', rollNumber=101, gpa=3.9}
Student{name='Charlie', rollNumber=102, gpa=3.7}

Sorted by GPA:
Student{name='Bob', rollNumber=101, gpa=3.9}
Student{name='Alice', rollNumber=103, gpa=3.8}
Student{name='Charlie', rollNumber=102, gpa=3.7}
Parting Thoughts on Collections

The Collections Framework gives you, the programmer, a powerful set of well-engineered
solutions to some of programming’s most common tasks. Consider using a collection the
next time that you need to store and retrieve information. Remember, collections need not
be reserved for only the “large jobs,” such as corporate databases, mailing lists, or inventory
systems. They are also effective when applied to smaller jobs. For example, a TreeMap might
make an excellent collection to hold the directory structure of a set of files. A TreeSet could
be quite useful for storing project-management information. Frankly, the types of problems
that will benefit from a collections-based solution are limited only by your imagination.

Choosing the Right Collection Type:


 Use List when order matters and duplicates are allowed
 Use Set when uniqueness is required
 Use Map for key-value associations
 Use Queue/Deque for ordered processing and FIFO/LIFO operations
2. Performance Considerations:
 ArrayList: Fast iteration and random access, slow insertion/deletion
 LinkedList: Fast insertion/deletion, slower random access
 HashSet/HashMap: O(1) for most operations but no ordering
 TreeSet/TreeMap: O(log n) operations but maintains order
 Consider initial capacity setting for better performance
3. Thread Safety:
 Regular collections are not thread-safe
 Use Collections.synchronizedXXX() wrappers when needed
 Consider concurrent collections (ConcurrentHashMap, CopyOnWriteArrayList) for
better performance in concurrent applications
 Avoid Vector/Hashtable unless legacy code requires them
4. Best Practices:
 Use interfaces as variable types (List instead of ArrayList)
 Use generics to ensure type safety
 Consider unmodifiable wrappers for read-only views
 Use isEmpty() instead of checking size() == 0
 Use enhanced for-loop for simple iterations
 Use stream API for complex operations
5. Common Gotchas:
 ConcurrentModificationException during iteration
 NullPointerException in TreeSet/TreeMap with null elements
 Memory leaks in caching scenarios
 Unexpected ordering in HashSet/HashMap
6. Collection Utility Methods:
 Collections class provides useful algorithms
 Arrays class helps convert between arrays and collections
 Stream API provides powerful data processing capabilities
7. Memory Efficiency:
 Use appropriate initial capacity
 Consider using EnumSet for enum types
 Use trimToSize() when size stabilizes
 Be cautious with large auto-boxing collections
8. Design Patterns with Collections:
 Observer Pattern with List of listeners
 Composite Pattern with recursive structures
 Iterator Pattern for custom collections
 Decorator Pattern with collection wrappers
9. Modern Approaches:
 Prefer stream operations for bulk data processing
 Use Optional for null safety
 Consider immutable collections where possible
 Use factory methods (List.of(), Set.of(), etc.)
10. Maintenance Considerations:
 Document any special ordering requirements
 Consider implementing equals/hashCode for custom objects
 Use clear naming conventions
 Handle exceptions appropriately

You might also like