code: public static <T, R> Collector<T, ?, Optional<R>> minMax(final Comparator<? super T> comparator,
              final BiFunction<? super T, ? super T, ? extends R> finisher) {
  
          final BiFunction<Optional<T>, Optional<T>, Optional<R>> finisher2 = new BiFunction<Optional<T>, Optional<T>, Optional<R>>() {
              @Override
              public Optional<R> apply(Optional<T> min, Optional<T> max) {
                  return min.isPresent() ? Optional.of((R) finisher.apply(min.get(), max.get())) : Optional.<R> empty();
              }
          };
  
          return combine(Collectors.minBy(comparator), Collectors.maxBy(comparator), finisher2);
      }
docs: It's copied from StreamEx: https://github.com/amaembo/streamex under Apache License v2 and may be modified.
  <br />
  
  Returns a {@code Collector} which finds the minimal and maximal element
  according to the supplied comparator, then applies finisher function to
  them producing the final result.
  
  <p>
  This collector produces stable result for ordered stream: if several
  minimal or maximal elements appear, the collector always selects the
  first encountered.
  
  <p>
  If there are no input elements, the finisher method is not called and
  empty {@code Optional} is returned. Otherwise the finisher result is
  wrapped into {@code Optional}.
  
  @param <T> the type of the input elements
  @param <R> the type of the result wrapped into {@code Optional}
  @param comparator comparator which is used to find minimal and maximal
  element
  @param finisher a {@link BiFunction} which takes minimal and maximal
  element and produces the final result.
  @return a {@code Collector} which finds minimal and maximal elements.