【问题标题】:Group and sort by multiple attribute using stream: Java 8使用流按多个属性分组和排序:Java 8
【发布时间】:2020-08-15 08:06:59
【问题描述】:

我有 MainEntity

的列表
public class MainEntity {
private String keyword;
private double cost;
private String company;
}

我有 CompanyEntity

public class CompanyEntity {
    private double cost;
    private String company;
    }

我正在尝试将我的列表转换为Map<String,List<CompanyEntity>>,其中键为keywordList<CompanyEntity> 将具有所有成本的平均值并进行排序。我正在尝试在流和 Java 8 中做到这一点。

对于作为输入的特定关键字,我正在这样做。

List<MainEntity> entityList = keyWordMap.get(entity.getKeyword());
        entityList.add(entity);
        keyWordMap.put(entity.getKeyword(), entityList);

Map<String, Double> average = (keyWordMap.get(keyword)).stream()
            .collect(groupingBy(MainEntity::getCompany,
                    Collectors.averagingDouble(MainEntity::getCtr)));
    result.put(keyword, new ArrayList<>());

    for (Map.Entry<String, Double> entity : average.entrySet()) {
        result.get(keyword).add(new CompanyEntity(entity.getKey(), entity.getValue()));
    }

但我试图为所有关键字创建地图。是否有可能或再次迭代整个列表有意义? 目前keyowordMapMap&lt;String,MainEntity&gt; 类型,我是通过迭代MainEntity 列表来完成的,但我想要Map&lt;String,List&lt;MainEntity&gt;&gt;

【问题讨论】:

    标签: java collections java-8 java-stream


    【解决方案1】:

    首先,创建一个keyWordMap

    Map<String, List<MainEntity>> keyWordMap = 
                mainEntityList
                .stream()
                .collect(Collectors.groupingBy(MainEntity::getKeyword));
    

    然后对map进行迭代,对于每个关键字,可以直接得到CompanyEntity的列表,按平均值排序,用map()转换数据收集为List,然后放入result

    Map<String,List<CompanyEntity>> result = ....
    for (Map.Entry<String, List<MainEntity> entry : keyWordMap.entrySet()) {
        List<CompanyEntity> list = entry.getValue().stream()
                    .collect(groupingBy(MainEntity::getCompany,
                            Collectors.averagingDouble(MainEntity::getCtr)))
                    .entrySet()
                    .stream()
                    .sorted(Comparator.comparing(e -> e.getValue()))
                    .map(e -> new CompanyEntity(e.getKey(), e.getValue()))
                    .collect(Collectors.toList());
        result.put(entry.getKey(), list);
    }
    

    或者你想一次性完成

    Map<String,List<CompanyEntity>> mapData = 
               mainEntityList
                .stream()
                .collect(Collectors.groupingBy(MainEntity::getKeyWord,
                         Collectors.groupingBy(MainEntity::getCtr,
                            Collectors.averagingDouble(MainEntity::getCtr))))
                .entrySet()
                .stream()
                .collect(Collectors.toMap(m -> m.getKey(),
                     m -> m.entrySet()
                           .stream()
                           .sorted(Comparator.comparing(e -> e.getValue()))
                           .map(e -> new CompanyEntity(e.getKey(), e.getValue()))
                           .collect(Collectors.toList())));
    

    【讨论】:

    • 我正在尝试创建一个Map&lt;Keyword, List&lt;CompanyEntity&gt;&gt; 而不是一个关键字。
    • @AlokShukla 您只显示一个特定关键字的代码,并且您已经拥有keyWordMap 对吗?你还想要MainEntitykeyWordMap 的列表吗?
    • 我所拥有的是MainEntity("keyword1",1,"company 1"); MainEntity("keyword2",1,"company 1"); MainEntity("keyword1",1,"company 2"); MainEntity("keyword2",1,"company 2"); 我在关键字映射中所做的只是对未聚合的列表进行迭代。它就像Map&lt;String, MainEntity&gt;,但我正在尝试制作Map&lt;String, List&lt;Entity&gt;&gt;。也将编辑帖子以使其更清晰
    • @AlokShukla 还添加了一次性解决方案。
    • 您需要再次迭代列表,只需迭代地图并获取关键字实体列表并转换为 CompanyEntity 列表
    【解决方案2】:

    另一个答案在最初误解了这个问题后完全改变了它的答案,并且在良好的 StackOverflow 精神下,它吸引了第一个支持,因此现在被接受并获得最高支持。但这在代码中还有几个步骤显示正在发生的事情:

    这应该会给你结果:

    import java.io.IOException;
    import java.util.AbstractMap.SimpleEntry;
    import java.util.Comparator;
    import java.util.List;
    import java.util.Map;
    import java.util.Map.Entry;
    import java.util.function.Function;
    import java.util.stream.Collectors;
    import java.util.stream.Stream;
    import lombok.Value;
    
    public class CompanyEntityStackOverflowQuestion {
    
        public static void main(String[] args) throws IOException {
    
            //setup test data
            MainEntity one = new MainEntity("key1", 10D, "company1");
            MainEntity two = new MainEntity("key2", 5D, "company2");
            MainEntity three = new MainEntity("key1", 7D, "company3");
            MainEntity four = new MainEntity("key2", 3D, "company4");
            List<MainEntity> mainEntityList = List.of(one, two, three, four);
    
            //group list by keyword
            Map<String, List<MainEntity>> mainEntityByKeyword = mainEntityList.stream()
                .collect(Collectors.groupingBy(MainEntity::getKeyword));
    
            //map to companyEntity object
            Stream<SimpleEntry<String, List<CompanyEntity>>> mapped = mainEntityByKeyword.entrySet().stream()
                .map(entry -> new SimpleEntry<>(entry.getKey(), entry.getValue().stream().map(
                    getCompanyListFunction()).collect(Collectors.toList())));
    
            //sort and calculate average
            Stream<SimpleEntry<String, CompanyEntityListWithStats>> mappedToListWithStats = mapped
                .map(entry -> new SimpleEntry<>(entry.getKey(),
                    new CompanyEntityListWithStats(entry.getValue().stream().mapToDouble(company -> company.cost).average().orElse(0D), //or use Collectors.averagingDouble(company -> company.cost))
                        sortList(entry.getValue()))));
    
            //collect back to map
            Map<String, CompanyEntityListWithStats> collect = mappedToListWithStats
                .collect(Collectors.toMap(Entry::getKey, Entry::getValue));
    
            //show result
            System.out.println(collect);
        }
    
        //sort by cost
        private static List<CompanyEntity> sortList(List<CompanyEntity> list) {
            list.sort(Comparator.comparing(company -> company.cost));
            return list;
        }
    
        //map MainEntity to CompanyEntity
        private static Function<MainEntity, CompanyEntity> getCompanyListFunction() {
            return mainEntity -> new CompanyEntity(mainEntity.cost, mainEntity.company);
        }
    
        @Value
        public static class MainEntity {
    
            public String keyword;
            public double cost;
            public String company;
        }
    
        @Value
        public static class CompanyEntity {
    
            public double cost;
            public String company;
        }
    
        @Value
        public static class CompanyEntityListWithStats {
    
            public double average;
            public List<CompanyEntity> companyList;
        }
    
    
    }
    

    输出:{key1=CompanyEntityStackOverflowQuestion.CompanyEntityListWithStats(average=8.5, companyList=[CompanyEntityStackOverflowQuestion.CompanyEntity(cost=7.0, company=company3), CompanyEntityStackOverflowQuestion.CompanyEntity(cost=10.0, company=company1)]), key2=CompanyEntityStackOverflowQuestion.CompanyEntityListWithStats(average=4.0, companyList=[CompanyEntityStackOverflowQuestion.CompanyEntity(cost=3.0, company=company4), CompanyEntityStackOverflowQuestion.CompanyEntity(cost=5.0, company=company2)])}

    您可以跳过一些步骤,这只是快速输入。您当然可以内联内容以使其看起来更短/更清晰,但这种格式显示正在发生的事情。

    【讨论】:

      猜你喜欢
      • 2014-12-21
      • 1970-01-01
      • 1970-01-01
      • 2015-02-06
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多