简体   繁体   English

是否有一个 Java 相当于 C# 的 LINQ “选择” function

[英]Is there a Java equivalent of C#'s LINQ "Select" function

LINQ has Enuemerable.Select , does Java have any equivalent? LINQ 有Enuemerable.Select ,Java 有什么等价物吗?

Say I have some class Record with three fields id , price , itemsSold and a list of these records from which I want to filter out the most popular item measured by which product sold the most items.假设我有一些 class记录,其中包含三个字段idpriceitemsSold和这些记录的列表,我想从中筛选出最受欢迎的商品,这些商品是根据哪种产品销售最多的商品来衡量的。

In LINQ I could do something like this在 LINQ 我可以做这样的事情

var mostPopularItem = records.GroupBy(sr => sr.Id)
       .Select(g => new
       {
           Id = g.Key,
           TotalSold = g.Sum(r => r.ItemsSold)
       })
       .OrderByDescending(i => i.TotalSold).First();

by using the Select(...) method to reconstruct into a suitable form.通过使用Select(...)方法重构为合适的形式。 How would I do the same in Java?我如何在 Java 中做同样的事情? I could simply use streams to extract the actual number我可以简单地使用流来提取实际数字

records.stream().map(r -> r.getItemsSold()).sorted(Comparator.reverseOrder()).collect(Collectors.toList)).get(0)

but this would only give me an array of the items sold sorted in descending order.但这只会给我一组按降序排列的已售商品。 Optimally I would like to end up with an object that contains the id and the itemsSold .最理想的是,我希望最终得到一个包含id和 itemsSold 的object

The equivalent of Select in Java is the map -function on Stream . Select中的 Select 相当于Stream上的map函数。 But in your case, you probably want to use the second parameter of groupingBy -collector to sum up the ItemsSold -values.但在您的情况下,您可能希望使用groupingBy -collector 的第二个参数来总结ItemsSold - 值。

Java doesn't have is anonymous objects, like you create in your Select -call. Java 没有匿名对象,就像您在Select调用中创建的那样。 So you would need to define a class or record to hold that data.因此,您需要定义一个 class 或记录来保存该数据。

Using records, you could do this:使用记录,您可以这样做:

record TotalSoldRecord(String id, int totalSold) {}

var mostPopularItem = records.stream()
    // Grouping by ID into a Map<String, int>, where the keys are 
    // the recordIds and the values are the sum of "itemsSold".
    .collect(Collectors.groupingBy(Record::getId, 
        Collectors.summingInt(Record::getItemsSold)))
    .entrySet()
    .stream()
    .map(entry -> new TotalSoldRecord(entry.getKey(), entry.getValue()))
    // Finding the TotalSoldRecord with the maximum totalSold.
    .max(Comparator.comparingInt(TotalSoldRecord::totalSold));

Note: You could also use Map.Entry as the result instead of mapping it to TotalSoldRecord .注意:您也可以使用Map.Entry作为结果,而不是将其映射到TotalSoldRecord

This should return the most popular item:这应该返回最受欢迎的项目:

var itemsSoldById = records.stream()
        .collect(groupingBy(rec -> rec.getId(), summingInt(rec -> rec.getItemsSold())));

var mostPopularItem = itemsSoldById
        .entrySet()
        .stream()
        .max(Comparator.comparingInt(Map.Entry::getValue))
        .orElseThrow(()-> new IllegalStateException("The list of records is empty"));

Previous version before my edit that did not group records by id:我编辑之前的上一个版本没有按 id 对记录进行分组:

   var mostPopularItem = records.stream()
        .sorted(Comparator.comparing(rec -> rec.getItemsSold()))
        .reduce((rec1 , rec2) -> rec1.getItemsSold() > rec2.getItemsSold() ? rec1 : rec2)
        .orElseThrow(()-> new IllegalStateException("The list of records is empty"));

Something like this?是这样的吗?

package example.stackoverflow;

import java.util.List;
import java.util.stream.Collectors;

public class Select {
    public static void main(String[] args) {
        record Thing(String id, int price, int itemsSold){}
        List<Thing> records = List.of(
                new Thing("a", 5, 14),
                new Thing("b", 4, 33),
                new Thing("c", 6, 10),
                new Thing("a", 5, 21),
                new Thing("c", 6, 12)
        );
        record PopularThing(String id, int totalSold){}
        records.stream()
                .collect(Collectors.groupingBy(Thing::id, Collectors.summingInt(Thing::itemsSold)))
                .entrySet().stream().map(e -> new PopularThing(e.getKey(), e.getValue()))
                .max((a,b) -> a.totalSold - b.totalSold)
                .ifPresent(System.out::println);
    }
}
        List<Record> recordList = new ArrayList<>();
        recordList.add(new Record(1L, new BigDecimal(2), 4));
        recordList.add(new Record(1L, new BigDecimal(2), 5));
        recordList.add(new Record(1L, new BigDecimal(2), 7));
        recordList.add(new Record(2L, new BigDecimal(2), 10));

        Map.Entry<Long, Integer> mostPopularItem = recordList.stream().collect(groupingBy(Record::getId, summingInt(Record::getItemsSold))).entrySet().stream().max(Map.Entry.comparingByValue()).orElse(null);

Output: Output:

Key = 1, Value = 16
public class Record {
    public Record(Long id, BigDecimal price, Integer itemsSold) {
        this.id = id;
        this.price = price;
        this.itemsSold = itemsSold;
    }
    private Long id;
    private BigDecimal price;
    private Integer itemsSold;

    public Long getId() {
        return id;
    }

    public void setId(Long id) {
        this.id = id;
    }

    public BigDecimal getPrice() {
        return price;
    }

    public void setPrice(BigDecimal price) {
        this.price = price;
    }

    public Integer getItemsSold() {
        return itemsSold;
    }

    public void setItemsSold(Integer itemsSold) {
        this.itemsSold = itemsSold;
    }
}

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

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