## Sample 5: Repeating Record Sections

[Sample 5 Source Code: sample5.java](./src/main/java/Sample5.java)

When a section in a SMF record can be repeated, the sections will be returned in a `List<T>`.
Sometimes a record will have no instances of a particular section - in that case an empty list is returned. 

Sample 5 shows how to get multiple sections from a record and generate a report based on the SMF type 30 EXCP Section.

The report lists job steps with a STEPLIB entry in the EXCP sections.
The program ignores jobs where the job name begins with the userid.
Each combination of Jobname, Step Number, Step Name and Program Name is listed only once.

(This sample is used to demonstrate various techniques. It is not intended to imply that there is any problem with jobs using a STEPLIB.)

### Processing the Data

Reading the data is done in the same way as [Sample 1](Sample1.md).

There is an additional filtering step to ignore any jobs where the job name starts with the userid:

```
if (!r30.identificationSection().smf30jbn()
    .startsWith(r30.identificationSection().smf30rud()))
{
    ...
}
```

Then we loop over the EXCP Sections:
```
for (ExcpSection excp : r30.excpSections())
{
   ...
}
```
If the record does not have any EXCP sections the excpSections() method returns an empty list. We don't have to specifically check for that - it just means the loop will be processed zero times.

For each EXCP section, we check whether the DDNAME is "STEPLIB". If it is we attempt to add an entry to the Job-Step-Program Set. If there is a duplicate entry the add(...) method returns false and the entry isn't added, we can just ignore that.

```
if (excp.smf30ddn().equals("STEPLIB"))
{
    jobStepPrograms.add(new JobStepProgram(r30));    
}
```

#### Recognising Duplicates

To eliminate duplicates, we collect the entries in a `Set<T>`. 
A Set is a collection which does not allow duplicate entries. 
We need to create a class to contain the job/step/program information, and provide `hashCode()` and `equals(Object)` methods which the Set uses to find entries and test for equality.

Exactly the same code is used to create a complex key for a HashMap. You can use this technique to summarize based on more complex keys than the simple String used in [Sample 4](./Sample4.md).

```
private static class JobStepProgram
{            
    private String jobname;
    private int stepnumber;
    private String stepname;
    private String programname;      
...
    @Override
    public int hashCode() {
        return Objects.hash(jobname, programname, stepname, stepnumber);
    }

    @Override
    public boolean equals(Object obj) {
        if (this == obj)
            return true;
        if (obj == null)
            return false;
        if (getClass() != obj.getClass())
            return false;
        JobStepProgram other = (JobStepProgram) obj;
        return Objects.equals(jobname, other.jobname)
            && Objects.equals(programname, other.programname) 
            && Objects.equals(stepname, other.stepname)
            && stepnumber == other.stepnumber;
    }
...
}
```     

##### A short discussion on hashcode() and equals(Object) #####

The `equals(Object obj)` method needs to compare all the properties of the object (jobname, step number, step name and program name) that determine whether the objects are equal.

The most important rule for `hashcode()` is that objects that are equal must return the same hash code. Otherwise collections like HashSet and HashMap will not work correctly. This means that if you provide an equals() method, you **must** also provide a hashcode() method.

The second rule is that objects that are **not** equal **should** return different hash codes. It is not absolutely required (and not even possible for every instance of every object) but if hash collisions are frequent it can badly affect performance of the hash based collections.

The methods shown were generated by Eclipse. Equals compares the fields in the class. The Objects.hash(...) method combines hashes from the same fields into a new hash using a method that should be resistant to collisions.

This is a bare bones discussion of hashcode() and equals(), there is plenty of Java books and documentation that goes into greater depth.

### Producing the Output

Again, Java Streams provides a neat way to process the data for output. We defined methods in the JobStepProgram class to access the fields, this means that we can sort using the method names in the comparing(...) statements.   

```
jobStepPrograms.stream()
    .sorted(
        comparing(JobStepProgram::getJobName)
        .thenComparing(JobStepProgram::getStepNumber)
        .thenComparing(JobStepProgram::getStepName)
        .thenComparing(JobStepProgram::getProgramName))
    .forEachOrdered( entry ->
        System.out.format("%-10s %6d  %-10s %-10s%n",
            entry.getJobName(),
            entry.getStepNumber(),
            entry.getStepName(),
            entry.getProgramName()));
```

[Sample 5 Source Code: sample5.java](./src/main/java/Sample5.java)