简体   繁体   English

如何从具有 Stream 的列表中获取具有最高值的 3 个对象 API

[英]How to fetch 3 objects having the highest values from a List with Stream API

I have a method like this:我有这样的方法:

public String mostExpensiveItems() {
        List<Entry> myList = getList();
        List<Double> expensive = myList.stream()
                .map(Entry::getAmount)
                .sorted(Comparator.reverseOrder())
                .limit(3)
                .toList();

        return "";
    }

This method needs to return the product IDs of the 3 most expensive items as a string like this:此方法需要以字符串形式返回3最昂贵商品的商品 ID ,如下所示:

"item1, item2, item3"

I should be able to use only streams and I got stuck here.我应该只能使用流,但我被困在这里了。 I should be able to sort the items by value then get the product IDs, but I can't seem to make it work.我应该能够按价值对项目进行排序,然后获取产品 ID,但我似乎无法让它工作。

Entry class条目 class

public class Entry {

    private String productId;
    private LocalDate date;
    private String state;
    private String category;
    private Double amount;

    public Entry(LocalDate orderDate, String state, String productId, String category, Double sales) {
        this.date = orderDate;
        this.productId = productId;
        this.state = state;
        this.category = category;
        this.amount = sales;
    }

    public String getProductId() {
        return productId;
    }

Assuming product ID is inside Entry, it can be something like this.假设产品 ID 在 Entry 中,它可以是这样的。

public String mostExpensiveItems() {
    List<Entry> myList = getList();
    List<String> expensive = myList.stream()
            .sorted(Comparator.comparing(Entry::getAmount).reversed())
            .limit(3)
            .map(Entry::getProductID)
            .toList();

    return "";
}

NB: I didn't test this out yet, but this should be able to convey the idea.注意:我还没有对此进行测试,但这应该能够传达这个想法。

You don't to sort all the given data for this task.您不需要为此任务对所有给定数据进行排序。 Because sorting is overkill , when only need to fetch 3 largest values.因为排序是大材小用,只需要取出3最大值。

Because sorting the hole data set will cost O(n log n) time.因为对孔数据集进行排序会花费O(n log n)的时间。 Meanwhile, this task can be done in a single pass through the list, maintaining only 3 largest previously encountered values in the sorted order.同时,这个任务可以在单次遍历列表中完成,只维护3最大的先前遇到的排序顺序的值。 And time complexity will be very close to a linear time .而且时间复杂度会非常接近线性时间

To implement the partial sorting with streams, you can define a custom collector (an object that is responsible for accumulating the data from the stream).要使用流实现部分排序,您可以定义一个自定义收集器(负责从流中收集数据的 object)。

You can create a custom collector either inline by using one of the versions of the static method Collector.of() or by creating a class that implements the Collector interface.您可以通过使用 static 方法Collector.of()的版本之一内联或通过创建实现Collector接口的class来创建自定义收集器。

These are parameters that you need to provide while defining a custom collector:这些是您在定义自定义收集器时需要提供的参数:

  • Supplier Supplier<A> is meant to provide a mutable container which store elements of the stream. In this case because we need to perform a partial sorting , PriorityQueue will be handy for that purpose as a mutable container. Supplier Supplier<A>旨在提供一个可变容器,用于存储 stream 的元素。在这种情况下,因为我们需要执行部分排序PriorityQueue将作为可变容器方便地用于该目的。
  • Accumulator BiConsumer<A,T> defines how to add elements into the container provided by the supplier . Accumulator BiConsumer<A,T> 定义如何将元素添加到供应商提供的容器中。 For this task, the accumulator needs to guarantee that queue will not exceed the given size by rejecting values that are smaller than the lowest value previously added to the queue and by removing the lowest value if the size has reached the limit a new value needs to be added.对于此任务,累加器需要通过拒绝小于先前添加到队列中的最低值的值来保证队列不会超过给定大小,并且如果大小已达到新值需要的限制,则删除最低值被添加。
  • Combiner BinaryOperator<A> combiner() establishes a rule on how to merge two containers obtained while executing stream in parallel. Combiner BinaryOperator<A> combiner()建立了关于如何合并并行执行 stream 时获得的两个容器的规则。 Here combiner rely on the same logic that was described for accumulator.这里的组合器依赖于为累加器描述的相同逻辑。
  • Finisher Function<A,R> is meant to produce the final result by transforming the mutable container. Finisher Function<A,R>旨在通过转换可变容器来产生最终结果。 The finisher function in the code below turns the queue into an immutable list.下面代码中的 finisher function 将队列变成一个不可变列表。
  • Characteristics allow to provide additional information, for instance Collector.Characteristics.UNORDERED which is used in this case denotes that the order in which partial results of the reduction produced while executing in parallel is not significant, which can improve performance of this collector with parallel streams. Characteristics允许提供额外的信息,例如Collector.Characteristics.UNORDERED ,在这种情况下使用的表示并行执行时产生的部分归约结果的顺序并不重要,这可以提高并行流收集器的性能.

Note that with Collector.of() only supplier , accumulator and combiner are mandatory, other parameters are defined if needed.请注意,使用Collector.of()只有supplieraccumulatorcombiner是强制性的,其他参数在需要时定义。

The method below that generates a collector would be more reusable if we apply generic type parameter to it and declare it to expect a comparator as a parameter (will be used in the constructor of the PriorityQueue and while adding elements to the queue).如果我们将泛型类型参数应用于它并声明它期望将比较器作为参数(将在PriorityQueue的构造函数中使用并向队列添加元素时),则下面生成收集器的方法将更具可重用性。

Custom collector:自定义收集器:

public static <T> Collector<T, ?, List<T>> getMaxN(int size, Comparator<T> comparator) {
    
    return Collector.of(
        () -> new PriorityQueue<>(comparator),
        (Queue<T> queue, T next) -> tryAdd(queue, next, comparator, size),
        (Queue<T> left, Queue<T> right) -> {
            right.forEach(next -> tryAdd(left, next, comparator, size));
            return left;
        },
        (Queue<T> queue) -> queue.stream().toList(),
        Collector.Characteristics.UNORDERED);
}

public static <T> void tryAdd(Queue<T> queue, T next, Comparator<T> comparator, int size) {
    if (queue.size() == size && comparator.compare(queue.element(), next) < 0) queue.remove(); // if next value is greater than the smallest element in the queue and max size has been exceeded the smallest element needs to be removed from the queue
    if (queue.size() < size) queue.add(next);
}

Stream: Stream:

public static <T> String getMostExpensive(List<T> list, Function<T, String> function,
                                          Comparator<T> comparator, int limit) {
    
    return list.stream()
        .collect(getMaxN(limit, comparator))
        .stream()
        .map(function)
        .collect(Collectors.joining(", "));
}

main() - demo with dummy Entries that expects only amount as a parameter. main() - 带有仅需要amount作为参数的虚拟Entries的演示。

public static void main(String[] args) {
    List<Entry> entries =
        List.of(new Entry("item1", 2.6), new Entry("item2", 3.5), new Entry("item3", 5.7),
                new Entry("item4", 1.9), new Entry("item5", 3.2), new Entry("item6", 9.5),
                new Entry("item7", 7.2), new Entry("item8", 8.1), new Entry("item9", 7.9));

    System.out.println(getMostExpensive(entries, Entry::getProductId,
                                        Comparator.comparingDouble(Entry::getAmount), 3));
}

Output Output

[item9, item6, item8] // final result is not sorted PriorityQueue maintains the elements in unsorted order (sorting happens only while dequeue operation happens), if these values are requeted to be sorted it could be done by changing the finisher function

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM