This collection of 25 Java exercises covers the full Set family, HashSet and TreeSet, and how they differ in ordering, duplicate handling, and navigation.
- You’ll start with
HashSetbasics: uniqueness, membership checks, iteration, and array conversion. - Then move into set operations like intersection, union, and difference.
- The
TreeSetexercises cover natural and custom sort order, range extraction and the navigation methods. - The final exercises cover overriding
equals()andhashCode()for custom objects, deduplicating a list, and collecting a filtered stream directly into a sorted set.
Each exercise includes a Problem Statement, Purpose, Hint, and a Solution with Explanation that clarifies exactly why each method behaves the way it does.
- Also, See: Java Exercises with over 20+ topic-wise sets and 575+ coding questions to practice.
- Practice questions using our Online Java Compiler
+ Table of Contents (25 Exercises)
Table of contents
- Exercise 1: Initialize and Append
- Exercise 2: Verification of Existence
- Exercise 3: Iteration Techniques
- Exercise 4: HashSet to Array Conversion
- Exercise 5: Clear and Empty Check
- Exercise 6: Find Common Elements (Intersection)
- Exercise 7: Clone a HashSet
- Exercise 8: Natural Order Verification
- Exercise 9: Reverse Alphabetical Order
- Exercise 10: Fetch Extremes
- Exercise 11: Destructive Polling
- Exercise 12: Head Range Selection (headSet)
- Exercise 13: Tail Range Selection (tailSet)
- Exercise 14: Subset Range extraction (subSet)
- Exercise 15: The Sorting Transformation
- Exercise 16: HashSet to ArrayList
- Exercise 17: Case-Insensitive TreeSet
- Exercise 18: Custom Objects in a TreeSet
- Exercise 19: The Duplicate Stripper
- Exercise 20: Finding Closest Match (floor and ceiling)
- Exercise 21: Strictly Higher/Lower (higher and lower)
- Exercise 22: Mathematical Union and Difference
- Exercise 23: Overriding hashCode() and equals()
- Exercise 24: Unique Word Counter
- Exercise 25: Stream API Integration
Exercise 1: Initialize and Append
Problem Statement: Create a HashSet of integers. Add 5 distinct numbers to it. Try adding a duplicate of one of the numbers and print the set to observe how it handles duplicates.
Purpose: This exercise demonstrates the defining trait of a Set, that it never stores duplicate elements, and shows that attempting to add one simply has no effect rather than causing an error.
Given Input: Numbers to add: 10, 20, 30, 40, 50, then a duplicate 30
Expected Output: [50, 20, 40, 10, 30] (exact printed order may vary since a HashSet does not guarantee ordering)
▼ Hint
- Call
add()five times with five different numbers. - Call
add(30)a second time, attempting to insert a number already present in the set. - Print the set afterward and confirm it still contains exactly 5 elements, since the duplicate call was silently ignored.
▼ Solution & Explanation
Explanation:
numbers.add(30)the second time: Since30already exists in the set, this call has no effect on the set’s contents.boolean wasAdded: Theadd()method returnsfalsewhen the element was already present, which is a convenient way to detect a duplicate without a separatecontains()check.- Unordered printing: A
HashSetmakes no guarantee about the order elements are stored or printed in, so the visible order can differ from the order the numbers were added.
Exercise 2: Verification of Existence
Problem Statement: Create a HashSet containing five different colors (Strings). Write a program to check if the color “Yellow” is present in the set.
Purpose: This exercise practices membership testing on a HashSet, which runs in constant time on average thanks to hashing, making it a much faster lookup than scanning a list one element at a time.
Given Input: colors = {"Red", "Green", "Blue", "Orange", "Purple"}
Expected Output: Contains Yellow: false
▼ Hint
Call colors.contains("Yellow"), which returns true only if an element equal to "Yellow" exists somewhere in the set.
▼ Solution & Explanation
Explanation:
colors.contains("Yellow"): Computes the hash of"Yellow"and checks only the corresponding bucket for a match, rather than comparing it against every element in the set.- Result: Since
"Yellow"was never added, the method returnsfalse.
Exercise 3: Iteration Techniques
Problem Statement: Add 5 elements to a HashSet. Write code to iterate through the set using two different methods: an Iterator and an enhanced for-each loop.
Purpose: This exercise practices both common ways of traversing a Set, and highlights that only the explicit Iterator supports safely removing elements while iterating.
Given Input: letters = {"A", "B", "C", "D", "E"}
Using Iterator: A B C D E Using for-each: A B C D E
▼ Hint
- Get an
Iteratorusingletters.iterator(), then use awhile (iterator.hasNext())loop to print each element withiterator.next(). - Use
for (String letter : letters)as a simpler alternative that hides the iterator mechanics entirely. - Since a
HashSetdoes not guarantee order, both loops will visit the elements in the same relative order as each other, even if that order does not match insertion order.
▼ Solution & Explanation
Explanation:
letters.iterator(): Returns an explicitIteratorobject that tracks its own position within the set, giving direct control over the traversal.for (String letter : letters): Internally uses the sameIteratormechanism, but Java hides thehasNext()andnext()calls, producing more concise code.- Removal safety: Only the explicit
Iteratorsupportsiterator.remove()during traversal, since modifying the set directly inside a for-each loop would throw aConcurrentModificationException.
Exercise 4: HashSet to Array Conversion
Problem Statement: Create a HashSet of strings and convert it into a standard Java array (String[]). Print the array elements.
Purpose: This exercise practices exporting a HashSet‘s contents into a fixed-size array, useful when passing data to an API or method that specifically expects an array rather than a Collection.
Given Input: fruits = {"Apple", "Mango", "Banana"}
Expected Output: [Apple, Mango, Banana] (exact order may vary)
▼ Hint
- Call
fruits.toArray(new String[0]), which returns a properly typedString[]array containing all the set’s elements. - Use
Arrays.toString()to print the array contents in a readable format, since printing an array directly shows its memory reference instead.
▼ Solution & Explanation
Explanation:
fruits.toArray(new String[0]): The empty array argument tells Java the exact type of array to produce, and the method internally creates a new array of the correct size to hold every element.Arrays.toString(fruitArray): Converts the array into a readable, comma-separatedStringrepresentation suitable for printing.- Independent copy: The resulting array is a separate structure from the set, so modifying the array afterward would not affect
fruits.
Exercise 5: Clear and Empty Check
Problem Statement: Write a program that populates a HashSet, checks if it is empty, clears all elements using a single method, and checks if it is empty again.
Purpose: This exercise practices resetting a set back to an empty state and verifying that state before and after, a common pattern when a collection needs to be reused across multiple operations.
Given Input: tags = {"java", "python", "sql"}
Is empty before clear: false Is empty after clear: true
▼ Hint
- Call
tags.isEmpty()right after populating the set, which should returnfalse. - Call
tags.clear()to remove every element from the set at once. - Call
tags.isEmpty()again, which should now returntrue.
▼ Solution & Explanation
Explanation:
tags.isEmpty(): Returnstrueonly when the set contains zero elements, and internally is equivalent to checking whethersize() == 0.tags.clear(): Removes every element from the set in a single call, resetting its size to zero without needing to reassign the variable to a new object.- Reusability: Because
tagsis cleared rather than replaced, the same set object can continue to be reused for future insertions.
Exercise 6: Find Common Elements (Intersection)
Problem Statement: Given two separate HashSet instances populated with numbers, write a program to find and display only the elements that exist in both sets.
Purpose: This exercise practices set intersection using the built-in retainAll() method, which mirrors the mathematical concept of a set intersection directly in code.
Given Input: setA = {1, 2, 3, 4, 5}, setB = {3, 4, 5, 6, 7}
Expected Output: [3, 4, 5] (exact printed order may vary)
▼ Hint
- Make a copy of
setAfirst, sinceretainAll()modifies the set it is called on. - Call
copyOfSetA.retainAll(setB), which removes any element fromcopyOfSetAthat is not also present insetB. - Whatever remains in
copyOfSetAafterward is exactly the intersection of the two original sets.
▼ Solution & Explanation
Explanation:
new HashSet<>(setA): Creates an independent copy ofsetA, so the intersection operation does not destroy the original data.intersection.retainAll(setB): Removes every element fromintersectionthat is not also found insetB, leaving only the elements common to both sets.- Both originals untouched:
setAandsetBremain exactly as they were, since all the modification happened on the separateintersectioncopy.
Exercise 7: Clone a HashSet
Problem Statement: Create a HashSet of employee names. Write a program to clone this HashSet into another HashSet and verify that both contain the same elements.
Purpose: This exercise practices creating an independent copy of a set’s structure, and confirms equality between two separate set objects based on their contents rather than their identity.
Given Input: employees = {"Rahul", "Sneha", "Vikram"}
Expected Output: Sets are equal: true
▼ Hint
- Call
employees.clone(), which returns anObjectthat needs to be cast back toHashSet<String>. - Use
employees.equals(clonedSet)to check whether both sets contain exactly the same elements, regardless of whether they are the same object in memory. HashSetoverridesequals()to compare contents rather than object identity, so two different set objects with identical elements are considered equal.
▼ Solution & Explanation
Explanation:
employees.clone(): Produces a shallow copy containing the same elements asemployees, though the returned type must be cast sinceclone()returns a rawObject.employees.equals(clonedSet): Compares the contents of both sets element by element, returningtruebecause every name in one set also appears in the other.- Independent objects: Even though they are equal by content,
employeesandclonedSetare two distinct set objects, so modifying one afterward would not affect the other.
Exercise 8: Natural Order Verification
Problem Statement: Create a TreeSet of integers. Insert 10 numbers in a completely random, unsorted order. Print the set to observe the automatic sorting behavior.
Purpose: This exercise introduces TreeSet, which, unlike HashSet, always keeps its elements in sorted order regardless of the sequence they were inserted in.
Given Input: Insert order: 42, 7, 19, 3, 88, 15, 61, 24, 5, 99
Expected Output: [3, 5, 7, 15, 19, 24, 42, 61, 88, 99]
▼ Hint
- Insert the ten numbers into a
TreeSet<Integer>in the exact random order given, without worrying about their final position. - Print the set directly.
- Because
TreeSetstores its elements in a sorted tree structure internally, iterating or printing it always produces the elements in ascending natural order for numbers.
▼ Solution & Explanation
Explanation:
- Insertion order irrelevant: The ten numbers were added in a scrambled sequence, but a
TreeSetplaces every new element into its correct sorted position immediately upon insertion. - Natural ordering: For
Integerelements, the natural order is simple ascending numerical order, which is what determines the final printed sequence. - Contrast with
HashSet: AHashSetholding the same ten numbers would print them in an unpredictable order based on their hash codes, whereas theTreeSetoutput is fully deterministic.
Exercise 9: Reverse Alphabetical Order
Problem Statement: Create a TreeSet that stores strings in reverse alphabetical order (Z to A) by using a custom comparator (Comparator.reverseOrder()).
Purpose: This exercise practices supplying a custom Comparator to a TreeSet‘s constructor, showing how the default natural ordering can be overridden entirely with your own comparison rule.
Given Input: Insert order: "Banana", "Apple", "Cherry", "Date"
Expected Output: [Date, Cherry, Banana, Apple]
▼ Hint
- Pass
Comparator.reverseOrder()into theTreeSetconstructor instead of leaving it empty. - Add the four fruit names in any order you like.
- Printing the set will show the strings arranged from
"Date"down to"Apple", the reverse of standard alphabetical order.
▼ Solution & Explanation
Explanation:
new TreeSet<>(Comparator.reverseOrder()): Supplies a custom ordering rule at construction time, telling the set to sort in the opposite direction of the natural ordering forString.Comparator.reverseOrder(): A built-in comparator that flips whatever the natural comparison result would be, meaning"Z"now sorts before"A".- Result: Regardless of the order the fruits were added, the set always maintains and prints them from
"Date"to"Apple", following the reversed rule.
Exercise 10: Fetch Extremes
Problem Statement: Write a program to find and print the lowest (first) and highest (last) elements currently stored in a TreeSet of prices.
Purpose: This exercise practices retrieving the boundary elements of a TreeSet directly, taking advantage of its sorted structure instead of scanning every element to compare values manually.
Given Input: prices = {49.99, 12.50, 89.00, 5.75, 34.20}
Lowest price: 5.75 Highest price: 89.0
▼ Hint
- Call
prices.first(), which returns the smallest element currently in the set. - Call
prices.last(), which returns the largest element currently in the set. - Both methods throw
NoSuchElementExceptionif the set is empty, so they should only be called when at least one element is guaranteed to exist.
▼ Solution & Explanation
Explanation:
prices.first(): Because the set’s internal tree structure always keeps the smallest element at the leftmost position, this method retrieves it directly without scanning the rest of the set.prices.last(): Similarly retrieves the rightmost, largest element directly from the tree structure.- Efficiency: Both operations are much faster than manually looping through every price and tracking the minimum and maximum by hand, since the sorted structure already knows where the extremes are.
Exercise 11: Destructive Polling
Problem Statement: Populate a TreeSet with numbers. Use pollFirst() and pollLast() to retrieve and remove the highest and lowest elements, then print the remaining set.
Purpose: This exercise practices combining a read and a delete into a single call, useful whenever you need to process and consume the smallest and largest elements of a set repeatedly, such as in a priority-driven task queue.
Given Input: numbers = {15, 8, 23, 4, 42, 16}
Polled first (lowest): 4 Polled last (highest): 42 Remaining set: [8, 15, 16, 23]
▼ Hint
- Call
numbers.pollFirst(), which returns and removes the smallest element in one step. - Call
numbers.pollLast(), which returns and removes the largest element in one step. - Unlike
first()andlast(), these methods returnnullinstead of throwing an exception if the set happens to be empty.
▼ Solution & Explanation
Explanation:
numbers.pollFirst(): Reads the smallest element,4, and removes it from the set in the same operation, leaving8as the new smallest.numbers.pollLast(): Reads the largest remaining element,42, and removes it, leaving23as the new largest.- Remaining set: After both calls, only the four middle values remain, still automatically sorted since the underlying structure is still a
TreeSet.
Exercise 12: Head Range Selection (headSet)
Problem Statement: Given a TreeSet of integers from 1 to 10, use the headSet() method to find and print all numbers strictly less than 7.
Purpose: This exercise practices extracting a leading range of elements from a sorted set, taking advantage of the set’s structure instead of manually filtering with an if check inside a loop.
Given Input: numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9, 10}
Expected Output: [1, 2, 3, 4, 5, 6]
▼ Hint
Call numbers.headSet(7), which returns every element strictly less than 7, excluding 7 itself from the result.
▼ Solution & Explanation
Explanation:
numbers.headSet(7): Returns all elements ordered before7in the sorted set, relying on the tree structure to locate the cutoff point efficiently.- Exclusive by default: The single-argument version of
headSet()always excludes the boundary value itself, which is why7does not appear in the result. SortedSet<Integer>:headSet()returns a view typed as the more generalSortedSetinterface, since that is the return type declared by the method.
Exercise 13: Tail Range Selection (tailSet)
Problem Statement: Given a TreeSet of exam scores, use the tailSet() method to find and print all scores that are greater than or equal to 65.
Purpose: This exercise practices extracting a trailing range of elements from a sorted set, the complementary operation to headSet(), and confirms that the boundary value itself is included.
Given Input: scores = {42, 58, 65, 71, 80, 95}
Expected Output: [65, 71, 80, 95]
▼ Hint
Call scores.tailSet(65), which returns every element greater than or equal to 65, including 65 itself since it is present in the set.
▼ Solution & Explanation
Explanation:
scores.tailSet(65): Returns all elements ordered from65onward, walking the tree structure to the correct starting point instead of scanning from the beginning.- Inclusive by default: The single-argument version of
tailSet()always includes the boundary value if it exists in the set, unlikeheadSet()which excludes its boundary. - Result: The returned view contains every score that would be considered passing under a 65 point threshold.
Exercise 14: Subset Range extraction (subSet)
Problem Statement: Given a TreeSet of alphabetic characters, extract and print a subset of characters that fall between ‘C’ (inclusive) and ‘P’ (exclusive).
Purpose: This exercise practices extracting a range from both ends at once using subSet(), combining the behavior of headSet() and tailSet() into a single call.
Given Input: letters = {'A', 'C', 'F', 'K', 'P', 'T', 'Z'}
Expected Output: [C, F, K]
▼ Hint
- Call
letters.subSet('C', 'P'), which returns every element starting from'C'up to but not including'P'. - The starting boundary is inclusive and the ending boundary is exclusive by default, matching the same rule used by
subMap()on aTreeMap. - Since
'P'itself is excluded, it will not appear in the result even though it exists in the original set.
▼ Solution & Explanation
Explanation:
letters.subSet('C', 'P'): Combines a lower and upper bound in one call, returning only the elements that fall within that range according to the set’s natural ordering.- Inclusive start, exclusive end:
'C'appears in the result because the starting boundary is inclusive, while'P'is left out because the ending boundary is exclusive. 'A'and'T','Z'excluded: These fall entirely outside the requested range, sosubSet()leaves them out of the returned view.
Exercise 15: The Sorting Transformation
Problem Statement: Create a HashSet with unsorted city names. Convert this entire HashSet into a TreeSet and print it to show how it becomes sorted.
Purpose: This exercise practices converting between set implementations using a constructor call, the same pattern used earlier to convert a HashMap into a sorted TreeMap.
Given Input: HashSet: {"Mumbai", "Delhi", "Bangalore", "Chennai"}
Expected Output: [Bangalore, Chennai, Delhi, Mumbai]
▼ Hint
Pass the existing HashSet directly into the TreeSet constructor, new TreeSet<>(unsortedCities), which copies every element and arranges them immediately according to natural ordering.
▼ Solution & Explanation
Explanation:
new TreeSet<>(unsortedCities):TreeSethas a constructor that accepts anyCollection, copying its elements in and sorting them as part of construction.- Independent result:
sortedCitiesis a completely separate object fromunsortedCities, so changes to one afterward do not affect the other. - Result: The same four city names now appear in strict alphabetical order, regardless of whatever unpredictable order the original
HashSetmay have printed them in.
Exercise 16: HashSet to ArrayList
Problem Statement: Convert a HashSet of elements into an ArrayList. Explain or demonstrate via code why you might want to switch to a List index-based retrieval.
Purpose: This exercise practices converting a Set into a List, and demonstrates a capability sets fundamentally lack, retrieving an element by its numeric position.
Given Input: tools = {"Hammer", "Wrench", "Screwdriver"}
Expected Output: Element at index 1: Wrench (exact element may vary depending on the original HashSet’s iteration order)
▼ Hint
- Pass the
HashSetinto theArrayListconstructor,new ArrayList<>(tools), to build a list containing the same elements. - Call
toolList.get(1)to retrieve the element at index1, something aHashSethas no equivalent method for. - This conversion is useful whenever code needs indexed access, sorting by a custom order, or duplicate values, none of which a
Setsupports directly.
▼ Solution & Explanation
Explanation:
new ArrayList<>(tools): Copies every element from theHashSetinto a newArrayList, fixing whatever order the set’s iterator produced into a specific, stable list order.toolList.get(1): ASethas no concept of position, so there is no equivalent method onHashSet; converting to aListis required whenever indexed access like this is needed.- Other
List-only capabilities: Beyond indexed access, aListalso permits duplicate elements and allows sorting with a customComparatorviaCollections.sort(), both of which are impossible on aSetdirectly.
Exercise 17: Case-Insensitive TreeSet
Problem Statement: Create a TreeSet of strings that ignores case sensitivity. If “Java” is already in the set, trying to add “java” or “JAVA” should be rejected as a duplicate.
Purpose: This exercise practices supplying a custom Comparator that changes what counts as a duplicate entirely, showing that a TreeSet‘s notion of equality is fully controlled by whatever comparator it is given.
Given Input: Insert order: "Java", "java", "JAVA", "Python"
Expected Output: [Java, Python]
▼ Hint
- Pass
String.CASE_INSENSITIVE_ORDERinto theTreeSetconstructor, a built-in comparator designed exactly for this purpose. - Because a
TreeSetuses its comparator, notequals(), to decide whether two elements are the same, any string that compares as equal under case-insensitive rules will be rejected as a duplicate. - Only the first version of the string encountered is kept, since later duplicate attempts are simply ignored by
add().
▼ Solution & Explanation
Explanation:
String.CASE_INSENSITIVE_ORDER: A ready-made comparator that treats strings differing only in letter case as equal, avoiding the need to write this comparison logic manually.languages.add("java")andlanguages.add("JAVA"): Both calls are rejected since the comparator finds them equal to the already present"Java", keeping the set’s size at just two unique entries.- Comparator, not
equals(): Once aTreeSetis given a comparator, it relies entirely on that comparator’s result to determine equality, ignoring the class’s ownequals()implementation altogether.
Exercise 18: Custom Objects in a TreeSet
Problem Statement: Create a Product class with id (int) and name (String). Implement the Comparable interface so that a TreeSet<Product> automatically sorts products by their id.
Purpose: This exercise practices implementing Comparable on a custom class, which is what allows a TreeSet to sort objects of that class without needing a separate Comparator to be supplied.
Given Input: Insert order: Product(103, "Monitor"), Product(101, "Keyboard"), Product(102, "Mouse")
Expected Output: [101 - Keyboard, 102 - Mouse, 103 - Monitor]
▼ Hint
- Declare
class Product implements Comparable<Product>. - Implement
compareTo(Product other)by comparing the two objects’idfields, typically usingInteger.compare(this.id, other.id). - Once
ProductimplementsComparable, a plainnew TreeSet<Product>()will automatically usecompareTo()to determine sort order without needing any comparator argument.
▼ Solution & Explanation
Explanation:
class Product implements Comparable<Product>: Declares thatProductobjects have a natural ordering defined by the class itself, rather than relying on an external comparator.Integer.compare(this.id, other.id): Returns a negative, zero, or positive number depending on howthis.idcompares toother.id, which is exactly whatTreeSetneeds to decide ordering.new TreeSet<>(): Since no comparator is passed, the set falls back to thecompareTo()method defined onProduct, sorting entries byidautomatically.
Exercise 19: The Duplicate Stripper
Problem Statement: Given an ArrayList containing hundreds of duplicate user IDs, write the most concise code possible using a HashSet to completely remove the duplicates.
Purpose: This exercise practices one of the most common real-world uses of a HashSet, deduplicating a list in a single line by taking advantage of the fact that a set can never contain duplicate elements.
Given Input: userIds = [101, 102, 101, 103, 102, 101, 104]
Expected Output: Unique user IDs: 4
▼ Hint
Pass the entire ArrayList directly into a HashSet constructor, new HashSet<>(userIds), which automatically discards any duplicate values as it copies the elements in.
▼ Solution & Explanation
Explanation:
new HashSet<>(userIds): Iterates through every element of the list and attempts to add each one to the set, with any repeated value simply being ignored since it already exists.- Single line deduplication: This one constructor call accomplishes what would otherwise require a manual loop checking each element against every previous one.
- Result: Out of seven total entries in
userIds, only four distinct values,101,102,103, and104, remain inuniqueIds.
Exercise 20: Finding Closest Match (floor and ceiling)
Problem Statement: Given a TreeSet of target scores [55, 65, 75, 85, 95], use floor() and ceiling() to find the closest matches for a query score of 80.
Purpose: This exercise practices floor() and ceiling() on a TreeSet, the set-based equivalent of the floorKey() and ceilingKey() methods explored earlier on TreeMap.
Given Input: targetScores = {55, 65, 75, 85, 95}, query = 80
Floor of 80: 75 Ceiling of 80: 85
▼ Hint
- Call
targetScores.floor(80), which returns the largest element less than or equal to80. - Call
targetScores.ceiling(80), which returns the smallest element greater than or equal to80. - Since
80itself is not present in the set, the two results will differ, one below and one above the query value.
▼ Solution & Explanation
Explanation:
targetScores.floor(80): Searches for the largest value not exceeding80, finding75since80itself is not a member of the set.targetScores.ceiling(80): Searches for the smallest value not smaller than80, finding85.- Exact match case: Had the query been exactly
75instead, bothfloor(75)andceiling(75)would have returned75directly, the same behavior seen earlier withfloorKey()andceilingKey()on aTreeMap.
Exercise 21: Strictly Higher/Lower (higher and lower)
Problem Statement: Given a TreeSet of auction bids, find the bid that is strictly higher than $150 and the bid strictly lower than $150 using higher() and lower().
Purpose: This exercise practices higher() and lower() on a TreeSet, which behave like ceiling() and floor() except that they always exclude an exact match, mirroring the higherKey() and lowerKey() methods seen earlier on TreeMap.
Given Input: bids = {80, 120, 150, 175, 220}, reference = 150
Lower than 150: 120 Higher than 150: 175
▼ Hint
- Call
bids.lower(150), which returns the largest element strictly less than150, skipping over150itself even though it is present in the set. - Call
bids.higher(150), which returns the smallest element strictly greater than150. - Compare this to what
floor(150)andceiling(150)would return, both of which would give back150directly since it exists in the set.
▼ Solution & Explanation
Explanation:
bids.lower(150): Even though150is a member of the set, this method deliberately skips it and returns120, the next value down.bids.higher(150): Similarly skips past150and returns175, the next value up.- Strict exclusion: The defining trait of
lower()andhigher()is that neither one will ever return the exact reference value, even when that value is present in the set.
Exercise 22: Mathematical Union and Difference
Problem Statement: Given two HashSet collections (Set A and Set B), write code to perform a mathematical Union (all unique elements from both) and a Difference (elements in A but not in B).
Purpose: This exercise practices two more of the standard set operations, addAll() for a union and removeAll() for a difference, rounding out the intersection operation covered in an earlier exercise.
Given Input: setA = {1, 2, 3, 4}, setB = {3, 4, 5, 6}
Union: [1, 2, 3, 4, 5, 6] Difference (A - B): [1, 2]
▼ Hint
- For the union, make a copy of
setA, then callcopy.addAll(setB), which merges in every element ofsetBwhile automatically skipping anything already present. - For the difference, make a separate copy of
setA, then callcopy.removeAll(setB), which strips out any element that also appears insetB. - Always operate on copies rather than the original sets, so
setAandsetBremain unchanged for both calculations.
▼ Solution & Explanation
Explanation:
union.addAll(setB): Adds every element ofsetBintounion, and since a set can never hold duplicates, elements like3and4that already existed simply stay as single entries.difference.removeAll(setB): Removes any element fromdifferencethat also appears insetB, leaving only the values unique tosetA.- Originals preserved: Because both operations were performed on separate copies,
setAandsetBremain exactly as they were defined.
Exercise 23: Overriding hashCode() and equals()
Problem Statement: Create a Book class with title and author. Override hashCode() and equals() so that a HashSet<Book> correctly identifies two different book objects with identical titles and authors as duplicates.
Purpose: This exercise reinforces why HashSet depends on hashCode() and equals() to detect duplicates, extending the same lesson seen earlier with HashMap keys to a set of custom objects.
Given Input: Book b1 = new Book("Dune", "Frank Herbert"); Book b2 = new Book("Dune", "Frank Herbert");
Expected Output: Set size after adding both: 1
▼ Hint
- Override
equals(Object obj)to compare both thetitleandauthorfields between the current object and the one being compared against. - Override
hashCode()usingObjects.hash(title, author), combining the same two fields used inequals(). - Without both overrides in place,
b1andb2would be treated as two separate objects despite having identical field values, since the defaultObjectbehavior compares memory references.
▼ Solution & Explanation
Explanation:
title.equals(other.title) && author.equals(other.author): Defines twoBookobjects as equal only when both their title and author match exactly.Objects.hash(title, author): Produces a hash code derived from the same two fields used inequals(), satisfying the requirement that equal objects must produce equal hash codes.- Result: Even though
b1andb2are two separate objects in memory, theHashSetnow recognizes them as duplicates and keeps only one, leaving the set’s size at1.
Exercise 24: Unique Word Counter
Problem Statement: Write a program that takes a long paragraph of text, splits it into individual words, cleans up punctuation, and uses a Set to count exactly how many unique words were used.
Purpose: This exercise practices combining string cleanup with a Set, showing how punctuation and casing must be normalized first, or otherwise identical words would be miscounted as distinct due to trailing symbols or mismatched case.
Given Input: String text = "The fox runs. The fox jumps! Does the fox run fast, or does the fox jump far?";
Expected Output: Unique word count: 11
▼ Hint
- Convert the whole string to lowercase first with
toLowerCase(), so that"The"and"the"are treated as the same word. - Use
text.split("\\s+")to break the text into words based on any whitespace. - For each word, strip out punctuation using
word.replaceAll("[^a-z]", ""), keeping only lowercase letters. - Add each cleaned word to a
HashSet<String>, then readsize()once every word has been processed.
▼ Solution & Explanation
Explanation:
text.toLowerCase(): Normalizes casing across the whole string first, so"The"and"the"collapse into the exact same word once compared.word.replaceAll("[^a-z]", ""): Removes anything that is not a lowercase letter, stripping periods, commas, exclamation marks, and question marks left attached to words after splitting.if (!cleaned.isEmpty()): Guards against adding an empty string to the set, which could otherwise happen if a token consisted entirely of punctuation.uniqueWords.size(): Since the set automatically discards repeats, its final size directly gives the count of distinct words used in the text.
Exercise 25: Stream API Integration
Problem Statement: Given a HashSet of integers, use Java Streams to filter out all even numbers, square the remaining odd numbers, and collect the final results directly into a sorted TreeSet.
Purpose: This exercise practices chaining filter() and map() on a Stream built from a Set, then collecting the results directly into a different set implementation, tying together sets and Streams in a single pipeline.
Given Input: numbers = {1, 2, 3, 4, 5, 6, 7, 8, 9}
Expected Output: [1, 9, 25, 49, 81]
▼ Hint
- Call
numbers.stream()to begin the pipeline. - Use
.filter(n -> n % 2 != 0)to keep only the odd numbers. - Use
.map(n -> n * n)to square each remaining number. - Finish with
.collect(Collectors.toCollection(TreeSet::new)), which gathers the results directly into a new, automatically sortedTreeSet.
▼ Solution & Explanation
Explanation:
.filter(n -> n % 2 != 0): Keeps only the numbers whose remainder when divided by2is not zero, discarding every even number from the stream..map(n -> n * n): Transforms each remaining odd number into its square, producing a new stream of squared values.Collectors.toCollection(TreeSet::new): Gathers the final stream values into aTreeSetspecifically, rather than the defaultHashSetthat a plainCollectors.toSet()would produce, guaranteeing the output stays sorted.

Leave a Reply