changeset 6373:197bf7b0507d

Additional utils and javadoc Reviwed-by: bgoetz, mduigou
author henryjen
date Mon, 05 Nov 2012 14:11:21 -0800
parents 0cf151046f3e
children 425d175eca55
files src/share/classes/java/util/Comparators.java test-ng/tests/org/openjdk/tests/java/util/ComparatorsTest.java
diffstat 2 files changed, 300 insertions(+), 136 deletions(-) [+]
line wrap: on
line diff
--- a/src/share/classes/java/util/Comparators.java	Mon Nov 05 21:22:22 2012 +0100
+++ b/src/share/classes/java/util/Comparators.java	Mon Nov 05 14:11:21 2012 -0800
@@ -31,13 +31,26 @@
 import java.util.functions.Mapper;
 
 /**
- * Comparators
+ * This class consists of {@code static} utility methods for comparators. Mostly
+ * factory method that returns a {@code Comparator}.
+ *
+ * <p>The methods of this class throw a {@link java.lang.NullPointerException
+ * NullPointerException} if parameters provided to them are {@code null} unless
+ * otherwise noted.
+ *
+ * @see Comparator
+ * @since 1.8
  */
 public class Comparators {
     private Comparators() {
         throw new AssertionError("no instances");
     }
 
+    /**
+     * Compares {@code Comparable} objects in natural order.
+     *
+     * @see Comparable
+     */
     private static class NaturalOrderComparator
         implements Comparator<Comparable<Object>>, Serializable {
 
@@ -53,166 +66,188 @@
         private Object readResolve() { return INSTANCE; }
     }
 
-    private static class IntMapperComparator<T>
-        implements Comparator<T>, Serializable {
-        private static final long serialVersionUID = -5450968559910565116L;
-
-        private final IntMapper<? super T> mapper;
-
-        private IntMapperComparator(IntMapper<? super T> mapper) {
-            Objects.requireNonNull(mapper);
-            this.mapper = mapper;
-        }
-
-        @Override
-        public int compare(T c1, T c2) {
-            return Integer.compare(mapper.map(c1), mapper.map(c2));
-        }
-    }
-
-    private static class DoubleMapperComparator<T>
-        implements Comparator<T>, Serializable {
-        private static final long serialVersionUID = -5734087649067745165L;
-
-        private final DoubleMapper<? super T> mapper;
-
-        private DoubleMapperComparator(DoubleMapper<? super T> mapper) {
-            Objects.requireNonNull(mapper);
-            this.mapper = mapper;
-        }
-
-        @Override
-        public int compare(T c1, T c2) {
-            return Double.compare(mapper.map(c1), mapper.map(c2));
-        }
-    }
-
-    private static class LongMapperComparator<T>
-        implements Comparator<T>, Serializable {
-        private static final long serialVersionUID = 915311427971695245L;
-
-        private final LongMapper<? super T> mapper;
-
-        private LongMapperComparator(LongMapper<? super T> mapper) {
-            Objects.requireNonNull(mapper);
-            this.mapper = mapper;
-        }
-
-        @Override
-        public int compare(T c1, T c2) {
-            return Long.compare(mapper.map(c1), mapper.map(c2));
-        }
-    }
-
-    private static class MappingKeyComparator<K extends Comparable<? super K>, V>
-        implements Comparator<Mapping<K,V>>, Serializable {
-
-        // FIXME needs to be calculated.
-        private static final long serialVersionUID = -1;
-
-        private static final MappingKeyComparator<? extends Comparable<?>,?> INSTANCE = new MappingKeyComparator<>();
-
-        @Override
-        public int compare(Mapping<K,V> c1, Mapping<K,V> c2) {
-            return c1.getKey().compareTo(c2.getKey());
-        }
-
-        private Object readResolve() { return INSTANCE; }
-    }
-
-    private static class MappingValueComparator<K, V extends Comparable<? super V>>
-        implements Comparator<Mapping<K,V>>, Serializable {
-
-        // FIXME needs to be calculated.
-        private static final long serialVersionUID = -1;
-
-        private static final MappingValueComparator<?,? extends Comparable<?>> INSTANCE = new MappingValueComparator<>();
-
-        @Override
-        public int compare(Mapping<K,V> c1, Mapping<K,V> c2) {
-            return c1.getValue().compareTo(c2.getValue());
-        }
-
-        private Object readResolve() { return INSTANCE; }
-    }
-
-    private static class MapperComparator<T, U extends Comparable<? super U>>
-        implements Comparator<T>, Serializable {
-
-        private static final long serialVersionUID = 8900536460967781434L;
-
-        private final Mapper<? extends U, ? super T> mapper;
-
-        private MapperComparator(Mapper<? extends U, ? super T> mapper) {
-            Objects.requireNonNull(mapper);
-            this.mapper = mapper;
-        }
-
-        @Override
-        public int compare(T c1, T c2) {
-            return mapper.map(c1).compareTo(mapper.map(c2));
-        }
-    }
-
-    private static class ComposedComparator<T>
-        implements Comparator<T>, Serializable {
-
-        private static final long serialVersionUID = -3088981872625314196L;
-
-        private final Comparator<? super T> first;
-        private final Comparator<? super T> second;
-
-        private ComposedComparator(Comparator<? super T> first, Comparator<? super T> second) {
-            this.first = Objects.requireNonNull(first);
-            this.second = Objects.requireNonNull(second);
-        }
-
-        @Override
-        public int compare(T c1, T c2) {
-            int res = first.compare(c1, c2);
-            return (res != 0) ? res : second.compare(c1, c2);
-        }
-    }
-
-    // Included for convenience, duplicates Collections functionality
+    /**
+     * Returns a comparator that imposes the reverse of the <em>natural
+     * ordering</em>.
+     *
+     * The returned comparator is serializable.
+     *
+     * @return A comparator that imposes the reverse of the <i>natural
+     *         ordering</i> on a collection of objects that implement
+     *         the <tt>Comparable</tt> interface.
+     * @see Comparable
+     */
     public static <T extends Comparable<? super T>> Comparator<T> reverseOrder() {
         return Collections.reverseOrder();
     }
 
-    // Included for convenience, duplicates Collections functionality
+    /**
+     * Returns a comparator that imposes the reverse ordering of the specified
+     * comparator.  If the specified comparator is {@code null}, this method
+     * throws NullPointerExcetion.
+     *
+     * <p>The returned comparator is serializable (assuming the specified
+     * comparator is also serializable).
+     *
+     * @param cmp a comparator who's ordering is to be reversed by the returned
+     * comparator
+     * @return A comparator that imposes the reverse ordering of the
+     *         specified comparator.
+     */
     public static <T> Comparator<T> reverseOrder(Comparator<T> cmp) {
+        Objects.requireNonNull(cmp);
         return Collections.reverseOrder(cmp);
     }
 
+    /**
+     * Get a Comparator compares Comparable type in natural order.
+     *
+     * @param <T> Comparable type
+     */
     public static <T extends Comparable<? super T>> Comparator<T> naturalOrder() {
         return (Comparator<T>) NaturalOrderComparator.INSTANCE;
     }
 
+    /**
+     * A comparator compares Mapping in natural order on Key.
+     *
+     * @param <K> Comparable Key type
+     * @param <V> Value type
+     */
     public static <K extends Comparable<? super K>, V> Comparator<Mapping<K,V>> naturalOrderKeys() {
-        return (Comparator<Mapping<K,V>>) MappingKeyComparator.INSTANCE;
+        return (Comparator<Mapping<K, V>> & Serializable)
+            (c1, c2) -> c1.getKey().compareTo(c2.getKey());
     }
 
+    /**
+     * A comparator compares Mapping in natural order on Value.
+     *
+     * @param <K> Key type
+     * @param <V> Comparable Value type
+     */
     public static <K, V extends Comparable<? super V>> Comparator<Mapping<K,V>> naturalOrderValues() {
-        return (Comparator<Mapping<K,V>>) MappingValueComparator.INSTANCE;
+        return (Comparator<Mapping<K, V>> & Serializable)
+            (c1, c2) -> c1.getValue().compareTo(c2.getValue());
     }
 
-    public static <T, U extends Comparable<? super U>> Comparator<T> comparing(Mapper<? extends U, ? super T> mapper) {
-        return new MapperComparator<>(mapper);
+    /**
+     * A comparator compares Mapping by Key using the given Comparator.
+     *
+     * <p>The returned comparator is serializable assuming the specified
+     * comparators are also serializable.
+     *
+     * @param <K> Key type
+     * @param <V> Value type
+     * @param cmp The key comparator
+     */
+    public static <K, V> Comparator<Mapping<K, V>> byKey(Comparator<? super K> cmp) {
+        Objects.requireNonNull(cmp);
+        return (Comparator<Mapping<K, V>> & Serializable)
+            (c1, c2) -> cmp.compare(c1.getKey(), c2.getKey());
     }
 
-    public static <T> Comparator<T> comparing(IntMapper<? super T> mapper) {
-        return new IntMapperComparator<>(mapper);
+    /**
+     * A comparator compares Mapping by Value using the given Comparator.
+     *
+     * @param <K> Key type
+     * @param <V> Value type
+     * @param cmp The value comparator
+     */
+    public static <K, V> Comparator<Mapping<K, V>> byValue(Comparator<? super V> cmp) {
+        Objects.requireNonNull(cmp);
+        return (Comparator<Mapping<K, V>> & Serializable)
+            (c1, c2) -> cmp.compare(c1.getValue(), c2.getValue());
     }
 
-    public static <T> Comparator<T> comparing(LongMapper<? super T> mapper) {
-        return new LongMapperComparator<>(mapper);
+    /**
+     * Accepts a function that extracts a Comparable sort key from a type T, and
+     * returns a {@code Comparator<T>} that compares by that sort key.  For
+     * example, if a class Person has a String-valued getter getLastName, then
+     * {@code comparing(Person::getLastName)} would return a
+     * {@code Comparator<Person>} that compares Person objects by their last
+     * name.
+     *
+     * @param <T> the original element type
+     * @param <U> the Comparable type for comparison
+     * @param mapper the function used to extract the Comparable sort key
+     */
+    public static <T, U extends Comparable<? super U>> Comparator<T> comparing(Mapper<? extends U, ? super T> mapper) {
+        Objects.requireNonNull(mapper);
+        return (Comparator<T> & Serializable)
+            (c1, c2) -> mapper.map(c1).compareTo(mapper.map(c2));
     }
 
-    public static<T> Comparator<T> comparing(DoubleMapper<? super T> mapper) {
-        return new DoubleMapperComparator<>(mapper);
+    /**
+     * Accepts a function that extracts an {@code int} value from a type T, and
+     * returns a {@code Comparator<T>} that compares by that value.
+     *
+     * <p>The returned comparator is serializable assuming the specified
+     * mapper is also serializable.
+     *
+     * @see #comparing(Mapper)
+     * @param <T> the original element type
+     * @param mapper the function used to extract the integer value
+     */
+    public static <T> Comparator<T> comparing(IntMapper<? super T> mapper) {
+        Objects.requireNonNull(mapper);
+        return (Comparator<T> & Serializable)
+            (c1, c2) -> Integer.compare(mapper.map(c1), mapper.map(c2));
     }
 
+    /**
+     * Accepts a function that extracts a {@code long} value from a type T, and
+     * returns a {@code Comparator<T>} that compares by that value.
+     *
+     * <p>The returned comparator is serializable assuming the specified
+     * mapper is also serializable.
+     *
+     * @see #comparing(Mapper)
+     * @param <T> the original element type
+     * @param mapper the function used to extract the long value
+     */
+    public static <T> Comparator<T> comparing(LongMapper<? super T> mapper) {
+        Objects.requireNonNull(mapper);
+        return (Comparator<T> & Serializable)
+            (c1, c2) -> Long.compare(mapper.map(c1), mapper.map(c2));
+    }
+
+    /**
+     * Accepts a function that extracts a {@code double} value from a type T,
+     * and returns a {@code Comparator<T>} that compares by that value.
+     *
+     * <p>The returned comparator is serializable assuming the specified
+     * mapper is also serializable.
+     *
+     * @see #comparing(Mapper)
+     * @param <T> the original element type
+     * @param mapper the function used to extract the double value
+     */
+    public static<T> Comparator<T> comparing(DoubleMapper<? super T> mapper) {
+        Objects.requireNonNull(mapper);
+        return (Comparator<T> & Serializable)
+            (c1, c2) -> Double.compare(mapper.map(c1), mapper.map(c2));
+    }
+
+    /**
+     * Construct a lexicographic order from two Comparators.  For example, if
+     * you have comparators byLastName and byFirstName, each of type
+     * {@code Comparator<Person>}, then {@code compose(byLastName, byFirstName)}
+     * creates a {@code Comparator<Person>} which sorts by last name, and for
+     * equal last names sorts by first name.
+     *
+     * <p>The returned comparator is serializable assuming the specified
+     * comparators are also serializable.
+     *
+     * @param <T> the element type to be compared
+     * @param first the first comparator
+     * @param second the secondary comparator used when equals on the first
+     */
     public static<T> Comparator<T> compose(Comparator<? super T> first, Comparator<? super T> second) {
-        return new ComposedComparator<>(first, second);
+        Objects.requireNonNull(first);
+        Objects.requireNonNull(second);
+        return (Comparator<T> & Serializable) (c1, c2) -> {
+            int res = first.compare(c1, c2);
+            return (res != 0) ? res : second.compare(c1, c2);
+        };
     }
 }
--- a/test-ng/tests/org/openjdk/tests/java/util/ComparatorsTest.java	Mon Nov 05 21:22:22 2012 +0100
+++ b/test-ng/tests/org/openjdk/tests/java/util/ComparatorsTest.java	Mon Nov 05 14:11:21 2012 -0800
@@ -26,6 +26,7 @@
 
 import java.util.Comparator;
 import java.util.Comparators;
+import java.util.Mapping;
 import org.testng.annotations.Test;
 
 import java.util.functions.DoubleMapper;
@@ -34,6 +35,7 @@
 import java.util.functions.Mapper;
 
 import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
 
 /**
  * Unit tests for helper methods in Comparators
@@ -111,7 +113,7 @@
 
         assertComparisons(things, comp, comparisons);
     }
-    
+
     public void testDoubleComparator() {
         Thing[] things = new Thing[doubleValues.length];
         for (int i=0; i<doubleValues.length; i++)
@@ -145,4 +147,131 @@
 
         assertComparisons(stringValues, comp, comparisons);
     }
+
+    public void testReverseComparator() {
+        Comparator<String> cmpr = Comparators.reverseOrder();
+        Comparator<String> cmp = cmpr.reverse();
+
+        assertEquals(cmp.reverse(), cmpr);
+        assertEquals(0, cmp.compare("a", "a"));
+        assertEquals(0, cmpr.compare("a", "a"));
+        assertTrue(cmp.compare("a", "b") < 0);
+        assertTrue(cmpr.compare("a", "b") > 0);
+        assertTrue(cmp.compare("b", "a") > 0);
+        assertTrue(cmpr.compare("b", "a") < 0);
+    }
+
+    public void testReverseComparator2() {
+        Comparator<String> cmp = (s1, s2) -> s1.length() - s2.length();
+        Comparator<String> cmpr = cmp.reverse();
+
+        assertEquals(cmpr.reverse(), cmp);
+        assertEquals(0, cmp.compare("abc", "def"));
+        assertEquals(0, cmpr.compare("abc", "def"));
+        assertTrue(cmp.compare("abcd", "def") > 0);
+        assertTrue(cmpr.compare("abcd", "def") < 0);
+        assertTrue(cmp.compare("abc", "defg") < 0);
+        assertTrue(cmpr.compare("abc", "defg") > 0);        
+    }
+
+    public void testComposeComparator() {
+        // Longer string in front
+        Comparator<String> first = (s1, s2) -> s2.length() - s1.length();
+        Comparator<String> second = Comparators.naturalOrder();
+        Comparator<String> composed = Comparators.compose(first, second);
+
+        assertTrue(composed.compare("abcdefg", "abcdef") < 0);
+        assertTrue(composed.compare("abcdef", "abcdefg") > 0);
+        assertTrue(composed.compare("abcdef", "abcdef") == 0);
+        assertTrue(composed.compare("abcdef", "ghijkl") < 0);
+        assertTrue(composed.compare("ghijkl", "abcdefg") > 0);
+    }
+
+    private<K, V> void assertComparison(K k1, V v1, K k2, V v2,
+                                        Comparator<Mapping<K, V>> ck,
+                                        Comparator<Mapping<K, V>> cv) {
+        final Mapping<K, V> p11 = new Mapping.MappingValue<>(k1, v1);
+        final Mapping<K, V> p12 = new Mapping.MappingValue<>(k1, v2);
+        final Mapping<K, V> p21 = new Mapping.MappingValue<>(k2, v1);
+        final Mapping<K, V> p22 = new Mapping.MappingValue<>(k2, v2);
+
+        assertTrue(ck.compare(p11, p11) == 0);
+        assertTrue(ck.compare(p12, p11) == 0);
+        assertTrue(ck.compare(p11, p12) == 0);
+        assertTrue(ck.compare(p12, p22) < 0);
+        assertTrue(ck.compare(p12, p21) < 0);
+        assertTrue(ck.compare(p21, p11) > 0);
+        assertTrue(ck.compare(p21, p12) > 0);
+
+        assertTrue(cv.compare(p11, p11) == 0);
+        assertTrue(cv.compare(p12, p11) > 0);
+        assertTrue(cv.compare(p11, p12) < 0);
+        assertTrue(cv.compare(p12, p22) == 0);
+        assertTrue(cv.compare(p12, p21) > 0);
+        assertTrue(cv.compare(p21, p11) == 0);
+        assertTrue(cv.compare(p21, p12) < 0);
+
+        Comparator<Mapping<K, V>> cmp = Comparators.compose(ck, cv);
+        assertTrue(cmp.compare(p11, p11) == 0);
+        assertTrue(cmp.compare(p12, p11) > 0);
+        assertTrue(cmp.compare(p11, p12) < 0);
+        assertTrue(cmp.compare(p12, p22) < 0);
+        assertTrue(cmp.compare(p12, p21) < 0);
+        assertTrue(cmp.compare(p21, p11) > 0);
+        assertTrue(cmp.compare(p21, p12) > 0);
+
+        cmp = Comparators.compose(cv, ck);
+        assertTrue(cmp.compare(p11, p11) == 0);
+        assertTrue(cmp.compare(p12, p11) > 0);
+        assertTrue(cmp.compare(p11, p12) < 0);
+        assertTrue(cmp.compare(p12, p22) < 0);
+        assertTrue(cmp.compare(p12, p21) > 0);
+        assertTrue(cmp.compare(p21, p11) > 0);
+        assertTrue(cmp.compare(p21, p12) < 0);
+    }
+
+    public void testKVComparatorable() {
+        assertComparison(1, "ABC", 2, "XYZ",
+                         Comparators.<Integer, String>naturalOrderKeys(),
+                         Comparators.<Integer, String>naturalOrderValues());
+    }
+
+    private static class People {
+        final String firstName;
+        final String lastName;
+        final int age;
+
+        People(String first, String last, int age) {
+            firstName = first;
+            lastName = last;
+            this.age = age;
+        }
+
+        String getFirstName() { return firstName; }
+        String getLastName() { return lastName; }
+        int getAge() { return age; }
+    }
+
+    private final People people[] = {
+        new People("John", "Doe", 34),
+        new People("Mary", "Doe", 30),
+        new People("Maria", "Doe", 14),
+        new People("Jonah", "Doe", 10),
+        new People("John", "Cook", 54),
+        new People("Mary", "Cook", 50),
+    };
+
+    public void testKVComparators() {
+        // Comparator<People> cmp = Comparators.naturalOrder(); // Should fail to compiler as People is not comparable
+        // We can use simple comparator, but those have been tested above.
+        // Thus choose to do compose for some level of interation.
+        Comparator<People> cmp1 = Comparators.comparing((Mapper<String, People>) People::getFirstName);
+        Comparator<People> cmp2 = Comparators.comparing((Mapper<String, People>) People::getLastName);
+        Comparator<People> cmp = Comparators.compose(cmp1, cmp2);
+
+        assertComparison(people[0], people[0], people[1], people[1],
+                         Comparators.<People, People>byKey(cmp),
+                         Comparators.<People, People>byValue(cmp));
+
+    }
 }