Compares two Log Lines I am trying to compare two Log lines based on some conditions, in order to sort them:

Code:

public static final Comparator&lt;String&gt; HTMLcomparator = new Comparator&lt;String&gt;()
    {
        @Override
        public int compare(String line1, String line2)
        {
            HTMLLogLine htmlLogLine1 = new HTMLLogLine(line1);
            HTMLLogLine htmlLogLine2 = new HTMLLogLine(line2);
            int fullCompare = 0;

            String requestId1 = htmlLogLine1.getRequestId();
            String requestId2 = htmlLogLine2.getRequestId();

            if(requestId1 != null &amp;&amp; requestId2 != null)
            {
                fullCompare = requestId1.compareTo(requestId2);
            }
            else if(requestId1 == null &amp;&amp; requestId2 != null)
            {
                fullCompare = -1;
            }
            else if(requestId1 != null)
            {
                 fullCompare = 1;
            }
            else
            {
                fullCompare = 0;
            }

            if(fullCompare == 0)
            {
                String security1 = htmlLogLine1.getSecurity();
                String security2 = htmlLogLine2.getSecurity();
                if(security1 != null &amp;&amp; security2 != null)
                {
                    fullCompare = security1.compareTo(security2);
                }
                else if(security1 == null &amp;&amp; security2 != null)
                {
                    fullCompare = -1;
                }
                else if(security1 != null &amp;&amp; security2 == null)
                {
                    fullCompare = 1;
                }
                else
                {
                    fullCompare = 0;
                }
            }

            if(fullCompare == 0)
            {
                String scenario1 = htmlLogLine1.getScenario();
                String scenario2 = htmlLogLine2.getScenario();

                if(scenario1 != null &amp;&amp; scenario2 != null)
                {
                    fullCompare = scenario1.compareTo(scenario2);

                }
                else if(scenario1 == null &amp;&amp; scenario2 != null)
                {
                    fullCompare = -1;
                }
                else if(scenario1 != null)
                {
                    fullCompare = 1;
                }
                else
                {
                    fullCompare = 0;
                }
            }

            if(fullCompare == 0)
            {
                Optional&lt;Instant&gt; timestamp1 = htmlLogLine1.getTimestamp();
                Optional&lt;Instant&gt; timestamp2 = htmlLogLine2.getTimestamp();

                if(timestamp1.isPresent() &amp;&amp; timestamp2.isPresent())
                {
                    fullCompare = timestamp1.get().compareTo(timestamp2.get());
                }
                else if(!timestamp1.isPresent() &amp;&amp; timestamp2.isPresent())
                {
                    fullCompare = -1;
                }
                else if(timestamp1.isPresent() &amp;&amp; !timestamp2.isPresent())
                {
                    fullCompare = 1;
                }
                else
                {
                    fullCompare = 0;
                }

            }

            return fullCompare;
        }
    };


How can I reduce the duplicate code and make this compare method concise?
You can reduce the code duplication by moving the logic of comparing two values of which at least one is null to a separate method and call this method like this:

if (requestId1 != null &amp;&amp; requestId2 != null) {
    fullCompare = requestId1.compareTo(requestId2);
} else {
    fullCompare = compareWithNull(requestId1, requestId2);
}

//...

if (security1 != null &amp;&amp; security2 != null) {
    fullCompare = security1.compareTo(security2);
} else {
    fullCompare = compareWithNull(security1, security2);
}

//...


This reduces the code duplication, but you still have duplicate code, namely the procedure of checking whether both values are non-null and proceeding accordingly. So you could also move this logic into a separate method, for instance, into the compare(T, T) method of a separate comparator:

public int compare(T a, T b) {
    if (a == null) {
        return (b == null) ? 0 : -1;
    } else if (b == null) {
        return 1;
    } else {
        return a.compare(b);
    }
}


The good news is that you don't actually have to do this yourself, because since Java 8, there's a method Comparator.nullsFirst​(Comparator) that does exactly that: It returns a comparator that considers null less than non-null (and equal to null), and if both objects are non-null, compares them using the provided comparator. The code sample above is in fact a slightly modified copy from the source of Comparator.nullsFirst​(Comparator). Since you order non-null elements according to their natural ordering, you would just need to call Comparator.nullsFirst(Comparator.naturalOrder()).

So the first three comparison stages can be reduced to the functionality of these four comparators:

Comparator&lt;String&gt; nullsFirstStringComparator = Comparator.nullsFirst(Comparator.naturalOrder());

Comparator&lt;HTMLLogLine&gt; requestIdComparator = Comparator.comparing(HTMLLogLine::getRequestId, nullsFirstStringComparator);
Comparator&lt;HTMLLogLine&gt; securityComparator = Comparator.comparing(HTMLLogLine::getSecurity, nullsFirstStringComparator);
Comparator&lt;HTMLLogLine&gt; scenarioComparator = Comparator.comparing(HTMLLogLine::getScenario, nullsFirstStringComparator);


Judging by your comment, you already seem to be aware of the method Comparator.thenComparing(Comparator), so there's no need to explain how to chain those comparators using this method.

Unfortunately, the forth comparison stage does not fit into this pattern. Of course, you could ditch the nulls-first comparator and write your own generalized version of it which, instead of comparing the two values with null, checks a given Predicate against the values:

class CustomComparator&lt;T&gt; implements Comparator&lt;T&gt; {
    Predicate&lt;? super T&gt; predicate;
    Comparator&lt;? super T&gt; otherComparator;

    @Override
    public int compare(T o1, T o2) {
        if (predicate.test(o1)) {
            return (predicate.test(o2)) ? 0 : -1;
        } else if (predicate.test(o2)) {
            return 1;
        } else {
            return otherComparator.compare(o1, o2);
        }
    }
}


But I think there would only be a point in doing that if you need it more than once, because otherwise, it would not really be a remedy for code duplication, but just unnecessary code (since you don't really need it for the first three cases due to the pre-existing method Comparator.nullsFirst(Comparator)).