提交 83c573d4 编写于 作者: H henryjen

8009736: Comparator API cleanup

Reviewed-by: psandoz, briangoetz, mduigou, plevart
上级 071acd64
...@@ -4304,6 +4304,11 @@ public class Collections { ...@@ -4304,6 +4304,11 @@ public class Collections {
} }
private Object readResolve() { return Collections.reverseOrder(); } private Object readResolve() { return Collections.reverseOrder(); }
@Override
public Comparator<Comparable<Object>> reversed() {
return Comparator.naturalOrder();
}
} }
/** /**
...@@ -4367,6 +4372,11 @@ public class Collections { ...@@ -4367,6 +4372,11 @@ public class Collections {
public int hashCode() { public int hashCode() {
return cmp.hashCode() ^ Integer.MIN_VALUE; return cmp.hashCode() ^ Integer.MIN_VALUE;
} }
@Override
public Comparator<T> reversed() {
return cmp;
}
} }
/** /**
......
...@@ -25,10 +25,12 @@ ...@@ -25,10 +25,12 @@
package java.util; package java.util;
import java.io.Serializable;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.ToIntFunction; import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction; import java.util.function.ToLongFunction;
import java.util.function.ToDoubleFunction; import java.util.function.ToDoubleFunction;
import java.util.Comparators;
/** /**
* A comparison function, which imposes a <i>total ordering</i> on some * A comparison function, which imposes a <i>total ordering</i> on some
...@@ -175,88 +177,357 @@ public interface Comparator<T> { ...@@ -175,88 +177,357 @@ public interface Comparator<T> {
* Returns a comparator that imposes the reverse ordering of this * Returns a comparator that imposes the reverse ordering of this
* comparator. * comparator.
* *
* @return A comparator that imposes the reverse ordering of this * @return a comparator that imposes the reverse ordering of this
* comparator. * comparator.
* @since 1.8 * @since 1.8
*/ */
default Comparator<T> reverseOrder() { default Comparator<T> reversed() {
return Collections.reverseOrder(this); return Collections.reverseOrder(this);
} }
/** /**
* Constructs a lexicographic order comparator with another comparator. * Returns a lexicographic-order comparator with another comparator.
* For example, a {@code Comparator<Person> byLastName} can be composed * If this {@code Comparator} considers two elements equal, i.e.
* with another {@code Comparator<Person> byFirstName}, then {@code * {@code compare(a, b) == 0}, {@code other} is used to determine the order.
* byLastName.thenComparing(byFirstName)} creates a {@code *
* Comparator<Person>} which sorts by last name, and for equal last names * <p>The returned comparator is serializable if the specified comparator
* sorts by first name. * is also serializable.
* *
* @param other the other comparator used when equals on this. * @apiNote
* For example, to sort a collection of {@code String} based on the length
* and then case-insensitive natural ordering, the comparator can be
* composed using following code,
*
* <pre>{@code
* Comparator<String> cmp = Comparator.comparing(String::length)
* .thenComparing(String.CASE_INSENSITIVE_ORDER);
* }</pre>
*
* @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. * @throws NullPointerException if the argument is null.
* @since 1.8 * @since 1.8
*/ */
default Comparator<T> thenComparing(Comparator<? super T> other) { default Comparator<T> thenComparing(Comparator<? super T> other) {
return Comparators.compose(this, other); Objects.requireNonNull(other);
return (Comparator<T> & 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 * Returns a lexicographic-order comparator with a function that
* extracts a {@code Comparable} key. This default implementation calls * extracts a key to be compared with the given {@code Comparator}.
* {@code thenComparing(this, Comparators.comparing(keyExtractor))}. *
* @implSpec This default implementation behaves as if {@code
* thenComparing(comparing(keyExtractor, cmp))}.
* *
* @param <U> the {@link Comparable} type for comparison * @param <U> the type of the sort key
* @param keyExtractor the function used to extract the {@link Comparable} 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 comparator
* and then comparing on the key extracted by the keyExtractor function
* @throws NullPointerException if the argument is null. * @throws NullPointerException if the argument is null.
* @see Comparators#comparing(Function) * @see #comparing(Function, Comparator)
* @see #thenComparing(Comparator) * @see #thenComparing(Comparator)
* @since 1.8 * @since 1.8
*/ */
default <U extends Comparable<? super U>> Comparator<T> thenComparing(Function<? super T, ? extends U> keyExtractor) { default <U extends Comparable<? super U>> Comparator<T> thenComparing(
return thenComparing(Comparators.comparing(keyExtractor)); Function<? super T, ? extends U> keyExtractor,
Comparator<? super U> keyComparator)
{
return thenComparing(comparing(keyExtractor, keyComparator));
} }
/** /**
* Constructs a lexicographic order comparator with a function that * Returns a lexicographic-order comparator with a function that
* extracts a {@code int} value. This default implementation calls {@code * extracts a {@code Comparable} sort key.
* thenComparing(this, Comparators.comparing(keyExtractor))}. *
* @implSpec This default implementation behaves as if {@code
* thenComparing(comparing(keyExtractor))}.
* *
* @param keyExtractor the function used to extract the integer value * @param <U> 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. * @throws NullPointerException if the argument is null.
* @see Comparators#comparing(ToIntFunction) * @see #comparing(Function)
* @see #thenComparing(Comparator)
* @since 1.8
*/
default <U extends Comparable<? super U>> Comparator<T> thenComparing(
Function<? super T, ? extends U> keyExtractor)
{
return thenComparing(comparing(keyExtractor));
}
/**
* Returns a lexicographic-order comparator with a function that
* extracts a {@code int} sort key.
*
* @implSpec This default implementation behaves as if {@code
* thenComparing(comparing(keyExtractor))}.
*
* @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) * @see #thenComparing(Comparator)
* @since 1.8 * @since 1.8
*/ */
default Comparator<T> thenComparing(ToIntFunction<? super T> keyExtractor) { default Comparator<T> thenComparing(ToIntFunction<? super T> keyExtractor) {
return thenComparing(Comparators.comparing(keyExtractor)); return thenComparing(comparing(keyExtractor));
} }
/** /**
* Constructs a lexicographic order comparator with a function that * Returns a lexicographic-order comparator with a function that
* extracts a {@code long} value. This default implementation calls * extracts a {@code long} sort key.
* {@code thenComparing(this, Comparators.comparing(keyExtractor))}. *
* @implSpec This default implementation behaves as if {@code
* thenComparing(comparing(keyExtractor))}.
* *
* @param keyExtractor the function used to extract the long value * @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. * @throws NullPointerException if the argument is null.
* @see Comparators#comparing(ToLongFunction) * @see #comparing(ToLongFunction)
* @see #thenComparing(Comparator) * @see #thenComparing(Comparator)
* @since 1.8 * @since 1.8
*/ */
default Comparator<T> thenComparing(ToLongFunction<? super T> keyExtractor) { default Comparator<T> thenComparing(ToLongFunction<? super T> keyExtractor) {
return thenComparing(Comparators.comparing(keyExtractor)); return thenComparing(comparing(keyExtractor));
} }
/** /**
* Constructs a lexicographic order comparator with a function that * Returns a lexicographic-order comparator with a function that
* extracts a {@code double} value. This default implementation calls * extracts a {@code double} sort key.
* {@code thenComparing(this, Comparators.comparing(keyExtractor))}.
* *
* @param keyExtractor the function used to extract the double value * @implSpec This default implementation behaves as if {@code
* thenComparing(comparing(keyExtractor))}.
*
* @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. * @throws NullPointerException if the argument is null.
* @see Comparators#comparing(ToDoubleFunction) * @see #comparing(ToDoubleFunction)
* @see #thenComparing(Comparator) * @see #thenComparing(Comparator)
* @since 1.8 * @since 1.8
*/ */
default Comparator<T> thenComparing(ToDoubleFunction<? super T> keyExtractor) { default Comparator<T> thenComparing(ToDoubleFunction<? super T> keyExtractor) {
return thenComparing(Comparators.comparing(keyExtractor)); return thenComparing(comparing(keyExtractor));
}
/**
* Returns a comparator that imposes the reverse of the <em>natural
* ordering</em>.
*
* <p>The returned comparator is serializable and throws {@link
* NullPointerException} when comparing {@code null}.
*
* @param <T> the {@link Comparable} type of element to be compared
* @return a comparator that imposes the reverse of the <i>natural
* ordering</i> on {@code Comparable} objects.
* @see Comparable
* @since 1.8
*/
public static <T extends Comparable<? super T>> Comparator<T> reverseOrder() {
return Collections.reverseOrder();
}
/**
* Returns a comparator that compares {@link Comparable} objects in natural
* order.
*
* <p>The returned comparator is serializable and throws {@link
* NullPointerException} when comparing {@code null}.
*
* @param <T> the {@link Comparable} type of element to be compared
* @return a comparator that imposes the <i>natural ordering</i> on {@code
* Comparable} objects.
* @see Comparable
* @since 1.8
*/
public static <T extends Comparable<? super T>> Comparator<T> naturalOrder() {
return (Comparator<T>) 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. If the specified comparator is {@code null},
* then the returned comparator considers all non-null values to be equal.
*
* <p>The returned comparator is serializable if the specified comparator
* is serializable.
*
* @param <T> 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, and compares non-null objects with the supplied
* {@code Comparator}.
* @since 1.8
*/
public static <T> Comparator<T> nullsFirst(Comparator<? super T> comparator) {
return new Comparators.NullComparator(true, 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. If the specified comparator is {@code null},
* then the returned comparator considers all non-null values to be equal.
*
* <p>The returned comparator is serializable if the specified comparator
* is serializable.
*
* @param <T> 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, and compares non-null objects with the supplied
* {@code Comparator}.
* @since 1.8
*/
public static <T> Comparator<T> nullsLast(Comparator<? super T> comparator) {
return new Comparators.NullComparator(false, comparator);
}
/**
* Accepts a function that extracts a sort key from a type {@code T}, and
* returns a {@code Comparator<T>} that compares by that sort key using
* the specified {@link Comparator}.
*
* <p>The returned comparator is serializable if the specified function
* and comparator are both serializable.
*
* @apiNote
* For example, to obtain a {@code Comparator} that compares {@code
* Person} objects by their last name ignoring case differences,
*
* <pre>{@code
* Comparator<Person> cmp = Comparator.comparing(
* Person::getLastName,
* String.CASE_INSENSITIVE_ORDER);
* }</pre>
*
* @param <T> the type of element to be compared
* @param <U> 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 either argument is null
* @since 1.8
*/
public static <T, U> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor,
Comparator<? super U> keyComparator)
{
Objects.requireNonNull(keyExtractor);
Objects.requireNonNull(keyComparator);
return (Comparator<T> & 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<T>} that compares by that sort key.
*
* <p>The returned comparator is serializable if the specified function
* is also serializable.
*
* @apiNote
* For example, to obtain a {@code Comparator} that compares {@code
* Person} objects by their last name,
*
* <pre>{@code
* Comparator<Person> byLastName = Comparator.comparing(Person::getLastName);
* }</pre>
*
* @param <T> the type of element to be compared
* @param <U> 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 <T, U extends Comparable<? super U>> Comparator<T> comparing(
Function<? super T, ? extends U> keyExtractor)
{
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & 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<T>} that compares by that
* sort key.
*
* <p>The returned comparator is serializable if the specified function
* is also serializable.
*
* @param <T> 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 <T> Comparator<T> comparing(ToIntFunction<? super T> keyExtractor) {
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & 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<T>} that compares by that
* sort key.
*
* <p>The returned comparator is serializable if the specified function is
* also serializable.
*
* @param <T> 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 <T> Comparator<T> comparing(ToLongFunction<? super T> keyExtractor) {
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & 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<T>} that compares by that
* sort key.
*
* <p>The returned comparator is serializable if the specified function
* is also serializable.
*
* @param <T> 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<T> Comparator<T> comparing(ToDoubleFunction<? super T> keyExtractor) {
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> Double.compare(keyExtractor.applyAsDouble(c1), keyExtractor.applyAsDouble(c2));
} }
} }
/* /*
* 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. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -32,16 +32,9 @@ import java.util.function.ToIntFunction; ...@@ -32,16 +32,9 @@ import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction; import java.util.function.ToLongFunction;
/** /**
* This class consists of {@code static} utility methods for comparators. Mostly * Package private supporting class for {@link Comparator}.
* factory method that returns a {@link Comparator}.
*
* <p> 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
*/ */
public class Comparators { class Comparators {
private Comparators() { private Comparators() {
throw new AssertionError("no instances"); throw new AssertionError("no instances");
} }
...@@ -51,231 +44,55 @@ public class Comparators { ...@@ -51,231 +44,55 @@ public class Comparators {
* *
* @see Comparable * @see Comparable
*/ */
private enum NaturalOrderComparator implements Comparator<Comparable<Object>> { enum NaturalOrderComparator implements Comparator<Comparable<Object>> {
INSTANCE; INSTANCE;
@Override @Override
public int compare(Comparable<Object> c1, Comparable<Object> c2) { public int compare(Comparable<Object> c1, Comparable<Object> c2) {
return c1.compareTo(c2); return c1.compareTo(c2);
} }
}
/**
* Returns a comparator that imposes the reverse of the <em>natural
* ordering</em>.
*
* <p>The returned comparator is serializable.
*
* @param <T> {@link Comparable} type
*
* @return A comparator that imposes the reverse of the <i>natural
* ordering</i> on a collection of objects that implement
* the {@link Comparable} interface.
* @see Comparable
*/
public static <T extends Comparable<? super T>> Comparator<T> reverseOrder() {
return Collections.reverseOrder();
}
/**
* Returns a comparator that imposes the reverse ordering of the specified
* {@link Comparator}.
*
* <p>The returned comparator is serializable (assuming the specified
* comparator is also serializable).
*
* @param <T> 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 <T> Comparator<T> reverseOrder(Comparator<T> cmp) {
Objects.requireNonNull(cmp);
return Collections.reverseOrder(cmp);
}
/**
* Gets a comparator compares {@link Comparable} type in natural order.
*
* @param <T> {@link Comparable} type
*/
public static <T extends Comparable<? super T>> Comparator<T> naturalOrder() {
return (Comparator<T>) NaturalOrderComparator.INSTANCE;
}
/**
* Gets a comparator compares {@link Map.Entry} in natural order on key.
*
* @param <K> {@link Comparable} key type
* @param <V> value type
*/
public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> naturalOrderKeys() {
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> c1.getKey().compareTo(c2.getKey());
}
/**
* Gets a comparator compares {@link Map.Entry} in natural order on value.
*
* @param <K> key type
* @param <V> {@link Comparable} value type
*/
public static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K,V>> naturalOrderValues() {
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> c1.getValue().compareTo(c2.getValue());
}
/**
* Gets a comparator compares {@link Map.Entry} by key using the given
* {@link 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 {@link Comparator}
*/
public static <K, V> Comparator<Map.Entry<K, V>> byKey(Comparator<? super K> cmp) {
Objects.requireNonNull(cmp);
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> cmp.compare(c1.getKey(), c2.getKey());
}
/**
* Gets a comparator compares {@link Map.Entry} by value using the given
* {@link Comparator}.
*
* @param <K> key type
* @param <V> value type
* @param cmp the value {@link Comparator}
*/
public static <K, V> Comparator<Map.Entry<K, V>> byValue(Comparator<? super V> cmp) {
Objects.requireNonNull(cmp);
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> cmp.compare(c1.getValue(), c2.getValue());
}
/** @Override
* Accepts a function that extracts a {@link java.lang.Comparable public Comparator<Comparable<Object>> reversed() {
* Comparable} sort key from a type {@code T}, and returns a {@code return Comparator.reverseOrder();
* Comparator<T>} 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<Person>} that compares {@code Person} objects by their last
* name.
*
* @param <T> the original element type
* @param <U> the {@link Comparable} type for comparison
* @param keyExtractor the function used to extract the {@link Comparable} sort key
*/
public static <T, U extends Comparable<? super U>> Comparator<T> comparing(Function<? super T, ? extends U> keyExtractor) {
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & 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<T>} that compares by that value.
*
* <p>The returned comparator is serializable assuming the specified
* function is also serializable.
*
* @see #comparing(Function)
* @param <T> the original element type
* @param keyExtractor the function used to extract the integer value
*/
public static <T> Comparator<T> comparing(ToIntFunction<? super T> keyExtractor) {
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & 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<T>} that compares by that value.
*
* <p>The returned comparator is serializable assuming the specified
* function is also serializable.
*
* @see #comparing(Function)
* @param <T> the original element type
* @param keyExtractor the function used to extract the long value
*/
public static <T> Comparator<T> comparing(ToLongFunction<? super T> keyExtractor) {
Objects.requireNonNull(keyExtractor);
return (Comparator<T> & Serializable)
(c1, c2) -> Long.compare(keyExtractor.applyAsLong(c1), keyExtractor.applyAsLong(c2));
} }
/** /**
* Accepts a function that extracts a {@code double} value from a type * Null-friendly comparators
* {@code T}, and returns a {@code Comparator<T>} that compares by that
* value.
*
* <p>The returned comparator is serializable assuming the specified
* function is also serializable.
*
* @see #comparing(Function)
* @param <T> the original element type
* @param keyExtractor the function used to extract the double value
*/ */
public static<T> Comparator<T> comparing(ToDoubleFunction<? super T> keyExtractor) { final static class NullComparator<T> implements Comparator<T>, Serializable {
Objects.requireNonNull(keyExtractor); private static final long serialVersionUID = -7569533591570686392L;
return (Comparator<T> & Serializable) private final boolean nullFirst;
(c1, c2) -> Double.compare(keyExtractor.applyAsDouble(c1), keyExtractor.applyAsDouble(c2)); // if null, non-null Ts are considered equal
} private final Comparator<T> real;
@SuppressWarnings("unchecked")
NullComparator(boolean nullFirst, Comparator<? super T> real) {
this.nullFirst = nullFirst;
this.real = (Comparator<T>) real;
}
/** @Override
* Constructs a lexicographic order from two {@link Comparator}s. For public int compare(T a, T b) {
* example, if you have comparators {@code byLastName} and {@code if (a == null) {
* byFirstName}, each of type {@code Comparator<Person>}, then {@code return (b == null) ? 0 : (nullFirst ? -1 : 1);
* compose(byLastName, byFirstName)} creates a {@code Comparator<Person>} } else if (b == null) {
* which sorts by last name, and for equal last names sorts by first name. return nullFirst ? 1: -1;
* } else {
* <p>The returned comparator is serializable assuming the specified return (real == null) ? 0 : real.compare(a, b);
* 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) {
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);
};
}
/** @Override
* Constructs a {@link BinaryOperator} which returns the lesser of two elements public Comparator<T> thenComparing(Comparator<? super T> other) {
* according to the specified {@code Comparator} Objects.requireNonNull(other);
* return new NullComparator(nullFirst, real == null ? other : real.thenComparing(other));
* @param comparator A {@code Comparator} for comparing the two values }
* @param <T> 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<T> BinaryOperator<T> lesserOf(Comparator<? super T> comparator) {
Objects.requireNonNull(comparator);
return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
}
/** @Override
* Constructs a {@link BinaryOperator} which returns the greater of two elements public Comparator<T> reversed() {
* according to the specified {@code Comparator} return new NullComparator(!nullFirst, real == null ? null : real.reversed());
* }
* @param comparator A {@code Comparator} for comparing the two values
* @param <T> 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<T> BinaryOperator<T> greaterOf(Comparator<? super T> comparator) {
Objects.requireNonNull(comparator);
return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
} }
} }
...@@ -28,6 +28,7 @@ package java.util; ...@@ -28,6 +28,7 @@ package java.util;
import java.util.function.BiConsumer; import java.util.function.BiConsumer;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import java.util.function.Function; import java.util.function.Function;
import java.io.Serializable;
/** /**
* An object that maps keys to values. A map cannot contain duplicate keys; * An object that maps keys to values. A map cannot contain duplicate keys;
...@@ -446,6 +447,74 @@ public interface Map<K,V> { ...@@ -446,6 +447,74 @@ public interface Map<K,V> {
* @see #equals(Object) * @see #equals(Object)
*/ */
int hashCode(); int hashCode();
/**
* Returns a comparator that compares {@link Map.Entry} in natural order on key.
*
* <p>The returned comparator is serializable and throws {@link
* NullPointerException} when comparing an entry with a null key.
*
* @param <K> the {@link Comparable} type of then map keys
* @param <V> the type of the map values
* @return a comparator that compares {@link Map.Entry} in natural order on key.
* @see Comparable
*/
public static <K extends Comparable<? super K>, V> Comparator<Map.Entry<K,V>> comparingByKey() {
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> c1.getKey().compareTo(c2.getKey());
}
/**
* Returns a comparator that compares {@link Map.Entry} in natural order on value.
*
* <p>The returned comparator is serializable and throws {@link
* NullPointerException} when comparing an entry with null values.
*
* @param <K> the type of the map keys
* @param <V> the {@link Comparable} type of the map values
* @return a comparator that compares {@link Map.Entry} in natural order on value.
* @see Comparable
*/
public static <K, V extends Comparable<? super V>> Comparator<Map.Entry<K,V>> comparingByValue() {
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> c1.getValue().compareTo(c2.getValue());
}
/**
* Returns a comparator that compares {@link Map.Entry} by key using the given
* {@link Comparator}.
*
* <p>The returned comparator is serializable if the specified comparator
* is also serializable.
*
* @param <K> the type of the map keys
* @param <V> the type of the map values
* @param cmp the key {@link Comparator}
* @return a comparator that compares {@link Map.Entry} by the key.
*/
public static <K, V> Comparator<Map.Entry<K, V>> comparingByKey(Comparator<? super K> cmp) {
Objects.requireNonNull(cmp);
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> cmp.compare(c1.getKey(), c2.getKey());
}
/**
* Returns a comparator that compares {@link Map.Entry} by value using the given
* {@link Comparator}.
*
* <p>The returned comparator is serializable if the specified comparator
* is also serializable.
*
* @param <K> the type of the map keys
* @param <V> the type of the map values
* @param cmp the value {@link Comparator}
* @return a comparator that compares {@link Map.Entry} by the value.
*/
public static <K, V> Comparator<Map.Entry<K, V>> comparingByValue(Comparator<? super V> cmp) {
Objects.requireNonNull(cmp);
return (Comparator<Map.Entry<K, V>> & Serializable)
(c1, c2) -> cmp.compare(c1.getValue(), c2.getValue());
}
} }
// Comparison and hashing // Comparison and hashing
......
...@@ -2938,13 +2938,13 @@ public class TreeMap<K,V> ...@@ -2938,13 +2938,13 @@ public class TreeMap<K,V>
public int characteristics() { public int characteristics() {
return (side == 0 ? Spliterator.SIZED : 0) | return (side == 0 ? Spliterator.SIZED : 0) |
Spliterator.DISTINCT | Spliterator.SORTED | Spliterator.ORDERED; Spliterator.DISTINCT | Spliterator.SORTED | Spliterator.ORDERED;
} }
@Override @Override
public Comparator<? super Map.Entry<K, V>> getComparator() { public Comparator<? super Map.Entry<K, V>> getComparator() {
return tree.comparator != null ? return tree.comparator != null ?
Comparators.byKey(tree.comparator) : null; Map.Entry.comparingByKey(tree.comparator) : null;
} }
} }
} }
...@@ -24,6 +24,9 @@ ...@@ -24,6 +24,9 @@
*/ */
package java.util.function; 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 * 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. * {@code BiFunction} where the operands and the result are all of the same type.
...@@ -35,4 +38,31 @@ package java.util.function; ...@@ -35,4 +38,31 @@ package java.util.function;
*/ */
@FunctionalInterface @FunctionalInterface
public interface BinaryOperator<T> extends BiFunction<T,T,T> { public interface BinaryOperator<T> extends BiFunction<T,T,T> {
/**
* Returns 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}
* @throws NullPointerException if the argument is null
*/
public static<T> BinaryOperator<T> minBy(Comparator<? super T> comparator) {
Objects.requireNonNull(comparator);
return (a, b) -> comparator.compare(a, b) <= 0 ? a : b;
}
/**
* Returns 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}
* @throws NullPointerException if the argument is null
*/
public static<T> BinaryOperator<T> maxBy(Comparator<? super T> comparator) {
Objects.requireNonNull(comparator);
return (a, b) -> comparator.compare(a, b) >= 0 ? a : b;
}
} }
...@@ -30,7 +30,6 @@ import java.util.ArrayList; ...@@ -30,7 +30,6 @@ import java.util.ArrayList;
import java.util.Collection; import java.util.Collection;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.Comparators;
import java.util.DoubleSummaryStatistics; import java.util.DoubleSummaryStatistics;
import java.util.EnumSet; import java.util.EnumSet;
import java.util.HashMap; import java.util.HashMap;
...@@ -78,7 +77,7 @@ import java.util.function.ToLongFunction; ...@@ -78,7 +77,7 @@ import java.util.function.ToLongFunction;
* *
* // Find highest-paid employee * // Find highest-paid employee
* Employee highestPaid = employees.stream() * Employee highestPaid = employees.stream()
* .collect(Collectors.maxBy(Comparators.comparing(Employee::getSalary))); * .collect(Collectors.maxBy(Comparator.comparing(Employee::getSalary)));
* *
* // Group employees by department * // Group employees by department
* Map<Department, List<Employee>> byDept * Map<Department, List<Employee>> byDept
...@@ -89,7 +88,7 @@ import java.util.function.ToLongFunction; ...@@ -89,7 +88,7 @@ import java.util.function.ToLongFunction;
* Map<Department, Employee> highestPaidByDept * Map<Department, Employee> highestPaidByDept
* = employees.stream() * = employees.stream()
* .collect(Collectors.groupingBy(Employee::getDepartment, * .collect(Collectors.groupingBy(Employee::getDepartment,
* Collectors.maxBy(Comparators.comparing(Employee::getSalary)))); * Collectors.maxBy(Comparator.comparing(Employee::getSalary))));
* *
* // Partition students into passing and failing * // Partition students into passing and failing
* Map<Boolean, List<Student>> passingFailing = * Map<Boolean, List<Student>> passingFailing =
...@@ -404,7 +403,7 @@ public final class Collectors { ...@@ -404,7 +403,7 @@ public final class Collectors {
* @implSpec * @implSpec
* This produces a result equivalent to: * This produces a result equivalent to:
* <pre>{@code * <pre>{@code
* reducing(Comparators.lesserOf(comparator)) * reducing(BinaryOperator.minBy(comparator))
* }</pre> * }</pre>
* *
* @param <T> the type of the input elements * @param <T> the type of the input elements
...@@ -413,7 +412,7 @@ public final class Collectors { ...@@ -413,7 +412,7 @@ public final class Collectors {
*/ */
public static <T> Collector<T, T> public static <T> Collector<T, T>
minBy(Comparator<? super T> comparator) { minBy(Comparator<? super T> comparator) {
return reducing(Comparators.lesserOf(comparator)); return reducing(BinaryOperator.minBy(comparator));
} }
/** /**
...@@ -423,7 +422,7 @@ public final class Collectors { ...@@ -423,7 +422,7 @@ public final class Collectors {
* @implSpec * @implSpec
* This produces a result equivalent to: * This produces a result equivalent to:
* <pre>{@code * <pre>{@code
* reducing(Comparators.greaterOf(comparator)) * reducing(BinaryOperator.maxBy(comparator))
* }</pre> * }</pre>
* *
* @param <T> the type of the input elements * @param <T> the type of the input elements
...@@ -432,7 +431,7 @@ public final class Collectors { ...@@ -432,7 +431,7 @@ public final class Collectors {
*/ */
public static <T> Collector<T, T> public static <T> Collector<T, T>
maxBy(Comparator<? super T> comparator) { maxBy(Comparator<? super T> comparator) {
return reducing(Comparators.greaterOf(comparator)); return reducing(BinaryOperator.maxBy(comparator));
} }
/** /**
...@@ -491,8 +490,8 @@ public final class Collectors { ...@@ -491,8 +490,8 @@ public final class Collectors {
* <p>For example, given a stream of {@code Person}, to calculate tallest * <p>For example, given a stream of {@code Person}, to calculate tallest
* person in each city: * person in each city:
* <pre>{@code * <pre>{@code
* Comparator<Person> byHeight = Comparators.comparing(Person::getHeight); * Comparator<Person> byHeight = Comparator.comparing(Person::getHeight);
* BinaryOperator<Person> tallerOf = Comparators.greaterOf(byHeight); * BinaryOperator<Person> tallerOf = BinaryOperator.greaterOf(byHeight);
* Map<City, Person> tallestByCity * Map<City, Person> tallestByCity
* = people.stream().collect(groupingBy(Person::getCity, reducing(tallerOf))); * = people.stream().collect(groupingBy(Person::getCity, reducing(tallerOf)));
* }</pre> * }</pre>
...@@ -531,8 +530,8 @@ public final class Collectors { ...@@ -531,8 +530,8 @@ public final class Collectors {
* <p>For example, given a stream of {@code Person}, to calculate the longest * <p>For example, given a stream of {@code Person}, to calculate the longest
* last name of residents in each city: * last name of residents in each city:
* <pre>{@code * <pre>{@code
* Comparator<String> byLength = Comparators.comparing(String::length); * Comparator<String> byLength = Comparator.comparing(String::length);
* BinaryOperator<String> longerOf = Comparators.greaterOf(byLength); * BinaryOperator<String> longerOf = BinaryOperator.greaterOf(byLength);
* Map<City, String> longestLastNameByCity * Map<City, String> longestLastNameByCity
* = people.stream().collect(groupingBy(Person::getCity, * = people.stream().collect(groupingBy(Person::getCity,
* reducing(Person::getLastName, longerOf))); * reducing(Person::getLastName, longerOf)));
......
...@@ -25,7 +25,6 @@ ...@@ -25,7 +25,6 @@
package java.util.stream; package java.util.stream;
import java.util.Comparator; import java.util.Comparator;
import java.util.Comparators;
import java.util.Iterator; import java.util.Iterator;
import java.util.Objects; import java.util.Objects;
import java.util.Optional; import java.util.Optional;
...@@ -512,12 +511,12 @@ abstract class ReferencePipeline<P_IN, P_OUT> ...@@ -512,12 +511,12 @@ abstract class ReferencePipeline<P_IN, P_OUT>
@Override @Override
public final Optional<P_OUT> max(Comparator<? super P_OUT> comparator) { public final Optional<P_OUT> max(Comparator<? super P_OUT> comparator) {
return reduce(Comparators.greaterOf(comparator)); return reduce(BinaryOperator.maxBy(comparator));
} }
@Override @Override
public final Optional<P_OUT> min(Comparator<? super P_OUT> comparator) { public final Optional<P_OUT> min(Comparator<? super P_OUT> comparator) {
return reduce(Comparators.lesserOf(comparator)); return reduce(BinaryOperator.minBy(comparator));
} }
......
...@@ -27,7 +27,6 @@ package java.util.stream; ...@@ -27,7 +27,6 @@ package java.util.stream;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparator; import java.util.Comparator;
import java.util.Comparators;
import java.util.Objects; import java.util.Objects;
import java.util.Spliterator; import java.util.Spliterator;
import java.util.concurrent.ForkJoinTask; import java.util.concurrent.ForkJoinTask;
...@@ -114,7 +113,7 @@ final class SortedOps { ...@@ -114,7 +113,7 @@ final class SortedOps {
StreamOpFlag.IS_ORDERED | StreamOpFlag.IS_SORTED); StreamOpFlag.IS_ORDERED | StreamOpFlag.IS_SORTED);
this.isNaturalSort = true; this.isNaturalSort = true;
// Will throw CCE when we try to sort if T is not Comparable // 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();
} }
/** /**
......
...@@ -43,7 +43,7 @@ import java.nio.file.Path; ...@@ -43,7 +43,7 @@ import java.nio.file.Path;
import java.nio.file.Paths; import java.nio.file.Paths;
import java.nio.file.attribute.BasicFileAttributes; import java.nio.file.attribute.BasicFileAttributes;
import java.util.Arrays; import java.util.Arrays;
import java.util.Comparators; import java.util.Comparator;
import java.util.Iterator; import java.util.Iterator;
import java.util.List; import java.util.List;
import java.util.Objects; import java.util.Objects;
...@@ -139,7 +139,7 @@ public class StreamTest { ...@@ -139,7 +139,7 @@ public class StreamTest {
public void testBasic() { public void testBasic() {
try (CloseableStream<Path> s = Files.list(testFolder)) { try (CloseableStream<Path> s = Files.list(testFolder)) {
Object[] actual = s.sorted(Comparators.naturalOrder()).toArray(); Object[] actual = s.sorted(Comparator.naturalOrder()).toArray();
assertEquals(actual, level1); assertEquals(actual, level1);
} catch (IOException ioe) { } catch (IOException ioe) {
fail("Unexpected IOException"); fail("Unexpected IOException");
...@@ -155,7 +155,7 @@ public class StreamTest { ...@@ -155,7 +155,7 @@ public class StreamTest {
public void testWalk() { public void testWalk() {
try (CloseableStream<Path> s = Files.walk(testFolder)) { try (CloseableStream<Path> s = Files.walk(testFolder)) {
Object[] actual = s.sorted(Comparators.naturalOrder()).toArray(); Object[] actual = s.sorted(Comparator.naturalOrder()).toArray();
assertEquals(actual, all); assertEquals(actual, all);
} catch (IOException ioe) { } catch (IOException ioe) {
fail("Unexpected IOException"); fail("Unexpected IOException");
...@@ -165,7 +165,7 @@ public class StreamTest { ...@@ -165,7 +165,7 @@ public class StreamTest {
public void testWalkOneLevel() { public void testWalkOneLevel() {
try (CloseableStream<Path> s = Files.walk(testFolder, 1)) { try (CloseableStream<Path> s = Files.walk(testFolder, 1)) {
Object[] actual = s.filter(path -> ! path.equals(testFolder)) Object[] actual = s.filter(path -> ! path.equals(testFolder))
.sorted(Comparators.naturalOrder()) .sorted(Comparator.naturalOrder())
.toArray(); .toArray();
assertEquals(actual, level1); assertEquals(actual, level1);
} catch (IOException ioe) { } catch (IOException ioe) {
...@@ -177,7 +177,7 @@ public class StreamTest { ...@@ -177,7 +177,7 @@ public class StreamTest {
// If link is not supported, the directory structure won't have link. // If link is not supported, the directory structure won't have link.
// We still want to test the behavior with FOLLOW_LINKS option. // We still want to test the behavior with FOLLOW_LINKS option.
try (CloseableStream<Path> s = Files.walk(testFolder, FileVisitOption.FOLLOW_LINKS)) { try (CloseableStream<Path> 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); assertEquals(actual, all_folowLinks);
} catch (IOException ioe) { } catch (IOException ioe) {
fail("Unexpected IOException"); fail("Unexpected IOException");
...@@ -637,13 +637,13 @@ public class StreamTest { ...@@ -637,13 +637,13 @@ public class StreamTest {
public void testClosedStream() throws IOException { public void testClosedStream() throws IOException {
try (CloseableStream<Path> s = Files.list(testFolder)) { try (CloseableStream<Path> s = Files.list(testFolder)) {
s.close(); s.close();
Object[] actual = s.sorted(Comparators.naturalOrder()).toArray(); Object[] actual = s.sorted(Comparator.naturalOrder()).toArray();
assertTrue(actual.length <= level1.length); assertTrue(actual.length <= level1.length);
} }
try (CloseableStream<Path> s = Files.walk(testFolder)) { try (CloseableStream<Path> s = Files.walk(testFolder)) {
s.close(); s.close();
Object[] actual = s.sorted(Comparators.naturalOrder()).toArray(); Object[] actual = s.sorted(Comparator.naturalOrder()).toArray();
fail("Operate on closed stream should throw IllegalStateException"); fail("Operate on closed stream should throw IllegalStateException");
} catch (IllegalStateException ex) { } catch (IllegalStateException ex) {
// expected // expected
...@@ -652,7 +652,7 @@ public class StreamTest { ...@@ -652,7 +652,7 @@ public class StreamTest {
try (CloseableStream<Path> s = Files.find(testFolder, Integer.MAX_VALUE, try (CloseableStream<Path> s = Files.find(testFolder, Integer.MAX_VALUE,
(p, attr) -> true)) { (p, attr) -> true)) {
s.close(); s.close();
Object[] actual = s.sorted(Comparators.naturalOrder()).toArray(); Object[] actual = s.sorted(Comparator.naturalOrder()).toArray();
fail("Operate on closed stream should throw IllegalStateException"); fail("Operate on closed stream should throw IllegalStateException");
} catch (IllegalStateException ex) { } catch (IllegalStateException ex) {
// expected // expected
......
...@@ -25,7 +25,6 @@ import java.util.ArrayList; ...@@ -25,7 +25,6 @@ import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collections; import java.util.Collections;
import java.util.Comparator; import java.util.Comparator;
import java.util.Comparators;
import java.util.List; import java.util.List;
import java.util.LinkedList; import java.util.LinkedList;
import java.util.Stack; import java.util.Stack;
...@@ -337,23 +336,23 @@ public class ListDefaults { ...@@ -337,23 +336,23 @@ public class ListDefaults {
CollectionSupplier.shuffle(list); CollectionSupplier.shuffle(list);
list.sort(null); list.sort(null);
CollectionAsserts.assertSorted(list, Comparators.<Integer>naturalOrder()); CollectionAsserts.assertSorted(list, Comparator.<Integer>naturalOrder());
if (test.name.startsWith("reverse")) { if (test.name.startsWith("reverse")) {
Collections.reverse(list); Collections.reverse(list);
} }
CollectionAsserts.assertContents(list, original); CollectionAsserts.assertContents(list, original);
CollectionSupplier.shuffle(list); CollectionSupplier.shuffle(list);
list.sort(Comparators.<Integer>naturalOrder()); list.sort(Comparator.<Integer>naturalOrder());
CollectionAsserts.assertSorted(list, Comparators.<Integer>naturalOrder()); CollectionAsserts.assertSorted(list, Comparator.<Integer>naturalOrder());
if (test.name.startsWith("reverse")) { if (test.name.startsWith("reverse")) {
Collections.reverse(list); Collections.reverse(list);
} }
CollectionAsserts.assertContents(list, original); CollectionAsserts.assertContents(list, original);
CollectionSupplier.shuffle(list); CollectionSupplier.shuffle(list);
list.sort(Comparators.<Integer>reverseOrder()); list.sort(Comparator.<Integer>reverseOrder());
CollectionAsserts.assertSorted(list, Comparators.<Integer>reverseOrder()); CollectionAsserts.assertSorted(list, Comparator.<Integer>reverseOrder());
if (!test.name.startsWith("reverse")) { if (!test.name.startsWith("reverse")) {
Collections.reverse(list); Collections.reverse(list);
} }
...@@ -390,8 +389,8 @@ public class ListDefaults { ...@@ -390,8 +389,8 @@ public class ListDefaults {
final List<Integer> copy = new ArrayList<>(list); final List<Integer> copy = new ArrayList<>(list);
final List<Integer> subList = list.subList(SUBLIST_FROM, SUBLIST_TO); final List<Integer> subList = list.subList(SUBLIST_FROM, SUBLIST_TO);
CollectionSupplier.shuffle(subList); CollectionSupplier.shuffle(subList);
subList.sort(Comparators.<Integer>naturalOrder()); subList.sort(Comparator.<Integer>naturalOrder());
CollectionAsserts.assertSorted(subList, Comparators.<Integer>naturalOrder()); CollectionAsserts.assertSorted(subList, Comparator.<Integer>naturalOrder());
// verify that elements [0, from) remain unmodified // verify that elements [0, from) remain unmodified
for (int i = 0; i < SUBLIST_FROM; i++) { for (int i = 0; i < SUBLIST_FROM; i++) {
assertTrue(list.get(i) == copy.get(i), assertTrue(list.get(i) == copy.get(i),
...@@ -412,8 +411,8 @@ public class ListDefaults { ...@@ -412,8 +411,8 @@ public class ListDefaults {
public void call(final List<Integer> list) { public void call(final List<Integer> list) {
final List<Integer> copy = new ArrayList<>(list); final List<Integer> copy = new ArrayList<>(list);
CollectionSupplier.shuffle(list); CollectionSupplier.shuffle(list);
list.sort(Comparators.<Integer>naturalOrder()); list.sort(Comparator.<Integer>naturalOrder());
CollectionAsserts.assertSorted(list, Comparators.<Integer>naturalOrder()); CollectionAsserts.assertSorted(list, Comparator.<Integer>naturalOrder());
} }
}); });
} }
......
/* /*
* Copyright (c) 2012, Oracle and/or its affiliates. All rights reserved. * Copyright (c) 2013, Oracle and/or its affiliates. All rights reserved.
* DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER. * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
* *
* This code is free software; you can redistribute it and/or modify it * This code is free software; you can redistribute it and/or modify it
...@@ -21,19 +21,16 @@ ...@@ -21,19 +21,16 @@
* questions. * questions.
*/ */
/* /**
* @test * @test
* @bug 8001667 8010279 * @summary Comparator default method tests
* @run testng BasicTest * @run testng BasicTest
*/ */
import java.util.TreeMap;
import java.util.Comparator; import java.util.Comparator;
import java.util.Comparators;
import java.util.AbstractMap;
import java.util.Map;
import org.testng.annotations.Test; import org.testng.annotations.Test;
import java.util.function.BinaryOperator;
import java.util.function.Function; import java.util.function.Function;
import java.util.function.ToIntFunction; import java.util.function.ToIntFunction;
import java.util.function.ToLongFunction; import java.util.function.ToLongFunction;
...@@ -41,12 +38,8 @@ import java.util.function.ToDoubleFunction; ...@@ -41,12 +38,8 @@ import java.util.function.ToDoubleFunction;
import static org.testng.Assert.assertEquals; import static org.testng.Assert.assertEquals;
import static org.testng.Assert.assertTrue; import static org.testng.Assert.assertTrue;
import static org.testng.Assert.assertSame;
import static org.testng.Assert.fail; import static org.testng.Assert.fail;
/**
* Unit tests for helper methods in Comparators
*/
@Test(groups = "unit") @Test(groups = "unit")
public class BasicTest { public class BasicTest {
private static class Thing { private static class Thing {
...@@ -97,7 +90,7 @@ public class BasicTest { ...@@ -97,7 +90,7 @@ public class BasicTest {
Thing[] things = new Thing[intValues.length]; Thing[] things = new Thing[intValues.length];
for (int i=0; i<intValues.length; i++) for (int i=0; i<intValues.length; i++)
things[i] = new Thing(intValues[i], 0L, 0.0, null); things[i] = new Thing(intValues[i], 0L, 0.0, null);
Comparator<Thing> comp = Comparators.comparing(new ToIntFunction<BasicTest.Thing>() { Comparator<Thing> comp = Comparator.comparing(new ToIntFunction<Thing>() {
@Override @Override
public int applyAsInt(Thing thing) { public int applyAsInt(Thing thing) {
return thing.getIntField(); return thing.getIntField();
...@@ -111,7 +104,7 @@ public class BasicTest { ...@@ -111,7 +104,7 @@ public class BasicTest {
Thing[] things = new Thing[longValues.length]; Thing[] things = new Thing[longValues.length];
for (int i=0; i<longValues.length; i++) for (int i=0; i<longValues.length; i++)
things[i] = new Thing(0, longValues[i], 0.0, null); things[i] = new Thing(0, longValues[i], 0.0, null);
Comparator<Thing> comp = Comparators.comparing(new ToLongFunction<BasicTest.Thing>() { Comparator<Thing> comp = Comparator.comparing(new ToLongFunction<Thing>() {
@Override @Override
public long applyAsLong(Thing thing) { public long applyAsLong(Thing thing) {
return thing.getLongField(); return thing.getLongField();
...@@ -125,7 +118,7 @@ public class BasicTest { ...@@ -125,7 +118,7 @@ public class BasicTest {
Thing[] things = new Thing[doubleValues.length]; Thing[] things = new Thing[doubleValues.length];
for (int i=0; i<doubleValues.length; i++) for (int i=0; i<doubleValues.length; i++)
things[i] = new Thing(0, 0L, doubleValues[i], null); things[i] = new Thing(0, 0L, doubleValues[i], null);
Comparator<Thing> comp = Comparators.comparing(new ToDoubleFunction<BasicTest.Thing>() { Comparator<Thing> comp = Comparator.comparing(new ToDoubleFunction<Thing>() {
@Override @Override
public double applyAsDouble(Thing thing) { public double applyAsDouble(Thing thing) {
return thing.getDoubleField(); return thing.getDoubleField();
...@@ -139,7 +132,7 @@ public class BasicTest { ...@@ -139,7 +132,7 @@ public class BasicTest {
Thing[] things = new Thing[doubleValues.length]; Thing[] things = new Thing[doubleValues.length];
for (int i=0; i<doubleValues.length; i++) for (int i=0; i<doubleValues.length; i++)
things[i] = new Thing(0, 0L, 0.0, stringValues[i]); things[i] = new Thing(0, 0L, 0.0, stringValues[i]);
Comparator<Thing> comp = Comparators.comparing(new Function<Thing, String>() { Comparator<Thing> comp = Comparator.comparing(new Function<Thing, String>() {
@Override @Override
public String apply(Thing thing) { public String apply(Thing thing) {
return thing.getStringField(); return thing.getStringField();
...@@ -150,16 +143,16 @@ public class BasicTest { ...@@ -150,16 +143,16 @@ public class BasicTest {
} }
public void testNaturalOrderComparator() { public void testNaturalOrderComparator() {
Comparator<String> comp = Comparators.naturalOrder(); Comparator<String> comp = Comparator.naturalOrder();
assertComparisons(stringValues, comp, comparisons); assertComparisons(stringValues, comp, comparisons);
} }
public void testReverseComparator() { public void testReverseComparator() {
Comparator<String> cmpr = Comparators.reverseOrder(); Comparator<String> cmpr = Comparator.reverseOrder();
Comparator<String> cmp = cmpr.reverseOrder(); Comparator<String> cmp = cmpr.reversed();
assertEquals(cmp.reverseOrder(), cmpr); assertEquals(cmp.reversed(), cmpr);
assertEquals(0, cmp.compare("a", "a")); assertEquals(0, cmp.compare("a", "a"));
assertEquals(0, cmpr.compare("a", "a")); assertEquals(0, cmpr.compare("a", "a"));
assertTrue(cmp.compare("a", "b") < 0); assertTrue(cmp.compare("a", "b") < 0);
...@@ -170,9 +163,9 @@ public class BasicTest { ...@@ -170,9 +163,9 @@ public class BasicTest {
public void testReverseComparator2() { public void testReverseComparator2() {
Comparator<String> cmp = (s1, s2) -> s1.length() - s2.length(); Comparator<String> cmp = (s1, s2) -> s1.length() - s2.length();
Comparator<String> cmpr = cmp.reverseOrder(); Comparator<String> cmpr = cmp.reversed();
assertEquals(cmpr.reverseOrder(), cmp); assertEquals(cmpr.reversed(), cmp);
assertEquals(0, cmp.compare("abc", "def")); assertEquals(0, cmp.compare("abc", "def"));
assertEquals(0, cmpr.compare("abc", "def")); assertEquals(0, cmpr.compare("abc", "def"));
assertTrue(cmp.compare("abcd", "def") > 0); assertTrue(cmp.compare("abcd", "def") > 0);
...@@ -181,71 +174,11 @@ public class BasicTest { ...@@ -181,71 +174,11 @@ public class BasicTest {
assertTrue(cmpr.compare("abc", "defg") > 0); assertTrue(cmpr.compare("abc", "defg") > 0);
} }
@Test(expectedExceptions=NullPointerException.class) private <T> void assertComparison(Comparator<T> cmp, T less, T greater) {
public void testReverseComparatorNPE() { assertTrue(cmp.compare(less, greater) < 0, "less");
Comparator<String> cmp = Comparators.reverseOrder(null); assertTrue(cmp.compare(less, less) == 0, "equal");
} assertTrue(cmp.compare(greater, greater) == 0, "equal");
assertTrue(cmp.compare(greater, less) > 0, "greater");
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 assertPairComparison(K k1, V v1, K k2, V v2,
Comparator<Map.Entry<K, V>> ck,
Comparator<Map.Entry<K, V>> cv) {
final Map.Entry<K, V> p11 = new AbstractMap.SimpleImmutableEntry<>(k1, v1);
final Map.Entry<K, V> p12 = new AbstractMap.SimpleImmutableEntry<>(k1, v2);
final Map.Entry<K, V> p21 = new AbstractMap.SimpleImmutableEntry<>(k2, v1);
final Map.Entry<K, V> p22 = new AbstractMap.SimpleImmutableEntry<>(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<Map.Entry<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() {
assertPairComparison(1, "ABC", 2, "XYZ",
Comparators.<Integer, String>naturalOrderKeys(),
Comparators.<Integer, String>naturalOrderValues());
} }
private static class People { private static class People {
...@@ -273,33 +206,15 @@ public class BasicTest { ...@@ -273,33 +206,15 @@ public class BasicTest {
new People("Jonah", "Doe", 10), new People("Jonah", "Doe", 10),
new People("John", "Cook", 54), new People("John", "Cook", 54),
new People("Mary", "Cook", 50), new People("Mary", "Cook", 50),
new People("Mary", null, 25),
new People("John", null, 27)
}; };
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((Function<People, String>) People::getFirstName);
Comparator<People> cmp2 = Comparators.comparing((Function<People, String>) People::getLastName);
Comparator<People> cmp = Comparators.compose(cmp1, cmp2);
assertPairComparison(people[0], people[0], people[1], people[1],
Comparators.<People, People>byKey(cmp),
Comparators.<People, People>byValue(cmp));
}
private <T> void assertComparison(Comparator<T> cmp, T less, T greater) {
assertTrue(cmp.compare(less, greater) < 0, "less");
assertTrue(cmp.compare(less, less) == 0, "equal");
assertTrue(cmp.compare(greater, less) > 0, "greater");
}
public void testComparatorDefaultMethods() { public void testComparatorDefaultMethods() {
Comparator<People> cmp = Comparators.comparing((Function<People, String>) People::getFirstName); Comparator<People> cmp = Comparator.comparing((Function<People, String>) People::getFirstName);
Comparator<People> cmp2 = Comparators.comparing((Function<People, String>) People::getLastName); Comparator<People> cmp2 = Comparator.comparing((Function<People, String>) People::getLastName);
// reverseOrder // reverseOrder
assertComparison(cmp.reverseOrder(), people[1], people[0]); assertComparison(cmp.reversed(), people[1], people[0]);
// thenComparing(Comparator) // thenComparing(Comparator)
assertComparison(cmp.thenComparing(cmp2), people[0], people[1]); assertComparison(cmp.thenComparing(cmp2), people[0], people[1]);
assertComparison(cmp.thenComparing(cmp2), people[4], people[0]); assertComparison(cmp.thenComparing(cmp2), people[4], people[0]);
...@@ -317,96 +232,138 @@ public class BasicTest { ...@@ -317,96 +232,138 @@ public class BasicTest {
assertComparison(cmp.thenComparing(People::getAgeAsDouble), people[1], people[5]); assertComparison(cmp.thenComparing(People::getAgeAsDouble), people[1], people[5]);
} }
public void testGreaterOf() {
// lesser public void testNullsFirst() {
assertSame(Comparators.greaterOf(Comparators.comparing( Comparator<String> strcmp = Comparator.nullsFirst(Comparator.naturalOrder());
(Function<People, String>) People::getFirstName)) Comparator<People> cmp = Comparator.<People, String>comparing(People::getLastName, strcmp)
.apply(people[0], people[1]), .thenComparing(People::getFirstName, strcmp);
people[1]); // Mary.null vs Mary.Cook - solve by last name
// euqal assertComparison(cmp, people[6], people[5]);
assertSame(Comparators.greaterOf(Comparators.comparing( // John.null vs Mary.null - solve by first name
(Function<People, String>) People::getLastName)) assertComparison(cmp, people[7], people[6]);
.apply(people[0], people[1]),
people[0]); // More than one thenComparing
// greater strcmp = Comparator.nullsFirst(Comparator.comparing((ToIntFunction<String>) String::length)
assertSame(Comparators.greaterOf(Comparators.comparing( .thenComparing(String.CASE_INSENSITIVE_ORDER));
(ToIntFunction<People>) People::getAge)) assertComparison(strcmp, null, "abc");
.apply(people[0], people[1]), assertComparison(strcmp, "ab", "abc");
people[0]); assertComparison(strcmp, "abc", "def");
assertEquals(0, strcmp.compare("abc", "ABC"));
// Ensure reverse still handle null properly
Comparator<String> strcmp2 = strcmp.reversed().thenComparing(Comparator.naturalOrder());
assertComparison(strcmp2, "abc", null);
assertComparison(strcmp2, "abc", "ab");
assertComparison(strcmp2, "def", "abc");
assertComparison(strcmp2, "ABC", "abc");
// Considering non-null values to be equal
Comparator<String> blind = Comparator.nullsFirst(null);
assertComparison(blind, null, "abc");
assertEquals(0, blind.compare("abc", "def"));
// reverse still consider non-null values to be equal
strcmp = blind.reversed();
assertComparison(strcmp, "abc", null);
assertEquals(0, strcmp.compare("abc", "def"));
// chain with another comparator to compare non-nulls
strcmp = blind.thenComparing(Comparator.naturalOrder());
assertComparison(strcmp, null, "abc");
assertComparison(strcmp, "abc", "def");
}
public void testNullsLast() {
Comparator<String> strcmp = Comparator.nullsLast(Comparator.naturalOrder());
Comparator<People> cmp = Comparator.<People, String>comparing(People::getLastName, strcmp)
.thenComparing(People::getFirstName, strcmp);
// Mary.null vs Mary.Cook - solve by last name
assertComparison(cmp, people[5], people[6]);
// John.null vs Mary.null - solve by first name
assertComparison(cmp, people[7], people[6]);
// More than one thenComparing
strcmp = Comparator.nullsLast(Comparator.comparing((ToIntFunction<String>) String::length)
.thenComparing(String.CASE_INSENSITIVE_ORDER));
assertComparison(strcmp, "abc", null);
assertComparison(strcmp, "ab", "abc");
assertComparison(strcmp, "abc", "def");
// Ensure reverse still handle null properly
Comparator<String> strcmp2 = strcmp.reversed().thenComparing(Comparator.naturalOrder());
assertComparison(strcmp2, null, "abc");
assertComparison(strcmp2, "abc", "ab");
assertComparison(strcmp2, "def", "abc");
assertComparison(strcmp2, "ABC", "abc");
// Considering non-null values to be equal
Comparator<String> blind = Comparator.nullsLast(null);
assertComparison(blind, "abc", null);
assertEquals(0, blind.compare("abc", "def"));
// reverse still consider non-null values to be equal
strcmp = blind.reversed();
assertComparison(strcmp, null, "abc");
assertEquals(0, strcmp.compare("abc", "def"));
// chain with another comparator to compare non-nulls
strcmp = blind.thenComparing(Comparator.naturalOrder());
assertComparison(strcmp, "abc", null);
assertComparison(strcmp, "abc", "def");
} }
public void testLesserOf() { public void testComposeComparator() {
// lesser // Longer string in front
assertSame(Comparators.lesserOf(Comparators.comparing( Comparator<String> first = (s1, s2) -> s2.length() - s1.length();
(Function<People, String>) People::getFirstName)) Comparator<String> second = Comparator.naturalOrder();
.apply(people[0], people[1]), Comparator<String> composed = first.thenComparing(second);
people[0]);
// euqal assertTrue(composed.compare("abcdefg", "abcdef") < 0);
assertSame(Comparators.lesserOf(Comparators.comparing( assertTrue(composed.compare("abcdef", "abcdefg") > 0);
(Function<People, String>) People::getLastName)) assertTrue(composed.compare("abcdef", "abcdef") == 0);
.apply(people[0], people[1]), assertTrue(composed.compare("abcdef", "ghijkl") < 0);
people[0]); assertTrue(composed.compare("ghijkl", "abcdefg") > 0);
// greater
assertSame(Comparators.lesserOf(Comparators.comparing(
(ToIntFunction<People>) People::getAge))
.apply(people[0], people[1]),
people[1]);
} }
public void testNulls() { public void testNulls() {
try { try {
Comparators.<String>naturalOrder().compare("abc", (String) null); Comparator.<String>naturalOrder().compare("abc", (String) null);
fail("expected NPE with naturalOrder"); fail("expected NPE with naturalOrder");
} catch (NullPointerException npe) {} } catch (NullPointerException npe) {}
try { try {
Comparators.<String>naturalOrder().compare((String) null, "abc"); Comparator.<String>naturalOrder().compare((String) null, "abc");
fail("expected NPE with naturalOrder"); fail("expected NPE with naturalOrder");
} catch (NullPointerException npe) {} } catch (NullPointerException npe) {}
try { try {
Comparators.<String>reverseOrder().compare("abc", (String) null); Comparator.<String>reverseOrder().compare("abc", (String) null);
fail("expected NPE with naturalOrder"); fail("expected NPE with naturalOrder");
} catch (NullPointerException npe) {} } catch (NullPointerException npe) {}
try { try {
Comparators.<String>reverseOrder().compare((String) null, "abc"); Comparator.<String>reverseOrder().compare((String) null, "abc");
fail("expected NPE with naturalOrder"); fail("expected NPE with naturalOrder");
} catch (NullPointerException npe) {} } catch (NullPointerException npe) {}
try { try {
Comparator<Map.Entry<String, String>> cmp = Comparators.byKey(null); Comparator<People> cmp = Comparator.comparing((Function<People, String>) null, Comparator.<String>naturalOrder());
fail("byKey(null) should throw NPE"); fail("comparing(null, cmp) should throw NPE");
} catch (NullPointerException npe) {} } catch (NullPointerException npe) {}
try { try {
Comparator<Map.Entry<String, String>> cmp = Comparators.byValue(null); Comparator<People> cmp = Comparator.comparing((Function<People, String>) People::getFirstName, null);
fail("byValue(null) should throw NPE"); fail("comparing(f, null) should throw NPE");
} catch (NullPointerException npe) {} } catch (NullPointerException npe) {}
try { try {
Comparator<People> cmp = Comparators.comparing((Function<People, String>) null); Comparator<People> cmp = Comparator.comparing((Function<People, String>) null);
fail("comparing(null) should throw NPE"); fail("comparing(null) should throw NPE");
} catch (NullPointerException npe) {} } catch (NullPointerException npe) {}
try { try {
Comparator<People> cmp = Comparators.comparing((ToIntFunction<People>) null); Comparator<People> cmp = Comparator.comparing((ToIntFunction<People>) null);
fail("comparing(null) should throw NPE"); fail("comparing(null) should throw NPE");
} catch (NullPointerException npe) {} } catch (NullPointerException npe) {}
try { try {
Comparator<People> cmp = Comparators.comparing((ToLongFunction<People>) null); Comparator<People> cmp = Comparator.comparing((ToLongFunction<People>) null);
fail("comparing(null) should throw NPE"); fail("comparing(null) should throw NPE");
} catch (NullPointerException npe) {} } catch (NullPointerException npe) {}
try { try {
Comparator<People> cmp = Comparators.comparing((ToDoubleFunction<People>) null); Comparator<People> cmp = Comparator.comparing((ToDoubleFunction<People>) null);
fail("comparing(null) should throw NPE"); fail("comparing(null) should throw NPE");
} catch (NullPointerException npe) {} } catch (NullPointerException npe) {}
try {
BinaryOperator<String> op = Comparators.lesserOf(null);
fail("lesserOf(null) should throw NPE");
} catch (NullPointerException npe) {}
try {
BinaryOperator<String> op = Comparators.greaterOf(null);
fail("lesserOf(null) should throw NPE");
} catch (NullPointerException npe) {}
} }
} }
/*
* 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.
*
* 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 API narrowing type test
* @run testng TypeTest
*/
import java.util.function.Function;
import java.util.Map;
import java.util.TreeMap;
import java.util.Comparator;
import org.testng.annotations.Test;
@Test(groups = "unit")
public class TypeTest {
static class Person {
String name;
static Comparator<Person> C = (p1, p2) -> p1.name.compareTo(p2.name);
Person(String name) {
this.name = name;
}
String getName() { return name; }
}
static class Employee extends Person {
int id;
static Comparator<Employee> C = (e1, e2) -> e1.id - e2.id;
Employee(int id, String name) {
super(name);
this.id = id;
}
}
static class Manager extends Employee {
long reports;
static Comparator<Manager> C = (e1, e2) -> (int) (e1.reports - e2.reports);
Manager(String name, int id, long reports) {
super(id, name);
this.reports = reports;
}
}
static <T> void assertOrder(T o1, T o2, Comparator<? super T> cmp) {
if (cmp.compare(o1, o2) > 0) {
System.out.println("Fail!!");
}
if (cmp.compare(o1, o2) == 0) {
System.out.println("Equal!!");
}
}
public static void main(String[] args) {
Manager m1 = new Manager("Manager", 2, 2000);
Manager m2 = new Manager("Manager", 4, 1300);
// Comparator<Employee> tmp = Person.C;
// Comparator<Manager> cmp = Employee.C.thenComparing(Person.C);
Comparator<Employee> cmp = Employee.C.thenComparing(Person.C);
assertOrder(m1, m2, Employee.C.thenComparing(Person.C));
assertOrder(m1, m2, cmp);
assertOrder(m1, new Employee(1, "Z"), Person.C);
assertOrder(new Employee(1, "Z"), m2, Employee.C);
assertOrder(m1, m2, Comparator.comparing(Employee::getName, String.CASE_INSENSITIVE_ORDER));
Map<String, Integer> map = new TreeMap<>();
map.entrySet().stream().sorted(Map.Entry.comparingByKey(String.CASE_INSENSITIVE_ORDER));
}
}
/*
* Copyright (c) 2012, 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.
*
* 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
* @bug 8009736 8010279
* @run testng EntryComparators
*/
import java.util.function.Function;
import java.util.Comparator;
import java.util.AbstractMap;
import java.util.Map;
import org.testng.annotations.Test;
import static org.testng.Assert.assertTrue;
import static org.testng.Assert.fail;
/**
* Unit tests for Map.Entry.comparing
*/
@Test(groups = "unit")
public class EntryComparators {
private <K, V> void assertPairComparison(K k1, V v1, K k2, V v2,
Comparator<Map.Entry<K, V>> ck,
Comparator<Map.Entry<K, V>> cv) {
final Map.Entry<K, V> p11 = new AbstractMap.SimpleImmutableEntry<>(k1, v1);
final Map.Entry<K, V> p12 = new AbstractMap.SimpleImmutableEntry<>(k1, v2);
final Map.Entry<K, V> p21 = new AbstractMap.SimpleImmutableEntry<>(k2, v1);
final Map.Entry<K, V> p22 = new AbstractMap.SimpleImmutableEntry<>(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<Map.Entry<K, V>> cmp = ck.thenComparing(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 = cv.thenComparing(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 testKVComparables() {
assertPairComparison(1, "ABC", 2, "XYZ",
Map.Entry.<Integer, String>comparingByKey(),
Map.Entry.<Integer, String>comparingByValue());
}
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),
};
public void testKVComparators() {
// Comparator<People> cmp = Comparator.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 = Comparator.comparing((Function<People, String>) People::getFirstName);
Comparator<People> cmp2 = Comparator.comparing((Function<People, String>) People::getLastName);
Comparator<People> cmp = cmp1.thenComparing(cmp2);
assertPairComparison(people[0], people[0], people[1], people[1],
Map.Entry.<People, People>comparingByKey(cmp),
Map.Entry.<People, People>comparingByValue(cmp));
}
public void testNulls() {
try {
Comparator<Map.Entry<String, String>> cmp = Map.Entry.comparingByKey(null);
fail("comparingByKey(null) should throw NPE");
} catch (NullPointerException npe) {}
try {
Comparator<Map.Entry<String, String>> cmp = Map.Entry.comparingByValue(null);
fail("comparingByValue(null) should throw NPE");
} catch (NullPointerException npe) {}
}
}
/*
* Copyright (c) 2012, 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.
*
* 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
* @bug 8009736 8010279
* @run testng BasicTest
*/
import java.util.Comparator;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.ToIntFunction;
import org.testng.annotations.Test;
import static java.util.function.BinaryOperator.minBy;
import static java.util.function.BinaryOperator.maxBy;
import static org.testng.Assert.assertSame;
import static org.testng.Assert.fail;
/**
* Unit tests for helper methods in Comparators
*/
@Test(groups = "unit")
public class BasicTest {
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),
};
public void testMaxBy() {
Comparator<People> cmp = Comparator.comparing((Function<People, String>) People::getFirstName);
// lesser
assertSame(maxBy(cmp).apply(people[0], people[1]), people[1]);
// euqal
cmp = Comparator.comparing((Function<People, String>) People::getLastName);
assertSame(maxBy(cmp).apply(people[0], people[1]), people[0]);
// greater
cmp = Comparator.comparing((ToIntFunction<People>) People::getAge);
assertSame(maxBy(cmp).apply(people[0], people[1]), people[0]);
}
public void testLesserOf() {
Comparator<People> cmp = Comparator.comparing((Function<People, String>) People::getFirstName);
// lesser
assertSame(minBy(cmp).apply(people[0], people[1]), people[0]);
// euqal
cmp = Comparator.comparing((Function<People, String>) People::getLastName);
assertSame(minBy(cmp).apply(people[0], people[1]), people[0]);
// greater
cmp = Comparator.comparing((ToIntFunction<People>) People::getAge);
assertSame(minBy(cmp).apply(people[0], people[1]), people[1]);
}
public void testNulls() {
try {
BinaryOperator<String> op = minBy(null);
fail("minBy(null) should throw NPE");
} catch (NullPointerException npe) {}
try {
BinaryOperator<String> op = maxBy(null);
fail("maxBy(null) should throw NPE");
} catch (NullPointerException npe) {}
}
}
...@@ -22,7 +22,6 @@ ...@@ -22,7 +22,6 @@
*/ */
package org.openjdk.tests.java.util.stream; package org.openjdk.tests.java.util.stream;
import java.util.Comparators;
import java.util.stream.LambdaTestHelpers; import java.util.stream.LambdaTestHelpers;
import java.util.stream.OpTestCase; import java.util.stream.OpTestCase;
import java.util.stream.StreamTestDataProvider; import java.util.stream.StreamTestDataProvider;
...@@ -109,9 +108,9 @@ public class SequentialOpTest extends OpTestCase { ...@@ -109,9 +108,9 @@ public class SequentialOpTest extends OpTestCase {
= new UnaryOperator[] { = new UnaryOperator[] {
(UnaryOperator<Stream<Integer>>) s -> s, (UnaryOperator<Stream<Integer>>) s -> s,
(UnaryOperator<Stream<Integer>>) s -> s.map(id), (UnaryOperator<Stream<Integer>>) s -> s.map(id),
(UnaryOperator<Stream<Integer>>) s -> s.sorted(Comparators.naturalOrder()), (UnaryOperator<Stream<Integer>>) s -> s.sorted(Comparator.naturalOrder()),
(UnaryOperator<Stream<Integer>>) s -> s.map(id).sorted(Comparators.naturalOrder()).map(id), (UnaryOperator<Stream<Integer>>) s -> s.map(id).sorted(Comparator.naturalOrder()).map(id),
(UnaryOperator<Stream<Integer>>) s -> s.filter(LambdaTestHelpers.pEven).sorted(Comparators.naturalOrder()).map(id), (UnaryOperator<Stream<Integer>>) s -> s.filter(LambdaTestHelpers.pEven).sorted(Comparator.naturalOrder()).map(id),
}; };
for (int c1Index = 0; c1Index < changers.length; c1Index++) { for (int c1Index = 0; c1Index < changers.length; c1Index++) {
......
...@@ -270,7 +270,7 @@ public class SliceOpTest extends OpTestCase { ...@@ -270,7 +270,7 @@ public class SliceOpTest extends OpTestCase {
public void testLimitSort() { public void testLimitSort() {
List<Integer> l = countTo(100); List<Integer> l = countTo(100);
Collections.reverse(l); Collections.reverse(l);
exerciseOps(l, s -> s.limit(10).sorted(Comparators.naturalOrder())); exerciseOps(l, s -> s.limit(10).sorted(Comparator.naturalOrder()));
} }
@Test(groups = { "serialization-hostile" }) @Test(groups = { "serialization-hostile" })
......
...@@ -40,10 +40,10 @@ public class SortedOpTest extends OpTestCase { ...@@ -40,10 +40,10 @@ public class SortedOpTest extends OpTestCase {
public void testSorted() { public void testSorted() {
assertCountSum(countTo(0).stream().sorted(), 0, 0); assertCountSum(countTo(0).stream().sorted(), 0, 0);
assertCountSum(countTo(10).stream().sorted(), 10, 55); assertCountSum(countTo(10).stream().sorted(), 10, 55);
assertCountSum(countTo(10).stream().sorted(cInteger.reverseOrder()), 10, 55); assertCountSum(countTo(10).stream().sorted(cInteger.reversed()), 10, 55);
List<Integer> to10 = countTo(10); List<Integer> to10 = countTo(10);
assertSorted(to10.stream().sorted(cInteger.reverseOrder()).iterator(), cInteger.reverseOrder()); assertSorted(to10.stream().sorted(cInteger.reversed()).iterator(), cInteger.reversed());
Collections.reverse(to10); Collections.reverse(to10);
assertSorted(to10.stream().sorted().iterator()); assertSorted(to10.stream().sorted().iterator());
...@@ -51,7 +51,7 @@ public class SortedOpTest extends OpTestCase { ...@@ -51,7 +51,7 @@ public class SortedOpTest extends OpTestCase {
Spliterator<Integer> s = to10.stream().sorted().spliterator(); Spliterator<Integer> s = to10.stream().sorted().spliterator();
assertTrue(s.hasCharacteristics(Spliterator.SORTED)); assertTrue(s.hasCharacteristics(Spliterator.SORTED));
s = to10.stream().sorted(cInteger.reverseOrder()).spliterator(); s = to10.stream().sorted(cInteger.reversed()).spliterator();
assertFalse(s.hasCharacteristics(Spliterator.SORTED)); assertFalse(s.hasCharacteristics(Spliterator.SORTED));
} }
...@@ -87,8 +87,8 @@ public class SortedOpTest extends OpTestCase { ...@@ -87,8 +87,8 @@ public class SortedOpTest extends OpTestCase {
assertSorted(result.iterator()); assertSorted(result.iterator());
assertContentsUnordered(data, result); assertContentsUnordered(data, result);
result = exerciseOps(data, s -> s.sorted(cInteger.reverseOrder())); result = exerciseOps(data, s -> s.sorted(cInteger.reversed()));
assertSorted(result.iterator(), cInteger.reverseOrder()); assertSorted(result.iterator(), cInteger.reversed());
assertContentsUnordered(data, result); assertContentsUnordered(data, result);
} }
...@@ -104,23 +104,23 @@ public class SortedOpTest extends OpTestCase { ...@@ -104,23 +104,23 @@ public class SortedOpTest extends OpTestCase {
assertContentsUnordered(data, result); assertContentsUnordered(data, result);
result = withData(data) result = withData(data)
.stream(s -> s.sorted(cInteger.reverseOrder()).sorted(cInteger.reverseOrder()), .stream(s -> s.sorted(cInteger.reversed()).sorted(cInteger.reversed()),
new CollectorOps.TestParallelSizedOp<Integer>()) new CollectorOps.TestParallelSizedOp<Integer>())
.exercise(); .exercise();
assertSorted(result, cInteger.reverseOrder()); assertSorted(result, cInteger.reversed());
assertContentsUnordered(data, result); assertContentsUnordered(data, result);
result = withData(data) result = withData(data)
.stream(s -> s.sorted().sorted(cInteger.reverseOrder()), .stream(s -> s.sorted().sorted(cInteger.reversed()),
new CollectorOps.TestParallelSizedOp<Integer>()) new CollectorOps.TestParallelSizedOp<Integer>())
.exercise(); .exercise();
assertSorted(result, cInteger.reverseOrder()); assertSorted(result, cInteger.reversed());
assertContentsUnordered(data, result); assertContentsUnordered(data, result);
result = withData(data) result = withData(data)
.stream(s -> s.sorted(cInteger.reverseOrder()).sorted(), .stream(s -> s.sorted(cInteger.reversed()).sorted(),
new CollectorOps.TestParallelSizedOp<Integer>()) new CollectorOps.TestParallelSizedOp<Integer>())
.exercise(); .exercise();
......
...@@ -22,7 +22,7 @@ ...@@ -22,7 +22,7 @@
*/ */
import java.util.Objects; import java.util.Objects;
import java.util.Comparators; import java.util.Comparator;
import sun.misc.JavaLangAccess; import sun.misc.JavaLangAccess;
import sun.misc.SharedSecrets; import sun.misc.SharedSecrets;
...@@ -48,7 +48,7 @@ public class NewUnsafeString { ...@@ -48,7 +48,7 @@ public class NewUnsafeString {
if (!benchmark.equals(constructorCopy)) { if (!benchmark.equals(constructorCopy)) {
throw new Error("Copy not equal"); throw new Error("Copy not equal");
} }
if (0 != Objects.compare(benchmark, constructorCopy, Comparators.naturalOrder())) { if (0 != Objects.compare(benchmark, constructorCopy, Comparator.naturalOrder())) {
throw new Error("Copy not equal"); throw new Error("Copy not equal");
} }
...@@ -58,7 +58,7 @@ public class NewUnsafeString { ...@@ -58,7 +58,7 @@ public class NewUnsafeString {
if (!benchmark.equals(jlaCopy)) { if (!benchmark.equals(jlaCopy)) {
throw new Error("Copy not equal"); throw new Error("Copy not equal");
} }
if (0 != Objects.compare(benchmark, jlaCopy, Comparators.naturalOrder())) { if (0 != Objects.compare(benchmark, jlaCopy, Comparator.naturalOrder())) {
throw new Error("Copy not equal"); throw new Error("Copy not equal");
} }
...@@ -68,7 +68,7 @@ public class NewUnsafeString { ...@@ -68,7 +68,7 @@ public class NewUnsafeString {
if (!constructorCopy.equals(jlaCopy)) { if (!constructorCopy.equals(jlaCopy)) {
throw new Error("Copy not equal"); throw new Error("Copy not equal");
} }
if (0 != Objects.compare(constructorCopy, jlaCopy, Comparators.naturalOrder())) { if (0 != Objects.compare(constructorCopy, jlaCopy, Comparator.naturalOrder())) {
throw new Error("Copy not equal"); throw new Error("Copy not equal");
} }
......
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册