IntStream可能是最简单的方法,但我只能选择最小的M个数字,如下所示:
public class Test {
private static final int[] arr = {5, 3, 4, 2, 9, 1, 7, 8, 6};
public static void main(String[] args) throws Exception {
System.out.println(Arrays.asList(IntStream.of(arr).sorted().limit(5).boxed().toArray()));
}
}
btw,考虑算法复杂度并假设N>> M,“排序+限制”方法只有O(N log(N))的复杂性。
我认为最好的复杂性可能达到O(N log(M)),但我不知道Java 8是否有这种流方法或收集器。
答案 0 :(得分:5)
如果必须使用Streams:
IntStream.of(arr).sorted().skip(N-M)
否则使用PriorityQueue
并为自己写一个反转Comparator
。插入将是 O(N(log(N))并且M元素的删除将是 O(M(log(N))。不是你要求的,但是也许够近了。
答案 1 :(得分:3)
EJP没错,我测试了它 - 当输入为2时产生8和9。
import java.util.stream.IntStream;
public class Test {
private static final int[] arr = {5, 3, 4, 2, 9, 1, 7, 8, 6};
public static void main(String[] args) throws Exception {
int n = Integer.parseInt(args[0]);
System.out.println("Finding "+n+" largest numbers in arr");
IntStream.of(arr).sorted().skip(arr.length-n).boxed().forEach(big -> System.out.println(big));
}
}
答案 2 :(得分:2)
如果您已在项目中使用google guava,则可以利用MinMaxPriorityQueue
:
Collection<..> min5 = stream.collect(
toCollection(MinMaxPriorityQueue.maximumSize(5)::create)
);
答案 3 :(得分:1)
可以使用JDK PriorityQueue
创建自定义收集器来解决您的任务:
public static <T> Collector<T, ?, List<T>> maxN(Comparator<? super T> comparator,
int limit) {
BiConsumer<PriorityQueue<T>, T> accumulator = (queue, t) -> {
queue.add(t);
if (queue.size() > limit)
queue.poll();
};
return Collector.of(() -> new PriorityQueue<>(limit + 1, comparator),
accumulator, (q1, q2) -> {
for (T t : q2) {
accumulator.accept(q1, t);
}
return q1;
}, queue -> new ArrayList<>(queue));
}
用法:
int[] arr = {5, 3, 4, 2, 9, 1, 7, 8, 6};
System.out.println(IntStream.of(arr).boxed().collect(maxN(Comparator.naturalOrder(), 2)));
// [8, 9]
System.out.println(IntStream.of(arr).boxed().collect(maxN(Comparator.reverseOrder(), 3)));
// [3, 1, 2]
对于大数据集和小限制可能更快,因为它不排序。如果需要排序结果,可以将排序步骤添加到finisher
。
答案 4 :(得分:1)
您可以通过创建值的直方图来实现复杂性目标:
public static IntStream maxValues(IntStream source, int limit) {
TreeMap<Integer,Integer> m=new TreeMap<>();
source.forEachOrdered(new IntConsumer() {
int size, min=Integer.MIN_VALUE;
public void accept(int value) {
if(value<min) return;
m.merge(value, 1, Integer::sum);
if(size<limit) size++;
else m.compute(min=m.firstKey(), (k,count)->count==1? null: count-1);
}
});
if(m.size()==limit)// no duplicates
return m.keySet().stream().mapToInt(Integer::valueOf);
return m.entrySet().stream().flatMapToInt(e->{
int value = e.getKey(), count = e.getValue();
return count==1? IntStream.of(value): IntStream.range(0, count).map(i->value);
});
}
它创建一个从int值到其相应出现次数的映射,但是将其内容限制为所需的值数,因此,它的操作具有O(log(M))
复杂度(最坏情况,如果没有重复),并且,因为对每个值执行一次操作,总体复杂度为O(N×log(M))
,如您所愿。
您可以使用原始数组测试它
int[] arr = {5, 3, 4, 2, 9, 1, 7, 8, 6};
maxValues(Arrays.stream(arr), 3).forEach(System.out::println);
但是为了测试一些极端情况,您可以使用包含重复项的数组,如
int[] arr = {8, 5, 3, 4, 2, 2, 9, 1, 7, 9, 8, 6};
// note that the stream of three max elements contains one of the two eights
如果您努力获得最佳性能,使用原始数据类型替换具有适当数据结构的装箱树图可能是可行的,但这可能是次要的性能优化,因为此解决方案已经解决了复杂性问题。
顺便说一句,这个解决方案适用于任意流,即不需要知道N
的值。