Benefits of factoring a new class vs string values for elements in a Set
<p>This is more of a design question with implications for code simplicity vs. performance.</p>

<p>Lets say you want to make sure a set of values for a given user id are the same between two systems. The example here is to check that a student id has the same number of course enrollments in System A and System B.</p>

<p>For this we create:</p>

<pre><code>List&lt;String&gt; studentList = new ArrayList&lt;String&gt;();
Set&lt;String&gt; sysAEnrollments = new HashSet&lt;String&gt;();
Set&lt;String&gt; sysBEnrollments = new HashSet&lt;String&gt;();
private Map&lt;String, String&gt; badEnrollList = new HashMap&lt;String, String&gt;();
</code></pre>

<p>And fill them appropriately, given a list of student ids(studentList):</p>

<pre><code>studentList = getCurrentStudentList();

for (String id : studentList){
   sysAEnrollments = getSysAEnrollments(id);
   sysBEnrollments = getSysBEnrollments(id);
   if (!sysAEnrollments.containsAll(sysBEnrollments)){
      badEnrollList.put(id, getBadEnrollmentsById(id, sysAEnrollments, sysBEnrollments));
   }
}
</code></pre>

<p><strong>Question:</strong> What should the method '<em>getBadEnrollmentsById</em>' return?</p>

<p>Either a concatenated string with enough meaning so it can just be printed out.
Or have a new object, for example another collection with the list of course ids that could be used for further processing but harder to use for printed output.</p>

<p>Is it worth designing thoroughly all expected objects or replace some of them with concatenated strings for clarity and performance?</p>

<p><strong>NOTES:</strong></p>

<ul>
<li>System A is preferred as the authoritative source</li>
<li>Output from <em>getBadEnrollmentsById</em> should have all courses and flag those missing in system B.</li>
</ul>

<p><strong>PROPOSED SOLUTION: (2012-SEP-14)</strong></p>

<p><strong>EDIT (2012-SEP-17): Updated the Course class to include hashCode and equals</strong></p>

<p>As suggested by user351721 I continued modelling the remaining objects that match the expected results/requirements.
Slight changes made a big difference and allowed me to go over this design flaw and finish with the implementation.</p>

<p>The revised collections are:</p>

<pre><code>List&lt;String&gt; studentList = new ArrayList&lt;String&gt;();
Enrollment sysAEnrollments;
Enrollment sysBEnrollments;
Map&lt;String, List&lt;String&gt;&gt; badEnrollList = new HashMap&lt;String, List&lt;String&gt;&gt;();
</code></pre>

<p>And we populate the Enrollments:</p>

<pre><code>for (String id : studentList){
    sysAEnrollments = getSysAEnrollments(id);
    sysBEnrollments = getSysBEnrollments(id);
    if (!sysAEnrollments.getCourses().containsAll(sysBEnrollments.getCourses())){
        List&lt;String&gt; missingCourses = getProblemEnrollmentListById(id, sysAEnrollments, sysBEnrollments);
        badEnrollList.put(id, missingCourses);
    }
}
</code></pre>

<p>So for now the output can be printed from badEnrollList by getting at each ArrayList and printing the course names. A course name with a * will mean that it's missing in sysB.</p>

<p>The Enrollment class looks like this:</p>

<pre><code>public class Enrollment {
    private Set&lt;Course&gt; courses = new HashSet&lt;Course&gt;();
    public void setCourses(Set&lt;Course&gt; courses){
        this.courses = courses;
    }
    public Set&lt;Course&gt; getCourses(){
        return this.courses;
    }
}
</code></pre>

<p>And the Course class ended up like this:</p>

<pre><code>public class Course {
    private String id;
    private String name;

    public String getId() {
        return id;
    }
    public void setId(final String id) {
        this.id = id;
    }
    public String getName() {
        return name;
    }
    public void setName(final String name) {
        this.name = name;
    }

    // Must override hashCode() and equals()
    @Override
    public boolean equals(Object o){
        if (o == this)
            return true;
        if (!(o instanceof Course))
            return false;
        Course c = (Course) o;
        return c.id.equals(this.id) &amp;&amp; c.name.equals(this.name);
    }

    @Override 
    public int hashCode(){
        // Magic numbers as shown on Joshua Bloch's book "Effective Java" 2nd Edition, p.48
        int result = 17;
        result = 31 * this.id.hashCode();
        result = 31 * this.name.hashCode();
        return result;
    }   
}
</code></pre>

<p>The changes might look subtle but the important clue is that Enrollments are not a collection of strings, Enrollments are a collection of Courses AND each Course has a name and a availability property. They don't seem to do much but by using them I am defining the objects that I'm working with and documenting how these classes can be reused in the future.</p>
