View Javadoc
1   /*
2    * Licensed to the Apache Software Foundation (ASF) under one or more
3    * contributor license agreements.  See the NOTICE file distributed with
4    * this work for additional information regarding copyright ownership.
5    * The ASF licenses this file to You under the Apache License, Version 2.0
6    * (the "License"); you may not use this file except in compliance with
7    * the License.  You may obtain a copy of the License at
8    *
9    *      https://www.apache.org/licenses/LICENSE-2.0
10   *
11   * Unless required by applicable law or agreed to in writing, software
12   * distributed under the License is distributed on an "AS IS" BASIS,
13   * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
14   * See the License for the specific language governing permissions and
15   * limitations under the License.
16   */
17  package org.apache.commons.collections4.comparators;
18  
19  import java.io.Serializable;
20  import java.util.Comparator;
21  import java.util.Objects;
22  
23  import org.apache.commons.collections4.ComparatorUtils;
24  
25  /**
26   * A Comparator that will compare nulls to be either lower or higher than
27   * other objects.
28   *
29   * @param <E> The type of objects compared by this comparator
30   * @since 2.0
31   */
32  public class NullComparator<E> implements Comparator<E>, Serializable {
33  
34      /** Serialization version. */
35      private static final long serialVersionUID = -5820772575483504339L;
36  
37      /**
38       * The comparator to use when comparing two non-{@code null} objects.
39       */
40      private final Comparator<? super E> nonNullComparator;
41  
42      /**
43       * Specifies whether a {@code null} are compared as higher than
44       * non-{@code null} objects.
45       */
46      private final boolean nullsAreHigh;
47  
48      /**
49       * Construct an instance that sorts {@code null} higher than any
50       * non-{@code null} object it is compared with. When comparing two
51       * non-{@code null} objects, the {@link ComparableComparator} is
52       * used.
53       */
54      public NullComparator() {
55          this(ComparatorUtils.NATURAL_COMPARATOR, true);
56      }
57  
58      /**
59       * Construct an instance that sorts {@code null} higher or lower than
60       * any non-{@code null} object it is compared with.  When comparing
61       * two non-{@code null} objects, the {@link ComparableComparator} is
62       * used.
63       *
64       * @param nullsAreHigh A {@code true} value indicates that
65       * {@code null} should be compared as higher than a
66       * non-{@code null} object.  A {@code false} value indicates
67       * that {@code null} should be compared as lower than a
68       * non-{@code null} object.
69       */
70      public NullComparator(final boolean nullsAreHigh) {
71          this(ComparatorUtils.NATURAL_COMPARATOR, nullsAreHigh);
72      }
73  
74      /**
75       * Construct an instance that sorts {@code null} higher than any
76       * non-{@code null} object it is compared with.  When comparing two
77       * non-{@code null} objects, the specified {@link Comparator} is
78       * used.
79       *
80       * @param nonNullComparator The comparator to use when comparing two
81       * non-{@code null} objects.  This argument cannot be
82       * {@code null}
83       * @throws NullPointerException if {@code nonNullComparator} is
84       * {@code null}
85       */
86      public NullComparator(final Comparator<? super E> nonNullComparator) {
87          this(nonNullComparator, true);
88      }
89  
90      /**
91       * Construct an instance that sorts {@code null} higher or lower than
92       * any non-{@code null} object it is compared with.  When comparing
93       * two non-{@code null} objects, the specified {@link Comparator} is
94       * used.
95       *
96       * @param nonNullComparator The comparator to use when comparing two
97       * non-{@code null} objects. This argument cannot be
98       * {@code null}
99       * @param nullsAreHigh A {@code true} value indicates that
100      * {@code null} should be compared as higher than a
101      * non-{@code null} object.  A {@code false} value indicates
102      * that {@code null} should be compared as lower than a
103      * non-{@code null} object.
104      * @throws NullPointerException if {@code nonNullComparator} is
105      * {@code null}
106      */
107     public NullComparator(final Comparator<? super E> nonNullComparator, final boolean nullsAreHigh) {
108         this.nonNullComparator = Objects.requireNonNull(nonNullComparator, "nonNullComparator");
109         this.nullsAreHigh = nullsAreHigh;
110     }
111 
112     /**
113      * Perform a comparison between two objects.  If both objects are
114      * {@code null}, a {@code 0} value is returned.  If one object
115      * is {@code null} and the other is not, the result is determined on
116      * whether the Comparator was constructed to have nulls as higher or lower
117      * than other objects.  If neither object is {@code null}, an
118      * underlying comparator specified in the constructor (or the default) is
119      * used to compare the non-{@code null} objects.
120      *
121      * @param o1  The first object to compare
122      * @param o2  The object to compare it to.
123      * @return {@code -1} if {@code o1} is "lower" than (less than,
124      * before, etc.) {@code o2}; {@code 1} if {@code o1} is
125      * "higher" than (greater than, after, etc.) {@code o2}; or
126      * {@code 0} if {@code o1} and {@code o2} are equal.
127      */
128     @Override
129     public int compare(final E o1, final E o2) {
130         if (o1 == o2) {
131             return 0;
132         }
133         if (o1 == null) {
134             return nullsAreHigh ? 1 : -1;
135         }
136         if (o2 == null) {
137             return nullsAreHigh ? -1 : 1;
138         }
139         return nonNullComparator.compare(o1, o2);
140     }
141 
142     /**
143      * Determines whether the specified object represents a comparator that is
144      * equal to this comparator.
145      *
146      * @param obj  The object to compare this comparator with.
147      * @return {@code true} if the specified object is a NullComparator
148      * with equivalent {@code null} comparison behavior
149      * (i.e. {@code null} high or low) and with equivalent underlying
150      * non-{@code null} object comparators.
151      */
152     @Override
153     public boolean equals(final Object obj) {
154         if (obj == null) {
155             return false;
156         }
157         if (obj == this) {
158             return true;
159         }
160         if (!obj.getClass().equals(this.getClass())) {
161             return false;
162         }
163 
164         final NullComparator<?> other = (NullComparator<?>) obj;
165 
166         return nullsAreHigh == other.nullsAreHigh &&
167                 nonNullComparator.equals(other.nonNullComparator);
168     }
169 
170     /**
171      * Implement a hash code for this comparator that is consistent with
172      * {@link #equals(Object)}.
173      *
174      * @return A hash code for this comparator.
175      */
176     @Override
177     public int hashCode() {
178         return (nullsAreHigh ? -1 : 1) * nonNullComparator.hashCode();
179     }
180 }