Uploaded image for project: 'JDK'
  1. JDK
  2. JDK-8357219

Provide default methods min(T, T) and max(T, T) in Comparator interface

XMLWordPrintable

    • Icon: CSR CSR
    • Resolution: Unresolved
    • Icon: P4 P4
    • 26
    • core-libs
    • None
    • source
    • low
    • Hide
      As we are adding new default methods to an existing interface, there's a possibility of signature clash if override-equivalent methods were previously added in the implementations of these interfaces. The risk is considered to be low because:

      - Nowadays many users don't create new comparators directly, relying on combinator methods like `Comparator.comparing`.
      - Most of the implementations of `Comparator` interface are either functions (lambdas or method references) or declare only `compare` method.
      - If some implementations declare `min` and/or `max` method accepting two arguments, there are chances that these methods are source-compatible with proposed ones. A concrete example is the `Ordering` interface from the Guava library, which provides source-compatible `min()` and `max()` methods with the same semantics:
      https://guava.dev/releases/snapshot-jre/api/docs/com/google/common/collect/Ordering.html#max(E,E)

      It's still possible that some libraries provide non-generic versions of `min()` and `max()` methods in subtypes of `Comparator`, and this will cause a signature clash. For example, the following interface is not compilable anymore after the proposed changes:

      interface MyComparator<String> extends Comparator<String> {
        String min(String a, String b);
        String max(String a, String b);
      }

      However, binary compatibility is preserved.
      Show
      As we are adding new default methods to an existing interface, there's a possibility of signature clash if override-equivalent methods were previously added in the implementations of these interfaces. The risk is considered to be low because: - Nowadays many users don't create new comparators directly, relying on combinator methods like `Comparator.comparing`. - Most of the implementations of `Comparator` interface are either functions (lambdas or method references) or declare only `compare` method. - If some implementations declare `min` and/or `max` method accepting two arguments, there are chances that these methods are source-compatible with proposed ones. A concrete example is the `Ordering` interface from the Guava library, which provides source-compatible `min()` and `max()` methods with the same semantics: https://guava.dev/releases/snapshot-jre/api/docs/com/google/common/collect/Ordering.html#max(E,E) It's still possible that some libraries provide non-generic versions of `min()` and `max()` methods in subtypes of `Comparator`, and this will cause a signature clash. For example, the following interface is not compilable anymore after the proposed changes: interface MyComparator<String> extends Comparator<String> {   String min(String a, String b);   String max(String a, String b); } However, binary compatibility is preserved.
    • Java API
    • SE

      Summary

      New default methods min() and max() are added to the Comparator interface, which allow finding greater or smaller of two objects, according to this Comparator.

      Problem

      While we have Stream.min/max and Collections.min/max, often it's required to select a greater or smaller object of two. Doing this with existing APIs is unnecessarily complicated. E.g., given two objects a and b and a comparator comp, we have the following possibilities to find the maximal object:

      • comp.compare(a, b) > 0 ? a : b

        This explicit version mentions both a and b twice and not very readable, as it requires matching the sign (> or <) with right-hand operands of ?: to understand whether we are looking for maximum or minimum

      • Stream.of(a, b).max(comp).get()

        Creates an unnecessary Optional which is always present and should be explicitly unwrapped. Also, depending on JIT compiler used and compilation tier, it may create unnecessary performance overhead and heap objects.

      • BinaryOperator.maxBy(comp).apply(a, b)

        While being the longest in terms of characters, it's probably the best of existing alternatives. However, it's not very discoverable and creates an unnecessary function when we just want to get the value. Also, this version would require an explicit type argument if the comparator is defined for a more abstract type:

      Comparator<CharSequence> comp = Comparator.comparingInt(CharSequence::length);
      // Here <String> is necessary
      String max = BinaryOperator.<String>maxBy(comp).apply("long", "short");

      Solution

      It's suggested to extend the Comparator interface adding two default methods:

      public interface Comparator<T> {
      ...
          default <U extends T> U max(U o1, U o2) { ... }
      
          default <U extends T> U min(U o1, U o2) { ... }
      }

      The solution was preliminarily discussed in core-libs-dev in this thread: https://mail.openjdk.org/pipermail/core-libs-dev/2025-May/145638.html

      Specification

          /**
           * Returns the greater of two values according to this comparator.
           * If the arguments are equal with respect to this comparator,
           * the {@code o1} argument is returned.
           *
           * @param   o1   an argument.
           * @param   o2   another argument.
           * @param   <U> the type of the arguments and the result.
           * @return  the larger of {@code o1} and {@code o2} according to this comparator.
           * @throws  NullPointerException if an argument is null and this
           *          comparator does not permit null arguments
           * @throws  ClassCastException if the arguments' types prevent them from
           *          being compared by this comparator.
           *
           * @since 25
           */
          default <U extends T> U max(U o1, U o2) { ... }
      
          /**
           * Returns the smaller of two values according to this comparator.
           * If the arguments are equal with respect to this comparator,
           * the {@code o1} argument is returned.
           *
           * @param   o1   an argument.
           * @param   o2   another argument.
           * @param   <U> the type of the arguments and the result.
           * @return  the smaller of {@code o1} and {@code o2} according to this comparator.
           * @throws  NullPointerException if an argument is null and this
           *          comparator does not permit null arguments
           * @throws  ClassCastException if the arguments' types prevent them from
           *          being compared by this comparator.
           *
           * @since 25
           */
          default <U extends T> U min(U o1, U o2) { ... }

            tvaleev Tagir Valeev
            tvaleev Tagir Valeev
            Roger Riggs
            Votes:
            0 Vote for this issue
            Watchers:
            5 Start watching this issue

              Created:
              Updated: