我的答案将是带有SortedMap 的MC Emperor's anwer 版本,但此版本会在找到得分较高的单个玩家后立即丢弃“较低”组。这个版本适用于当元素真的很多时,保留所有可见的元素可能会成为问题。例如,当从一个非常大的文件中读取流式项目时,该文件将无法放入内存中。
整个解决方案如下所示:
List<Player> topScore = playersStream().collect(
topGroup(Comparator.comparingInt(Player::getHandScore))
);
要使其正常工作,您将需要一个带有状态容器的自定义收集器来保存组。我不确定 JDK 中是否有类似的东西(无论如何都不是 8),但您可能可以在其中一个库中找到它。面向外的方法如下所示:
static <T> Collector<T, ?, List<T>> topGroup(final Comparator<? super T> comparator) {
Objects.requireNonNull(comparator, "comparator");
return Collector.of(
() -> new Group<>(comparator),
// My local compiler can't infer type properly, I had to help it.
// Your experience may be different
(BiConsumer<Group<T>, T>) Group::accept,
Group::merge,
Group::asList
);
}
最重要的部分是有状态的Group<T>。它的目的是成为外部比较器认为排序最高的元素的容器。一旦遇到更高阶的元素,该组就会丢弃其所有先前的内容。示例实现是:
private static class Group<T> {
private final Comparator<? super T> comparator;
T sample;
List<T> more;
public Group(Comparator<? super T> comparator) {
this.comparator = comparator;
}
public void accept(T el) {
if (sample == null) {
sample = el;
}
else {
int order = comparator.compare(sample, el);
if (order == 0) {
more().add(el);
}
else if (order > 0) {
// element of a higher order, discard everything and make it a sample
sample = el;
more = null;
}
// else {element of a lower order, ignore}
}
}
public Group<T> merge(Group<T> other) {
if (this.comparator != other.comparator) {
throw new IllegalArgumentException("Cannot merge groups with different orders");
}
if (sample == null) {
return other; // we're empty
}
int order = comparator.compare(this.sample, other.sample);
if (order >= 0) {
if (order == 0) {
// merge with other group
more().addAll(other.asList());
}
return this;
}
else {
// other group is higher than us
return other;
}
}
public List<T> asList() {
List<T> result = new ArrayList<>();
if (sample != null) {
result.add(sample);
}
if (more != null) {
result.addAll(more);
}
return result;
}
}
这个实现也是解决寻找“Top N with ties”问题的一个途径(我的实现是“Top 1 with ties”)。