這可以做到這一點,這是不明確的從你的問題,你不知道你想要的子集。只包括一個最大年齡的人(或者如果它恰好是第一個人),這是一個有效的答案。所以我假設你想獲得最大可能的這樣的子集。正如@tobias_k所注意到的,這可以通過按年齡分類輸入,減少並選擇平均不超過限制的最長前綴來解決。
不幸的是,這不能在使用標準流API的單個Stream中解決。一個可能的解決方案可能是這樣的:
public static List<Person> maxSubSetWithGreaterAverage(Collection<Person> persons,
int averageLimit) {
List<Person> list = new ArrayList<>(persons);
// Sort people by age, decreasing
list.sort(Comparator.comparingInt(Person::getAge).reversed());
// get all the ages
int[] ages = list.stream().mapToInt(Person::getAge).toArray();
// transform them to cumulative sums
Arrays.parallelPrefix(ages, Integer::sum);
// Find the longest prefix for which the cumulative sum is bigger
// than average
int length = IntStream.range(0, ages.length)
.filter(count -> ages[count] <= averageLimit * (count + 1)).findFirst()
.orElse(ages.length);
// return the corresponding subList
return list.subList(0, length);
}
用法:
List<Person> filtered = maxSubSetWithGreaterAverage(allAges,
allAges.peekFirst().getAge());
但是不使用流API和parallelPrefix
的解決方案看起來更好,工作速度更快,吃得少內存:
public static List<Person> maxSubSetWithGreaterAverage(Collection<Person> persons,
int averageLimit) {
List<Person> list = new ArrayList<>(persons);
list.sort(Comparator.comparingInt(Person::getAge).reversed());
int cumulativeAge = 0;
for(int i=0; i<list.size(); i++) {
cumulativeAge += list.get(i).getAge();
if(cumulativeAge <= averageLimit * (i + 1))
return list.subList(0, i);
}
return list;
}
使用我的StreamEx庫可以定義自定義intermed
public static <T> UnaryOperator<StreamEx<T>> takeWhileAverageGreater(
ToIntFunction<? super T> keyExtractor, int averageLimit) {
return s -> takeWhileAverageGreater(
s.sorted(Comparator.comparingInt(keyExtractor).reversed()),
keyExtractor, 0L, 0L, averageLimit);
}
private static <T> StreamEx<T> takeWhileAverageGreater(StreamEx<T> input,
ToIntFunction<? super T> keyExtractor, long count, long cumulativeSum,
int averageLimit) {
return input.headTail((head, tail) -> {
// head is the first element, tail is the Stream of the rest
// update current sum
long newSum = cumulativeSum + keyExtractor.applyAsInt(head);
// short-circuit via null if the limit is reached
// otherwise call myself for the tail prepending with head
return newSum <= averageLimit * (count + 1) ? null :
takeWhileAverageGreater(tail, keyExtractor, count + 1, newSum, averageLimit)
.prepend(head);
});
}
現在新takeWhileAverageGreater
操作可以像這樣使用:萊特操作將執行單流的必要的過濾,雖然這需要高級的魔法
List<Person> filtered = StreamEx.of(allAges)
.chain(takeWhileAverageGreater(Person::getAge, allAges.peekFirst().getAge()))
.toList();
的結果是一樣的。
可能有多種解決方案。你想保留哪一個? – Tunaki
如果你想要最大的這樣的子集,按照他們的年齡對人進行排序並且將人添加到集合中,從最老的開始,直到平均值剛好高於其他人的年齡。 –
另外,它是否必須使用流?在這裏使用常規的舊for循環和一些輔助變量可能更容易。 –