In this article we will examine an extremely scaled-down version of a real life problem that can be solved using Java Set. For appreciating the choices made in the sample program presented here, you need to understand the differences between HashSet, LinkedHashSet and TreeSet.
Problem Description
A web based startup wishes to reward (distribute bonus, if you like) some of its employees every year in order to maintain their morale. In the interest of fairness, no employee can be rewarded twice during the same year. Some of the employees may not be rewarded in a particular year. We have to write some Java code to print names of rewarded employees in multiple ways. The name of the startup and the criteria for selecting employees is unimportant for this problem.
More Details
Our first task is to –
- Model the startup and its employees
- Implement a reasonable criteria for selecting employees for rewards
Next, we have to implement mechanisms to print the names of rewarded employees in –
- Alphabetical order – this prevents any hard feelings among the employees and this order is generally deemed politically correct.
- Seniority order – seniority in the current context and for our purposes is determined by tenure in the startup. An employee who joined 10 years ago is deemed senior to another employee who joined 9 years ago, immaterial of their current designation.
- Random order – this is another politically correct way and prevents any hard feelings among employees as the names of the rewarded employees are printed in random order.
Solution
Modeling the Employee Data Structure
The problem statement calls out for us to order Employees based on their joining dates with the company and randomly. Since the problem does not explicitly ask to display the joining date, we can make a relative ordering among Employees by awarding them increasing IDs. Therefore, an Employee who joins early has a smaller ID number and those joining afterwards have a larger value. In addition to the IDs, we also need to store the name of the Employee because this is what will get printed.
Lastly, we need to make one more important decision regarding the Employee class – whether to have setters or to take the IDs and name as constructor parameter. Although there are pros and cons of both approaches, we will go with constructor with parameters because this will ensure that all Employee objects have the necessary information at the time of creation.
Here is the Employee class –
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 | class Employee { private String name; private int id; public Employee(String name, int id) { this.name = name; this.id = id; } public String getName() { return name; } public int getId() { return id; } } // Employee class |
Modeling the Startup class
A real life model of a startup company has to have a number of features to represent the relevant departments such as development, maintenance, sales, marketing, HR and so on. In this scaled down problem we are concerned with one and only one aspect – rewarding bonuses to some of the employees. Therefore, we only need to maintain the list of all employees and employees who will be given bonuses. Since no employee will be awarded bonus twice and some employees may not get rewarded, we need to maintain the rewarded employees as a Java Set. Further, since we need to record the seniority of employees based on their IDs we need to record their arrival order. The Java Set most suited for capturing arrival order is LinkedHashSet and this is what we use to record rewarded employees.
Here is a first-cut of the Startup class.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | class Startup { List<Employee> employees; Set<Employee> rewardedEmployees; public Startup() { employees = new ArrayList<Employee>(); // make this LinkedHashSet // so that we can record and preserve arrival order rewardedEmployees = new LinkedHashSet<Employee>(); } public void addEmployee(Employee asset) { this.employees.add(asset); } } // class |
Notice that as with Employee class, we have relied on overloaded constructors rather than setters. We have an addEmployee() method that gets called each time a new Employee joins the startup. This approach ensures that all objects have the information they need at the creation time itself.
We next need to model a few operations in our class. Specifically, we need to have methods to (arbitrarily at this point) select a few employees and to display their names in random, alphabetic and arrival order.
To select a few employees for giving bonuses we go through the ArrayList of Employees and select Employees with even ID. Since the ArrayList already stores all Employees in their arrival order we can bank upon this and store rewarded Employees as LinkedHashSet. To display the rewarded Employees in a random way we create a HashSet out of LinkedHashSet. For displaying the rewarded Employees in alphabetical order we need to store them in a TreeSet. A TreeSet needs to be informed of the criterion for comparing two objects. This is accomplished by the Comparator object. Therefore, we need to write a Comparator that determines relative ordering of Employees based on their names as follows –
1 2 3 4 5 6 7 8 | class NameComparator implements Comparator<Employee> { @Override public int compare(Employee e1, Employee e2) { return e1.getName().compareTo(e2.getName()); } } // Comparator |
We need to pass an instance of this Comparator to the TreeSet. The TreeSet will call compare method to determine relative ordering between two Employees.
Here is the complete Startup class with all the required data and operations.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 | class Startup { List<Employee> employees; Set<Employee> rewardedEmployees; public Startup() { employees = new ArrayList<Employee>(); // make this LinkedHashSet // so that we can record and preserve arrival order rewardedEmployees = new LinkedHashSet<Employee>(); } public void addEmployee(Employee asset) { this.employees.add(asset); } public void rewardAFew() { for (int index = 0; index < employees.size(); index++) { if (index % 2 == 0) rewardedEmployees.add(employees.get(index)); } } public Set<Employee> announceInSeniorityOrder() { return rewardedEmployees; } public Set<Employee> announceInAlphabeticalOrder() { TreeSet<Employee> rSet = new TreeSet<Employee>(new NameComparator()); rSet.addAll(rewardedEmployees); return rSet; } public Set<Employee> announceInRandomWay() { return new HashSet<Employee>(rewardedEmployees); } } // startup's structure |
Putting It All Together
Let us put all these components together in a single class (just for demo purposes, it’s not a good practice). Since we will call our code from main method which is static, we will make our classes static so that they can be accessed from the main method.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 | package codingraptor.setsdemo; import java.util.ArrayList; import java.util.Comparator; import java.util.HashSet; import java.util.LinkedHashSet; import java.util.List; import java.util.Set; import java.util.TreeSet; public class BasicSetDemo { static class Startup { List<Employee> employees; Set<Employee> rewardedEmployees; public Startup() { employees = new ArrayList<Employee>(); // make this LinkedHashSet // so that we can record and preserve arrival order rewardedEmployees = new LinkedHashSet<Employee>(); } public void addEmployee(Employee asset) { this.employees.add(asset); } public void rewardAFew() { for (int index = 0; index < employees.size(); index++) { if (index % 2 == 0) rewardedEmployees.add(employees.get(index)); } /*rewardedEmployees.addAll(employees);*/ } public Set<Employee> announceInSeniorityOrder() { return rewardedEmployees; } public Set<Employee> announceInAlphabeticalOrder() { TreeSet<Employee> rSet = new TreeSet<Employee>(new NameComparator()); rSet.addAll(rewardedEmployees); return rSet; } public Set<Employee> announceInRandomWay() { return new HashSet<Employee>(rewardedEmployees); } } // startup's structure static class Employee { private String name; private int id; public Employee(String name, int id) { this.name = name; this.id = id; } public String getName() { return name; } public int getId() { return id; } } // structure of Employee class static class NameComparator implements Comparator<Employee> { @Override public int compare(Employee e1, Employee e2) { return e1.getName().compareTo(e2.getName()); } } public static void main(String[] args) { Startup codingRaptor = new Startup(); codingRaptor.addEmployee(new Employee("AAA", 1)); codingRaptor.addEmployee(new Employee("BBB", 2)); codingRaptor.addEmployee(new Employee("XXX", 3)); codingRaptor.addEmployee(new Employee("DDD", 4)); codingRaptor.addEmployee(new Employee("FFF", 5)); codingRaptor.addEmployee(new Employee("CCC", 6)); codingRaptor.rewardAFew(); System.out.println("**************Rewarded employees announced randomly"); // announce rewarded employees in a politically correct manner for (Employee anEmployee : codingRaptor.announceInRandomWay()) { System.out.println(anEmployee.getName()); } System.out.println("**************Rewarded employees ordered by their IDs (seniority)"); // announce rewarded employees in order of presidency for (Employee anEmployee : codingRaptor.announceInSeniorityOrder()) { System.out.println(anEmployee.getName()); } System.out.println("**************Rewarded employees in alphabetic order"); // announce rewarded employees in alphabetical order for (Employee anEmployee : codingRaptor.announceInAlphabeticalOrder()) { System.out.println(anEmployee.getName()); } } } |
The output of this program is –
1 2 3 4 5 6 7 8 9 10 11 12 | **************Rewarded employees announced randomly FFF AAA XXX **************Rewarded employees ordered by their IDs (seniority) AAA XXX FFF **************Rewarded employees in alphabetic order AAA FFF XXX |
Note that we are able to display the rewarded employees in arrival order, random order and alphabetical order.
Having examined HashSet, LinkedHashSet, TreeSet in previous article and a scaled-down real life example in this article, we will move on to the other Java Set – EnumSet in next article.