Warning: file_get_contents(/data/phpspider/zhask/data//catemap/9/java/344.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
要按大于最旧元素的平均值筛选的Java 8流_Java_Filter_Java 8_Java Stream_Deque - Fatal编程技术网

要按大于最旧元素的平均值筛选的Java 8流

要按大于最旧元素的平均值筛选的Java 8流,java,filter,java-8,java-stream,deque,Java,Filter,Java 8,Java Stream,Deque,我试图过滤城市中的一群人,使他们的平均年龄大于数据库中创建时间戳最早的人的年龄 我在做下面的事情 LinkedBlockingDeque<Person> allAges = null; LinkedBlockingDeque<Person> filteredAges = new LinkedBlockingDeque<Person>(); allAges = ageStorage.getAllAgesByCityOrderByInsertionTime("c

我试图过滤城市中的一群
,使他们的平均年龄大于数据库中创建时间戳最早的
人的年龄

我在做下面的事情

LinkedBlockingDeque<Person> allAges = null;
LinkedBlockingDeque<Person> filteredAges = new LinkedBlockingDeque<Person>();

allAges = ageStorage.getAllAgesByCityOrderByInsertionTime("city A");

allAges.stream()
       .filter(this.getFirstInsertedAgeGreaterThanAverage(allAges))
       .forEach(filteredAges::add);

我想这里有一些不太正确的地方,但不确定是什么…有没有一种方法可以不用
getfirstinsertedegreeaterthanaverage
方法来实现这一点从你的问题中不清楚你到底想要哪个子集。只包括一个年龄最大的人(如果恰好是第一个,则不包括任何人)是一个有效的答案。所以我假设你想要得到最大可能的子集。正如@tobias_k所注意到的,这可以通过按年龄排序、递减和选择平均值不超过限制的最长前缀来解决

不幸的是,使用标准流API无法在单个流中解决这一问题。可能的解决方案如下所示:

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 = StreamEx.of(allAges)
        .chain(takeWhileAverageGreater(Person::getAge, allAges.peekFirst().getAge()))
        .toList();

使用我的库,可以定义自定义中间操作,该操作将在单个流中执行必要的过滤,尽管这需要高级魔术:

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);
    });
}

结果是一样的。

可能有多种解决方案。您希望保留哪一个?如果您希望保留最大的此类子集,请按年龄对人员进行排序,并将人员添加到集合中,从最年长的开始,直到平均值刚好高于另一个人的年龄。此外,是否必须使用流?在这里,使用一个常规的for循环和一些helper变量可能会容易得多。这应该是一个技巧性的问题吗?平均值不可能大于最大值。通过选择最大/最早的元素本身(仅此),可以得到的最大平均值就是最大值。或者,如果存在多个元素,则可能所有元素都与最古老的元素具有相同的年龄。当然,选择任何其他项都会有一个较小的平均值。@g0c00l.g33k如果任务是返回集合的平均年龄是否大于第一个插入的人的年龄,这是另一回事。您编写的
谓词
代码为每个元素返回相同的结果,因为它不检查传递到谓词中的
Person p->
的任何值。
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;
}
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);
    });
}
List<Person> filtered = StreamEx.of(allAges)
        .chain(takeWhileAverageGreater(Person::getAge, allAges.peekFirst().getAge()))
        .toList();