应用某些函数后如何有效地计算集合的最大值

2022-09-03 06:57:43

假设你有一个像这样的方法,它计算一些的a的最大值:CollectionToIntFunction

static <T> void foo1(Collection<? extends T> collection, ToIntFunction<? super T> function) {
    if (collection.isEmpty())
        throw new NoSuchElementException();
    int max = Integer.MIN_VALUE;
    T maxT = null;
    for (T t : collection) {
        int result = function.applyAsInt(t);
        if (result >= max) {
            max = result;
            maxT = t;
        }
    }
    // do something with maxT
}

使用Java 8,这可以转化为

static <T> void foo2(Collection<? extends T> collection, ToIntFunction<? super T> function) {
    T maxT = collection.stream()
                       .max(Comparator.comparingInt(function))
                       .get();
    // do something with maxT
}

新版本的缺点是,对于 相同的值 重复调用 。(具体来说,如果集合的大小为 ,则调用时间,而调用时间)。function.applyAsIntTnfoo1applyAsIntnfoo22n - 2

第一种方法的缺点是代码不太清晰,您无法对其进行修改以使用并行性。

假设您希望使用并行流执行此操作,并且每个元素仅调用一次。这可以用简单的方式写吗?applyAsInt


答案 1

您可以使用自定义收集器来保持运行最大值和最大元素的对:

static <T> void foo3(Collection<? extends T> collection, ToIntFunction<? super T> function) {
    class Pair {
        int max = Integer.MIN_VALUE;
        T maxT = null;
    }
    T maxT = collection.stream().collect(Collector.of(
        Pair::new,
        (p, t) -> {
            int result = function.applyAsInt(t);
            if (result >= p.max) {
                p.max = result;
                p.maxT = t;
            }
        }, 
        (p1, p2) -> p2.max > p1.max ? p2 : p1,
        p -> p.maxT
    ));
    // do something with maxT
}

一个优点是,这将创建一个在整个收集过程中使用的中间对象。每次接受元素时,此持有者都会使用新的最大值进行更新。整理器操作仅返回最大元素并显示最大值。Pair


答案 2

正如我在评论中所说,我建议引入一个中间数据结构,如:

static <T> void foo2(Collection<? extends T> collection, ToIntFunction<? super T> function) {
  if (collection.isEmpty()) {
    throw new IllegalArgumentException();
  }
  class Pair {
    final T value;
    final int result;

    public Pair(T value, int result) {
      this.value = value;
      this.result = result;
    }

    public T getValue() {
      return value;
    }

    public int getResult() {
      return result;
    }
  }
  T maxT = collection.stream().map(t -> new Pair(t, function.applyAsInt(t)))
                     .max(Comparator.comparingInt(Pair::getResult)).get().getValue();
  // do something with maxT
}

推荐