) Collections.reverseOrder(this);
+ }
+
+ /**
+ * Returns a comparator that imposes the reverse of the natural
+ * ordering.
+ *
+ * The returned comparator is serializable. Try to compare null with
+ * returned comparator will throw {@link NullPointerException}.
+ *
+ * @param The {@link Comparable} type of element to be compared
+ * @return A comparator that imposes the reverse of the natural
+ * ordering on a collection of objects that implement
+ * the {@link Comparable} interface.
+ * @see Comparable
+ * @since 1.8
+ */
+ public static > Comparator reverseOrder() {
+ return Collections.reverseOrder();
+ }
+
+ /**
+ * Returns a comparator compares {@link Comparable} type in natural order.
+ *
+ * The returned comparator is serializable. Try to compare null with
+ * returned comparator will throw {@link NullPointerException}.
+ *
+ * @param The {@link Comparable} type of element to be compared
+ * @return A comparator that imposes the natural ordering on a
+ * collection of objects that implement the {@link Comparable}
+ * interface.
+ * @see Comparable
+ * @since 1.8
+ */
+ public static > Comparator naturalOrder() {
+ return (Comparator) Comparators.NaturalOrderComparator.INSTANCE;
+ }
+
+ /**
+ * Returns a null-friendly comparator that considers {@code null} to be
+ * less than non-null. When both are {@code null}, they are considered
+ * equal. If both are non-null, the specified {@code Comparator} is used
+ * to determine the order.
+ *
+ * The returned comparator is serializable if the specified comparator
+ * is serializable.
+ *
+ * @param the type of the elements to be compared
+ * @param comparator A {@code Comparator} for comparing non-null values
+ * @return A comparator that considers {@code null} to be less than non-null.
+ * @since 1.8
+ */
+ public static Comparator nullsFirst(Comparator super T> comparator) {
+ return new Comparators.NullComparator(-1, comparator);
+ }
+
+ /**
+ * Returns a null-friendly comparator that considers {@code null} to be
+ * greater than non-null. When both are {@code null}, they are considered
+ * equal. If both are non-null, the specified {@code Comparator} is used
+ * to determine the order.
+ *
+ * The returned comparator is serializable if the specified comparator
+ * is serializable.
+ *
+ * @param the type of the elements to be compared
+ * @param comparator A {@code Comparator} for comparing non-null values
+ * @return A comparator that considers {@code null} to be greater than non-null.
+ * @since 1.8
+ */
+ public static Comparator nullsLast(Comparator super T> comparator) {
+ return new Comparators.NullComparator(1, comparator);
+ }
+
+ /**
+ * Accepts a function that extracts a sort key from a type {@code T}, and
+ * returns a {@code Comparator} that compares by that sort key using
+ * the specified {@link Comparator}. For example, to obtain a {@code
+ * Comparator} that compares {@code Person} objects by their last name
+ * ignoring case differences,
+ *
+ * {@code
+ * Comparator cmp = Comparator.comparing(
+ * Person::getLastName,
+ * String.CASE_INSENSITIVE_ORDER);
+ * }
+ *
+ * The returned comparator is serializable if the specified function
+ * and comparator are both serializable.
+ *
+ * @param The type of element to be compared
+ * @param The type of the sort key
+ * @param keyExtractor the function used to extract the sort key
+ * @param keyComparator the {@code Comparator} used to compare the sort key
+ * @return A comparator that compares by an extracted key using the
+ * specified {@code Comparator}
+ * @throws NullPointerException if the argument is null
+ * @since 1.8
+ */
+ public static Comparator comparing(
+ Function super T, ? extends U> keyExtractor,
+ Comparator super U> keyComparator)
+ {
+ Objects.requireNonNull(keyExtractor);
+ Objects.requireNonNull(keyComparator);
+ return (Comparator & Serializable)
+ (c1, c2) -> keyComparator.compare(keyExtractor.apply(c1),
+ keyExtractor.apply(c2));
+ }
+
+ /**
+ * Accepts a function that extracts a {@link java.lang.Comparable
+ * Comparable} sort key from a type {@code T}, and returns a {@code
+ * Comparator} that compares by that sort key. For example, to obtain
+ * a {@code Comparator} that compares {@code Person} objects by their last
+ * name,
+ *
+ * {@code
+ * Comparator byLastName = Comparator.comparing(Person::getLastName);
+ * }
+ *
+ * The returned comparator is serializable if the specified function
+ * is also serializable.
+ *
+ * @param The type of element to be compared
+ * @param The type of the {@code Comparable} sort key
+ * @param keyExtractor the function used to extract the {@link
+ * Comparable} sort key
+ * @return A comparator that compares by an extracted key
+ * @throws NullPointerException if the argument is null
+ * @since 1.8
+ */
+ public static > Comparator comparing(
+ Function super T, ? extends U> keyExtractor)
+ {
+ Objects.requireNonNull(keyExtractor);
+ return (Comparator & Serializable)
+ (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
+ }
+
+ /**
+ * Accepts a function that extracts an {@code int} sort key from a type
+ * {@code T}, and returns a {@code Comparator} that compares by that
+ * sort key. For example, to obtain a {@code Comparator} that compares
+ * {@code Person} objects by their age,
+ *
+ * {@code
+ * Comparator byAge = Comparator.comparing(Person::getAge);
+ * }
+ *
+ * The returned comparator is serializable if the specified function
+ * is also serializable.
+ *
+ * @param The type of element to be compared
+ * @param keyExtractor the function used to extract the integer sort key
+ * @return A comparator that compares by an extracted key
+ * @see #comparing(Function)
+ * @throws NullPointerException if the argument is null
+ * @since 1.8
+ */
+ public static Comparator comparing(ToIntFunction super T> keyExtractor) {
+ Objects.requireNonNull(keyExtractor);
+ return (Comparator & Serializable)
+ (c1, c2) -> Integer.compare(keyExtractor.applyAsInt(c1), keyExtractor.applyAsInt(c2));
+ }
+
+ /**
+ * Accepts a function that extracts a {@code long} sort key from a type
+ * {@code T}, and returns a {@code Comparator} that compares by that
+ * sort key.
+ *
+ * The returned comparator is serializable if the specified function
+ * is also serializable.
+ *
+ * @param The type of element to be compared
+ * @param keyExtractor the function used to extract the long sort key
+ * @return A comparator that compares by an extracted key
+ * @see #comparing(Function)
+ * @throws NullPointerException if the argument is null
+ * @since 1.8
+ */
+ public static Comparator comparing(ToLongFunction super T> keyExtractor) {
+ Objects.requireNonNull(keyExtractor);
+ return (Comparator & Serializable)
+ (c1, c2) -> Long.compare(keyExtractor.applyAsLong(c1), keyExtractor.applyAsLong(c2));
+ }
+
+ /**
+ * Accepts a function that extracts a {@code double} sort key from a type
+ * {@code T}, and returns a {@code Comparator} that compares by that
+ * sort key.
+ *
+ * The returned comparator is serializable if the specified function
+ * is also serializable.
+ *
+ * @param The type of element to be compared
+ * @param keyExtractor the function used to extract the double sort key
+ * @return A comparator that compares by an extracted key
+ * @see #comparing(Function)
+ * @throws NullPointerException if the argument is null
+ * @since 1.8
+ */
+ public static Comparator comparing(ToDoubleFunction super T> keyExtractor) {
+ Objects.requireNonNull(keyExtractor);
+ return (Comparator & Serializable)
+ (c1, c2) -> Double.compare(keyExtractor.applyAsDouble(c1), keyExtractor.applyAsDouble(c2));
}
/**
* Constructs a lexicographic order comparator with another comparator.
- * For example, a {@code Comparator byLastName} can be composed
- * with another {@code Comparator byFirstName}, then {@code
- * byLastName.thenComparing(byFirstName)} creates a {@code
- * Comparator} which sorts by last name, and for equal last names
- * sorts by first name.
+ * If this {@code Comparator} considers two elements equal, i.e.
+ * {@code compare(a, b) == 0}, {@code other} is used to determine the order.
*
- * @param other the other comparator used when equals on this.
+ * For example, to sort a collection of {@code String} based on the
+ * length and then case-insensitive natural ordering, the comparator can
+ * be construcred using following code,
+ *
+ *
{@code
+ * Comparator cmp = Comparator.comparing(String::length)
+ * .thenComparing(String.CASE_INSENSITIVE_ORDER);
+ * }
+ *
+ * The returned comparator is serializable if the specified comparator
+ * is also serializable.
+ *
+ * @param The type of elements compared by the returned comparator.
+ * @param other the other comparator to be used when this comparator
+ * compares two objects that are equal.
+ * @return A lexicographic order comparator composed of this and then the
+ * other comparator
* @throws NullPointerException if the argument is null.
* @since 1.8
*/
default Comparator thenComparing(Comparator super T> other) {
- return Comparators.compose(this, other);
+ Objects.requireNonNull(other);
+ return (Comparator & Serializable) (c1, c2) -> {
+ int res = compare(c1, c2);
+ return (res != 0) ? res : other.compare(c1, c2);
+ };
}
/**
* Constructs a lexicographic order comparator with a function that
- * extracts a {@code Comparable} key. This default implementation calls
- * {@code thenComparing(this, Comparators.comparing(keyExtractor))}.
+ * extracts a key to be compared with the given {@code Comparator}.
*
- * @param the {@link Comparable} type for comparison
- * @param keyExtractor the function used to extract the {@link Comparable} sort key
+ * @implSpec This default implementation calls {@code
+ * thenComparing(comparing(keyExtractor, cmp))}.
+ *
+ * @param The type of elements compared by the returned comparator.
+ * @param The type of the sort key
+ * @param keyExtractor the function used to extract the sort key
+ * @param keyComparator the {@code Comparator} used to compare the sort key
+ * @return A lexicographic order comparator composed of this and then the
+ * key comparator.
* @throws NullPointerException if the argument is null.
- * @see Comparators#comparing(Function)
+ * @see #comparing(Function, Comparator)
* @see #thenComparing(Comparator)
* @since 1.8
*/
- default > Comparator thenComparing(Function super T, ? extends U> keyExtractor) {
- return thenComparing(Comparators.comparing(keyExtractor));
+ default > Comparator thenComparing(
+ Function super T, ? extends U> keyExtractor,
+ Comparator super U> keyComparator)
+ {
+ return thenComparing(comparing(keyExtractor, keyComparator));
}
/**
* Constructs a lexicographic order comparator with a function that
- * extracts a {@code int} value. This default implementation calls {@code
- * thenComparing(this, Comparators.comparing(keyExtractor))}.
+ * extracts a {@code Comparable} sort key.
*
- * @param keyExtractor the function used to extract the integer value
+ * @implSpec This default implementation calls {@code
+ * thenComparing(comparing(keyExtractor))}.
+ *
+ * @param The type of elements compared by the returned comparator.
+ * @param The type of the {@link Comparable} sort key
+ * @param keyExtractor the function used to extract the {@link
+ * Comparable} sort key
+ * @return A lexicographic order comparator composed of this and then the
+ * {@link Comparable} sort key.
* @throws NullPointerException if the argument is null.
- * @see Comparators#comparing(ToIntFunction)
+ * @see #comparing(Function)
+ * @see #thenComparing(Comparator)
+ * @since 1.8
+ */
+ default > Comparator thenComparing(
+ Function super T, ? extends U> keyExtractor)
+ {
+ return thenComparing(comparing(keyExtractor));
+ }
+
+ /**
+ * Constructs a lexicographic order comparator with a function that
+ * extracts a {@code int} sort key.
+ *
+ * @implSpec This default implementation calls {@code
+ * thenComparing(comparing(keyExtractor))}.
+ *
+ * @param The type of elements compared by the returned comparator.
+ * @param keyExtractor the function used to extract the integer sort key
+ * @return A lexicographic order comparator composed of this and then the
+ * {@code int} sort key
+ * @throws NullPointerException if the argument is null.
+ * @see #comparing(ToIntFunction)
* @see #thenComparing(Comparator)
* @since 1.8
*/
default Comparator thenComparing(ToIntFunction super T> keyExtractor) {
- return thenComparing(Comparators.comparing(keyExtractor));
+ return thenComparing(comparing(keyExtractor));
}
/**
* Constructs a lexicographic order comparator with a function that
- * extracts a {@code long} value. This default implementation calls
- * {@code thenComparing(this, Comparators.comparing(keyExtractor))}.
+ * extracts a {@code long} sort key.
*
- * @param keyExtractor the function used to extract the long value
+ * @implSpec This default implementation calls {@code
+ * thenComparing(comparing(keyExtractor))}.
+ *
+ * @param The type of elements compared by the returned comparator.
+ * @param keyExtractor the function used to extract the long sort key
+ * @return A lexicographic order comparator composed of this and then the
+ * {@code long} sort key
* @throws NullPointerException if the argument is null.
- * @see Comparators#comparing(ToLongFunction)
+ * @see #comparing(ToLongFunction)
* @see #thenComparing(Comparator)
* @since 1.8
*/
default Comparator thenComparing(ToLongFunction super T> keyExtractor) {
- return thenComparing(Comparators.comparing(keyExtractor));
+ return thenComparing(comparing(keyExtractor));
}
/**
* Constructs a lexicographic order comparator with a function that
- * extracts a {@code double} value. This default implementation calls
- * {@code thenComparing(this, Comparators.comparing(keyExtractor))}.
+ * extracts a {@code double} sort key.
*
- * @param keyExtractor the function used to extract the double value
+ * @implSpec This default implementation calls {@code
+ * thenComparing(comparing(keyExtractor))}.
+ *
+ * @param The type of elements compared by the returned comparator.
+ * @param keyExtractor the function used to extract the double sort key
+ * @return A lexicographic order comparator composed of this and then the
+ * {@code double} sort key
* @throws NullPointerException if the argument is null.
- * @see Comparators#comparing(ToDoubleFunction)
+ * @see #comparing(ToDoubleFunction)
* @see #thenComparing(Comparator)
* @since 1.8
*/
default Comparator thenComparing(ToDoubleFunction super T> keyExtractor) {
- return thenComparing(Comparators.comparing(keyExtractor));
+ return thenComparing(comparing(keyExtractor));
}
}
diff --git a/src/share/classes/java/util/Comparators.java b/src/share/classes/java/util/Comparators.java
--- a/src/share/classes/java/util/Comparators.java
+++ b/src/share/classes/java/util/Comparators.java
@@ -1,5 +1,5 @@
/*
- * Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved.
+ * Copyright (c) 2012, 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
*
* This code is free software; you can redistribute it and/or modify it
@@ -32,16 +32,9 @@
import java.util.function.ToLongFunction;
/**
- * This class consists of {@code static} utility methods for comparators. Mostly
- * factory method that returns a {@link Comparator}.
- *
- * Unless otherwise noted, passing a {@code null} argument to a method in
- * this class will cause a {@link NullPointerException} to be thrown.
- *
- * @see Comparator
- * @since 1.8
+ * Package private supporting class for {@link Comparator}.
*/
-public class Comparators {
+class Comparators {
private Comparators() {
throw new AssertionError("no instances");
}
@@ -51,7 +44,7 @@
*
* @see Comparable
*/
- private enum NaturalOrderComparator implements Comparator> {
+ enum NaturalOrderComparator implements Comparator> {
INSTANCE;
@Override
@@ -61,221 +54,38 @@
}
/**
- * Returns a comparator that imposes the reverse of the natural
- * ordering.
- *
- * The returned comparator is serializable.
- *
- * @param {@link Comparable} type
- *
- * @return A comparator that imposes the reverse of the natural
- * ordering on a collection of objects that implement
- * the {@link Comparable} interface.
- * @see Comparable
+ * Null-friendly comparators
*/
- public static > Comparator reverseOrder() {
- return Collections.reverseOrder();
- }
+ static class NullComparator implements Comparator, Serializable {
+ private static final long serialVersionUID = -7569533591570686392L;
+ private final int sign;
+ private final Comparator real;
- /**
- * Returns a comparator that imposes the reverse ordering of the specified
- * {@link Comparator}.
- *
- * The returned comparator is serializable (assuming the specified
- * comparator is also serializable).
- *
- * @param the element type to be compared
- * @param cmp a comparator whose ordering is to be reversed by the returned
- * comparator
- * @return A comparator that imposes the reverse ordering of the
- * specified comparator.
- */
- public static Comparator reverseOrder(Comparator cmp) {
- Objects.requireNonNull(cmp);
- return Collections.reverseOrder(cmp);
- }
+ NullComparator(int sign, Comparator super T> real) {
+ this.sign = sign;
+ this.real = (Comparator) Objects.requireNonNull(real);
+ }
- /**
- * Gets a comparator compares {@link Comparable} type in natural order.
- *
- * @param {@link Comparable} type
- */
- public static > Comparator naturalOrder() {
- return (Comparator) NaturalOrderComparator.INSTANCE;
- }
+ @Override
+ public int compare(T a, T b) {
+ if (a == null) {
+ return (b == null) ? 0 : sign;
+ } else if (b == null) {
+ return -sign;
+ } else {
+ return (real == null) ? 0 : real.compare(a, b);
+ }
+ }
- /**
- * Gets a comparator compares {@link Map.Entry} in natural order on key.
- *
- * @param {@link Comparable} key type
- * @param value type
- */
- public static , V> Comparator> naturalOrderKeys() {
- return (Comparator> & Serializable)
- (c1, c2) -> c1.getKey().compareTo(c2.getKey());
- }
+ @Override
+ public Comparator thenComparing(Comparator super T> other) {
+ Objects.requireNonNull(other);
+ return new NullComparator(sign, real.thenComparing(other));
+ }
- /**
- * Gets a comparator compares {@link Map.Entry} in natural order on value.
- *
- * @param key type
- * @param {@link Comparable} value type
- */
- public static > Comparator> naturalOrderValues() {
- return (Comparator> & Serializable)
- (c1, c2) -> c1.getValue().compareTo(c2.getValue());
- }
-
- /**
- * Gets a comparator compares {@link Map.Entry} by key using the given
- * {@link Comparator}.
- *
- * The returned comparator is serializable assuming the specified
- * comparators are also serializable.
- *
- * @param key type
- * @param value type
- * @param cmp the key {@link Comparator}
- */
- public static Comparator> byKey(Comparator super K> cmp) {
- Objects.requireNonNull(cmp);
- return (Comparator> & Serializable)
- (c1, c2) -> cmp.compare(c1.getKey(), c2.getKey());
- }
-
- /**
- * Gets a comparator compares {@link Map.Entry} by value using the given
- * {@link Comparator}.
- *
- * @param key type
- * @param value type
- * @param cmp the value {@link Comparator}
- */
- public static Comparator> byValue(Comparator super V> cmp) {
- Objects.requireNonNull(cmp);
- return (Comparator> & Serializable)
- (c1, c2) -> cmp.compare(c1.getValue(), c2.getValue());
- }
-
- /**
- * Accepts a function that extracts a {@link java.lang.Comparable
- * Comparable} sort key from a type {@code T}, and returns a {@code
- * Comparator} that compares by that sort key. For example, if a class
- * {@code Person} has a {@code String}-valued getter {@code getLastName},
- * then {@code comparing(Person::getLastName)} would return a {@code
- * Comparator} that compares {@code Person} objects by their last
- * name.
- *
- * @param the original element type
- * @param the {@link Comparable} type for comparison
- * @param keyExtractor the function used to extract the {@link Comparable} sort key
- */
- public static > Comparator comparing(Function super T, ? extends U> keyExtractor) {
- Objects.requireNonNull(keyExtractor);
- return (Comparator & Serializable)
- (c1, c2) -> keyExtractor.apply(c1).compareTo(keyExtractor.apply(c2));
- }
-
- /**
- * Accepts a function that extracts an {@code int} value from a type {@code
- * T}, and returns a {@code Comparator} that compares by that value.
- *
- * The returned comparator is serializable assuming the specified
- * function is also serializable.
- *
- * @see #comparing(Function)
- * @param the original element type
- * @param keyExtractor the function used to extract the integer value
- */
- public static Comparator comparing(ToIntFunction super T> keyExtractor) {
- Objects.requireNonNull(keyExtractor);
- return (Comparator & Serializable)
- (c1, c2) -> Integer.compare(keyExtractor.applyAsInt(c1), keyExtractor.applyAsInt(c2));
- }
-
- /**
- * Accepts a function that extracts a {@code long} value from a type {@code
- * T}, and returns a {@code Comparator} that compares by that value.
- *
- * The returned comparator is serializable assuming the specified
- * function is also serializable.
- *
- * @see #comparing(Function)
- * @param the original element type
- * @param keyExtractor the function used to extract the long value
- */
- public static Comparator comparing(ToLongFunction super T> keyExtractor) {
- Objects.requireNonNull(keyExtractor);
- return (Comparator & Serializable)
- (c1, c2) -> Long.compare(keyExtractor.applyAsLong(c1), keyExtractor.applyAsLong(c2));
- }
-
- /**
- * Accepts a function that extracts a {@code double} value from a type
- * {@code T}, and returns a {@code Comparator} that compares by that
- * value.
- *
- * The returned comparator is serializable assuming the specified
- * function is also serializable.
- *
- * @see #comparing(Function)
- * @param the original element type
- * @param keyExtractor the function used to extract the double value
- */
- public static Comparator comparing(ToDoubleFunction super T> keyExtractor) {
- Objects.requireNonNull(keyExtractor);
- return (Comparator & Serializable)
- (c1, c2) -> Double.compare(keyExtractor.applyAsDouble(c1), keyExtractor.applyAsDouble(c2));
- }
-
- /**
- * Constructs a lexicographic order from two {@link Comparator}s. For
- * example, if you have comparators {@code byLastName} and {@code
- * byFirstName}, each of type {@code Comparator}, then {@code
- * compose(byLastName, byFirstName)} creates a {@code Comparator}
- * which sorts by last name, and for equal last names sorts by first name.
- *
- * The returned comparator is serializable assuming the specified
- * comparators are also serializable.
- *
- * @param the element type to be compared
- * @param first the first comparator
- * @param second the secondary comparator used when equals on the first
- */
- public static Comparator compose(Comparator super T> first, Comparator super T> second) {
- Objects.requireNonNull(first);
- Objects.requireNonNull(second);
- return (Comparator & Serializable) (c1, c2) -> {
- int res = first.compare(c1, c2);
- return (res != 0) ? res : second.compare(c1, c2);
- };
- }
-
- /**
- * Constructs a {@link BinaryOperator} which returns the lesser of two elements
- * according to the specified {@code Comparator}
- *
- * @param comparator A {@code Comparator} for comparing the two values
- * @param the type of the elements to be compared
- * @return a {@code BinaryOperator} which returns the lesser of its operands,
- * according to the supplied {@code Comparator}
- */
- public static BinaryOperator lesserOf(Comparator super T> comparator) {
- Objects.requireNonNull(comparator);
- return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
- }
-
- /**
- * Constructs a {@link BinaryOperator} which returns the greater of two elements
- * according to the specified {@code Comparator}
- *
- * @param comparator A {@code Comparator} for comparing the two values
- * @param the type of the elements to be compared
- * @return a {@code BinaryOperator} which returns the greater of its operands,
- * according to the supplied {@code Comparator}
- */
- public static BinaryOperator greaterOf(Comparator super T> comparator) {
- Objects.requireNonNull(comparator);
- return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
+ @Override
+ public Comparator reversed() {
+ return new NullComparator(-sign, real.reversed());
+ }
}
}
diff --git a/src/share/classes/java/util/Map.java b/src/share/classes/java/util/Map.java
--- a/src/share/classes/java/util/Map.java
+++ b/src/share/classes/java/util/Map.java
@@ -28,6 +28,7 @@
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
+import java.io.Serializable;
/**
* An object that maps keys to values. A map cannot contain duplicate keys;
@@ -446,6 +447,76 @@
* @see #equals(Object)
*/
int hashCode();
+
+ /**
+ * Returns a comparator that compares {@link Map.Entry} in natural order on key.
+ *
+ * The returned comparator is serializable. Note that a null key in the
+ * map will cause the returned comparator to throw {@link
+ * NullPointerException}.
+ *
+ * @param {@link Comparable} key type
+ * @param value type
+ * @return A comparator that compares {@link Map.Entry} in natural order on key.
+ * @see Comparable
+ */
+ public static , V> Comparator> comparingByKey() {
+ return (Comparator> & Serializable)
+ (c1, c2) -> c1.getKey().compareTo(c2.getKey());
+ }
+
+ /**
+ * Returns a comparator that compares {@link Map.Entry} in natural order on value.
+ *
+ * The returned comparator is serializable. Note that a null value in
+ * the map will cause the returned comparator to throw {@link
+ * NullPointerException}.
+ *
+ * @param key type
+ * @param {@link Comparable} value type
+ * @return A comparator that compares {@link Map.Entry} in natural order on value.
+ * @see Comparable
+ */
+ public static > Comparator> comparingByValue() {
+ return (Comparator> & Serializable)
+ (c1, c2) -> c1.getValue().compareTo(c2.getValue());
+ }
+
+ /**
+ * Returns a comparator that compares {@link Map.Entry} by key using the given
+ * {@link Comparator}.
+ *
+ * The returned comparator is serializable if the specified
+ * comparator is also serializable.
+ *
+ * @param key type
+ * @param value type
+ * @param cmp the key {@link Comparator}
+ * @return A comparator that compares {@link Map.Entry} by the key.
+ */
+ public static Comparator> comparingByKey(Comparator super K> cmp) {
+ Objects.requireNonNull(cmp);
+ return (Comparator> & Serializable)
+ (c1, c2) -> cmp.compare(c1.getKey(), c2.getKey());
+ }
+
+ /**
+ * Returns a comparator that compares {@link Map.Entry} by value using the given
+ * {@link Comparator}.
+ *
+ * The returned comparator is serializable if the specified
+ * comparator is also serializable.
+ *
+ * @param key type
+ * @param value type
+ * @param cmp the value {@link Comparator}
+ * @return A comparator that compares {@link Map.Entry} by the value.
+ */
+ public static Comparator> comparingByValue(Comparator super V> cmp) {
+ Objects.requireNonNull(cmp);
+ return (Comparator> & Serializable)
+ (c1, c2) -> cmp.compare(c1.getValue(), c2.getValue());
+ }
}
// Comparison and hashing
diff --git a/src/share/classes/java/util/TreeMap.java b/src/share/classes/java/util/TreeMap.java
--- a/src/share/classes/java/util/TreeMap.java
+++ b/src/share/classes/java/util/TreeMap.java
@@ -2909,13 +2909,13 @@
public int characteristics() {
return (side == 0 ? Spliterator.SIZED : 0) |
- Spliterator.DISTINCT | Spliterator.SORTED | Spliterator.ORDERED;
+ Spliterator.DISTINCT | Spliterator.SORTED | Spliterator.ORDERED;
}
@Override
public Comparator super Map.Entry> getComparator() {
return tree.comparator != null ?
- Comparators.byKey(tree.comparator) : null;
+ Map.Entry.comparingByKey(tree.comparator) : null;
}
}
}
diff --git a/src/share/classes/java/util/function/BinaryOperator.java b/src/share/classes/java/util/function/BinaryOperator.java
--- a/src/share/classes/java/util/function/BinaryOperator.java
+++ b/src/share/classes/java/util/function/BinaryOperator.java
@@ -24,6 +24,9 @@
*/
package java.util.function;
+import java.util.Objects;
+import java.util.Comparator;
+
/**
* An operation upon two operands yielding a result. This is a specialization of
* {@code BiFunction} where the operands and the result are all of the same type.
@@ -35,4 +38,29 @@
*/
@FunctionalInterface
public interface BinaryOperator extends BiFunction {
+ /**
+ * Constructs a {@link BinaryOperator} which returns the lesser of two elements
+ * according to the specified {@code Comparator}
+ *
+ * @param comparator A {@code Comparator} for comparing the two values
+ * @return a {@code BinaryOperator} which returns the lesser of its operands,
+ * according to the supplied {@code Comparator}
+ */
+ public static BinaryOperator minBy(Comparator super T> comparator) {
+ Objects.requireNonNull(comparator);
+ return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
+ }
+
+ /**
+ * Constructs a {@link BinaryOperator} which returns the greater of two elements
+ * according to the specified {@code Comparator}
+ *
+ * @param comparator A {@code Comparator} for comparing the two values
+ * @return a {@code BinaryOperator} which returns the greater of its operands,
+ * according to the supplied {@code Comparator}
+ */
+ public static BinaryOperator maxBy(Comparator super T> comparator) {
+ Objects.requireNonNull(comparator);
+ return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
+ }
}
diff --git a/src/share/classes/java/util/stream/Collectors.java b/src/share/classes/java/util/stream/Collectors.java
--- a/src/share/classes/java/util/stream/Collectors.java
+++ b/src/share/classes/java/util/stream/Collectors.java
@@ -30,7 +30,6 @@
import java.util.Collection;
import java.util.Collections;
import java.util.Comparator;
-import java.util.Comparators;
import java.util.DoubleSummaryStatistics;
import java.util.EnumSet;
import java.util.HashMap;
@@ -78,7 +77,7 @@
*
* // Find highest-paid employee
* Employee highestPaid = employees.stream()
- * .collect(Collectors.maxBy(Comparators.comparing(Employee::getSalary)));
+ * .collect(Collectors.maxBy(Comparator.comparing(Employee::getSalary)));
*
* // Group employees by department
* Map> byDept
@@ -89,7 +88,7 @@
* Map highestPaidByDept
* = employees.stream()
* .collect(Collectors.groupingBy(Employee::getDepartment,
- * Collectors.maxBy(Comparators.comparing(Employee::getSalary))));
+ * Collectors.maxBy(Comparator.comparing(Employee::getSalary))));
*
* // Partition students into passing and failing
* Map> passingFailing =
@@ -404,7 +403,7 @@
* @implSpec
* This produces a result equivalent to:
* {@code
- * reducing(Comparators.lesserOf(comparator))
+ * reducing(BinaryOperator.minBy(comparator))
* }
*
* @param the type of the input elements
@@ -413,7 +412,7 @@
*/
public static Collector
minBy(Comparator super T> comparator) {
- return reducing(Comparators.lesserOf(comparator));
+ return reducing(BinaryOperator.minBy(comparator));
}
/**
@@ -423,7 +422,7 @@
* @implSpec
* This produces a result equivalent to:
* {@code
- * reducing(Comparators.greaterOf(comparator))
+ * reducing(BinaryOperator.maxBy(comparator))
* }
*
* @param the type of the input elements
@@ -432,7 +431,7 @@
*/
public static Collector
maxBy(Comparator super T> comparator) {
- return reducing(Comparators.greaterOf(comparator));
+ return reducing(BinaryOperator.maxBy(comparator));
}
/**
@@ -491,8 +490,8 @@
* For example, given a stream of {@code Person}, to calculate tallest
* person in each city:
*
{@code
- * Comparator byHeight = Comparators.comparing(Person::getHeight);
- * BinaryOperator tallerOf = Comparators.greaterOf(byHeight);
+ * Comparator byHeight = Comparator.comparing(Person::getHeight);
+ * BinaryOperator tallerOf = BinaryOperator.greaterOf(byHeight);
* Map tallestByCity
* = people.stream().collect(groupingBy(Person::getCity, reducing(tallerOf)));
* }
@@ -531,8 +530,8 @@
* For example, given a stream of {@code Person}, to calculate the longest
* last name of residents in each city:
*
{@code
- * Comparator byLength = Comparators.comparing(String::length);
- * BinaryOperator longerOf = Comparators.greaterOf(byLength);
+ * Comparator byLength = Comparator.comparing(String::length);
+ * BinaryOperator longerOf = BinaryOperator.greaterOf(byLength);
* Map longestLastNameByCity
* = people.stream().collect(groupingBy(Person::getCity,
* reducing(Person::getLastName, longerOf)));
diff --git a/src/share/classes/java/util/stream/ReferencePipeline.java b/src/share/classes/java/util/stream/ReferencePipeline.java
--- a/src/share/classes/java/util/stream/ReferencePipeline.java
+++ b/src/share/classes/java/util/stream/ReferencePipeline.java
@@ -25,7 +25,6 @@
package java.util.stream;
import java.util.Comparator;
-import java.util.Comparators;
import java.util.Iterator;
import java.util.Objects;
import java.util.Optional;
@@ -483,12 +482,12 @@
@Override
public final Optional max(Comparator super P_OUT> comparator) {
- return reduce(Comparators.greaterOf(comparator));
+ return reduce(BinaryOperator.maxBy(comparator));
}
@Override
public final Optional min(Comparator super P_OUT> comparator) {
- return reduce(Comparators.lesserOf(comparator));
+ return reduce(BinaryOperator.minBy(comparator));
}
diff --git a/src/share/classes/java/util/stream/SortedOps.java b/src/share/classes/java/util/stream/SortedOps.java
--- a/src/share/classes/java/util/stream/SortedOps.java
+++ b/src/share/classes/java/util/stream/SortedOps.java
@@ -27,7 +27,6 @@
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Comparator;
-import java.util.Comparators;
import java.util.Objects;
import java.util.Spliterator;
import java.util.concurrent.ForkJoinTask;
@@ -114,7 +113,7 @@
StreamOpFlag.IS_ORDERED | StreamOpFlag.IS_SORTED);
this.isNaturalSort = true;
// Will throw CCE when we try to sort if T is not Comparable
- this.comparator = (Comparator super T>) Comparators.naturalOrder();
+ this.comparator = (Comparator super T>) Comparator.naturalOrder();
}
/**
diff --git a/test/java/nio/file/Files/StreamTest.java b/test/java/nio/file/Files/StreamTest.java
--- a/test/java/nio/file/Files/StreamTest.java
+++ b/test/java/nio/file/Files/StreamTest.java
@@ -43,7 +43,7 @@
import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes;
import java.util.Arrays;
-import java.util.Comparators;
+import java.util.Comparator;
import java.util.Iterator;
import java.util.List;
import java.util.Objects;
@@ -139,7 +139,7 @@
public void testBasic() {
try (CloseableStream s = Files.list(testFolder)) {
- Object[] actual = s.sorted(Comparators.naturalOrder()).toArray();
+ Object[] actual = s.sorted(Comparator.naturalOrder()).toArray();
assertEquals(actual, level1);
} catch (IOException ioe) {
fail("Unexpected IOException");
@@ -155,7 +155,7 @@
public void testWalk() {
try (CloseableStream s = Files.walk(testFolder)) {
- Object[] actual = s.sorted(Comparators.naturalOrder()).toArray();
+ Object[] actual = s.sorted(Comparator.naturalOrder()).toArray();
assertEquals(actual, all);
} catch (IOException ioe) {
fail("Unexpected IOException");
@@ -165,7 +165,7 @@
public void testWalkOneLevel() {
try (CloseableStream s = Files.walk(testFolder, 1)) {
Object[] actual = s.filter(path -> ! path.equals(testFolder))
- .sorted(Comparators.naturalOrder())
+ .sorted(Comparator.naturalOrder())
.toArray();
assertEquals(actual, level1);
} catch (IOException ioe) {
@@ -177,7 +177,7 @@
// If link is not supported, the directory structure won't have link.
// We still want to test the behavior with FOLLOW_LINKS option.
try (CloseableStream s = Files.walk(testFolder, FileVisitOption.FOLLOW_LINKS)) {
- Object[] actual = s.sorted(Comparators.naturalOrder()).toArray();
+ Object[] actual = s.sorted(Comparator.naturalOrder()).toArray();
assertEquals(actual, all_folowLinks);
} catch (IOException ioe) {
fail("Unexpected IOException");
@@ -637,13 +637,13 @@
public void testClosedStream() throws IOException {
try (CloseableStream s = Files.list(testFolder)) {
s.close();
- Object[] actual = s.sorted(Comparators.naturalOrder()).toArray();
+ Object[] actual = s.sorted(Comparator.naturalOrder()).toArray();
assertTrue(actual.length <= level1.length);
}
try (CloseableStream s = Files.walk(testFolder)) {
s.close();
- Object[] actual = s.sorted(Comparators.naturalOrder()).toArray();
+ Object[] actual = s.sorted(Comparator.naturalOrder()).toArray();
fail("Operate on closed stream should throw IllegalStateException");
} catch (IllegalStateException ex) {
// expected
@@ -652,7 +652,7 @@
try (CloseableStream s = Files.find(testFolder, Integer.MAX_VALUE,
(p, attr) -> true)) {
s.close();
- Object[] actual = s.sorted(Comparators.naturalOrder()).toArray();
+ Object[] actual = s.sorted(Comparator.naturalOrder()).toArray();
fail("Operate on closed stream should throw IllegalStateException");
} catch (IllegalStateException ex) {
// expected
diff --git a/test/java/util/Collection/ListDefaults.java b/test/java/util/Collection/ListDefaults.java
--- a/test/java/util/Collection/ListDefaults.java
+++ b/test/java/util/Collection/ListDefaults.java
@@ -25,7 +25,6 @@
import java.util.Arrays;
import java.util.Collections;
import java.util.Comparator;
-import java.util.Comparators;
import java.util.List;
import java.util.LinkedList;
import java.util.Stack;
@@ -337,23 +336,23 @@
CollectionSupplier.shuffle(list);
list.sort(null);
- CollectionAsserts.assertSorted(list, Comparators.naturalOrder());
+ CollectionAsserts.assertSorted(list, Comparator.naturalOrder());
if (test.name.startsWith("reverse")) {
Collections.reverse(list);
}
CollectionAsserts.assertContents(list, original);
CollectionSupplier.shuffle(list);
- list.sort(Comparators.naturalOrder());
- CollectionAsserts.assertSorted(list, Comparators.naturalOrder());
+ list.sort(Comparator.naturalOrder());
+ CollectionAsserts.assertSorted(list, Comparator.naturalOrder());
if (test.name.startsWith("reverse")) {
Collections.reverse(list);
}
CollectionAsserts.assertContents(list, original);
CollectionSupplier.shuffle(list);
- list.sort(Comparators.reverseOrder());
- CollectionAsserts.assertSorted(list, Comparators.reverseOrder());
+ list.sort(Comparator.reverseOrder());
+ CollectionAsserts.assertSorted(list, Comparator.reverseOrder());
if (!test.name.startsWith("reverse")) {
Collections.reverse(list);
}
@@ -390,8 +389,8 @@
final List copy = new ArrayList<>(list);
final List subList = list.subList(SUBLIST_FROM, SUBLIST_TO);
CollectionSupplier.shuffle(subList);
- subList.sort(Comparators.naturalOrder());
- CollectionAsserts.assertSorted(subList, Comparators.naturalOrder());
+ subList.sort(Comparator.naturalOrder());
+ CollectionAsserts.assertSorted(subList, Comparator.naturalOrder());
// verify that elements [0, from) remain unmodified
for (int i = 0; i < SUBLIST_FROM; i++) {
assertTrue(list.get(i) == copy.get(i),
@@ -412,8 +411,8 @@
public void call(final List list) {
final List copy = new ArrayList<>(list);
CollectionSupplier.shuffle(list);
- list.sort(Comparators.naturalOrder());
- CollectionAsserts.assertSorted(list, Comparators.naturalOrder());
+ list.sort(Comparator.naturalOrder());
+ CollectionAsserts.assertSorted(list, Comparator.naturalOrder());
}
});
}
diff --git a/test/java/util/Comparator/BasicTest.java b/test/java/util/Comparator/BasicTest.java
new file mode 100644
--- /dev/null
+++ b/test/java/util/Comparator/BasicTest.java
@@ -0,0 +1,344 @@
+/*
+ * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
+ * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
+ *
+ * This code is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 only, as
+ * published by the Free Software Foundation. Oracle designates this
+ * particular file as subject to the "Classpath" exception as provided
+ * by Oracle in the LICENSE file that accompanied this code.
+ *
+ * This code is distributed in the hope that it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * version 2 for more details (a copy is included in the LICENSE file that
+ * accompanied this code).
+ *
+ * You should have received a copy of the GNU General Public License version
+ * 2 along with this work; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
+ *
+ * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
+ * or visit www.oracle.com if you need additional information or have any
+ * questions.
+ */
+
+/**
+ * @test
+ * @summary Comparator default method tests
+ * @run testng BasicTest
+ */
+
+import java.util.TreeMap;
+import java.util.Comparator;
+import org.testng.annotations.Test;
+
+import java.util.function.Function;
+import java.util.function.ToIntFunction;
+import java.util.function.ToLongFunction;
+import java.util.function.ToDoubleFunction;
+
+import static org.testng.Assert.assertEquals;
+import static org.testng.Assert.assertTrue;
+import static org.testng.Assert.fail;
+
+@Test(groups = "unit")
+public class BasicTest {
+ private static class Thing {
+ public final int intField;
+ public final long longField;
+ public final double doubleField;
+ public final String stringField;
+
+ private Thing(int intField, long longField, double doubleField, String stringField) {
+ this.intField = intField;
+ this.longField = longField;
+ this.doubleField = doubleField;
+ this.stringField = stringField;
+ }
+
+ public int getIntField() {
+ return intField;
+ }
+
+ public long getLongField() {
+ return longField;
+ }
+
+ public double getDoubleField() {
+ return doubleField;
+ }
+
+ public String getStringField() {
+ return stringField;
+ }
+ }
+
+ private final int[] intValues = { -2, -2, -1, -1, 0, 0, 1, 1, 2, 2 };
+ private final long[] longValues = { -2, -2, -1, -1, 0, 0, 1, 1, 2, 2 };
+ private final double[] doubleValues = { -2, -2, -1, -1, 0, 0, 1, 1, 2, 2 };
+ private final String[] stringValues = { "a", "a", "b", "b", "c", "c", "d", "d", "e", "e" };
+ private final int[] comparisons = { 0, -1, 0, -1, 0, -1, 0, -1, 0 };
+
+ private void assertComparisons(T[] things, Comparator comp, int[] comparisons) {
+ for (int i=0; i comp = Comparator.comparing(new ToIntFunction() {
+ @Override
+ public int applyAsInt(Thing thing) {
+ return thing.getIntField();
+ }
+ });
+
+ assertComparisons(things, comp, comparisons);
+ }
+
+ public void testLongComparator() {
+ Thing[] things = new Thing[longValues.length];
+ for (int i=0; i comp = Comparator.comparing(new ToLongFunction() {
+ @Override
+ public long applyAsLong(Thing thing) {
+ return thing.getLongField();
+ }
+ });
+
+ assertComparisons(things, comp, comparisons);
+ }
+
+ public void testDoubleComparator() {
+ Thing[] things = new Thing[doubleValues.length];
+ for (int i=0; i comp = Comparator.comparing(new ToDoubleFunction() {
+ @Override
+ public double applyAsDouble(Thing thing) {
+ return thing.getDoubleField();
+ }
+ });
+
+ assertComparisons(things, comp, comparisons);
+ }
+
+ public void testComparing() {
+ Thing[] things = new Thing[doubleValues.length];
+ for (int i=0; i comp = Comparator.comparing(new Function() {
+ @Override
+ public String apply(Thing thing) {
+ return thing.getStringField();
+ }
+ });
+
+ assertComparisons(things, comp, comparisons);
+ }
+
+ public void testNaturalOrderComparator() {
+ Comparator comp = Comparator.naturalOrder();
+
+ assertComparisons(stringValues, comp, comparisons);
+ }
+
+ public void testReverseComparator() {
+ Comparator cmpr = Comparator.reverseOrder();
+ Comparator cmp = cmpr.reversed();
+
+ assertEquals(cmp.reversed(), 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 cmp = (s1, s2) -> s1.length() - s2.length();
+ Comparator cmpr = cmp.reversed();
+
+ assertEquals(cmpr.reversed(), 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);
+ }
+
+ private void assertComparison(Comparator cmp, T less, T greater) {
+ assertTrue(cmp.compare(less, greater) < 0, "less");
+ assertTrue(cmp.compare(less, less) == 0, "equal");
+ assertTrue(cmp.compare(greater, greater) == 0, "equal");
+ assertTrue(cmp.compare(greater, less) > 0, "greater");
+ }
+
+ 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; }
+ long getAgeAsLong() { return (long) age; };
+ double getAgeAsDouble() { return (double) 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),
+ new People("Mary", null, 25),
+ new People("John", null, 27)
+ };
+
+ public void testComparatorDefaultMethods() {
+ Comparator cmp = Comparator.comparing((Function) People::getFirstName);
+ Comparator cmp2 = Comparator.comparing((Function