【问题标题】:java: Collect and combine data in a listjava:收集并组合列表中的数据
【发布时间】:2014-06-24 20:32:44
【问题描述】:

在我的程序中,我有一个植物列表,每个植物都有一个测量值(字符串)、天数(整数)、相机(整数)和复制数(整数)。我使用过滤器获得了所有想要的植物的列表:

List<Plant> selectPlants = allPlants.stream().filter(plant -> passesFilters(plant, filters)).collect(Collectors.toList());

我现在想做的是获取所有具有相同相机、测量和复制值的植物。并按天顺序组合它们。因此,如果我有 1、2、3、5 天,我想找到所有类似的植物并将值附加到 getValues(函数)所在的植物。

我向 Plant 添加了一个方法,该方法仅通过使用 addAll( new plant values ) 来附加值。

有没有什么方法可以做到这一点,而无需一遍又一遍地遍历列表以找到相似的植物,然后每天排序然后追加?我很抱歉这个问题的措辞很糟糕。

【问题讨论】:

  • 有几个答案已经弄清楚如何将“相似”植物分组在一起(假设“相似”意味着它们具有相同的相机、测量值和复制值)。既然已经收集到了类似的植物,那它们该怎么办呢?将哪些值附加到哪个工厂?

标签: java java-8 concatenation java-stream


【解决方案1】:

虽然Vakh’s answer 是正确的,但它过于复杂。

通常,实现您自己的密钥类的工作并没有得到回报。您可以使用 List 作为键,这意味着由于装箱原始值会产生轻微的开销,但考虑到我们在此处执行散列等操作,它可以忽略不计。

不必使用for 循环进行排序,更糟糕的是,还可以使用Comparator 的匿名内部类。为什么我们有流和 lambda?您可以使用(p1,p2) -&gt; p1.getDay()-p2.getDay() 之类的lambda 实现Comparator,或者更好的是Comparator.comparing(Plant::getDay)

此外,您可以一步完成整个操作。 sort 步骤将创建一个有序流,收集器将维护相遇顺序,因此您可以使用一个流进行排序和分组:

Map<List<?>, List<Plant>> groupedPlants =  allPlants.stream()
  .filter(plant -> passesFilters(plant, filters))
  .sorted(Comparator.comparing(Plant::getDay))
  .collect(Collectors.groupingBy(p ->
     Arrays.asList(p.getMeasurement(), p.getCamera(), p.getReplicateNumber())));

就是这样。

【讨论】:

  • 对不起,我有一个简单的问题。究竟是什么存储到地图的键和值中?我注意到键是大小为 4 的植物数组列表,而值也是大小为 3 的植物数组列表(例如)。
  • 不,这里的键应该有3 的大小,因为它是Arrays.asList(measurement,camera,replicateNumber) 形成你想要为每个组区分的元组的结果。此键列表包含上述值,而不是 Plants(不要被 String 表示混淆)。正如您在问题中所说,您想合并所有具有相同(measurement,camera,replicateNumber) 的植物。这些值可以具有任意大小,因为每个值列表都包含所有键值相等的Plants。
  • 我注意到在我对植物进行分组之后。将至少有一个工厂的时间值和常规值的大小不同,但它们在进入集合之前是相同的。你为什么认为这是?我检查了两个列表之前相等,但之后不相等。
【解决方案2】:

使用Collectors.groupBy

private static class PlantKey {
    private String measurement;
    private int camera;
    private int replicateNumber;
    // + constructor, getters, setters and haschode equals
}

Map<PlantKey, List<Plant>> groupedPlants = 
  allPlants.stream().filter(plant -> passesFilters(plant, filters))
                    .collect(Collectors.groupBy(p -> 
                               new PlantKey(p.getMeasurement(),
                                            p.getCamera(),
                                            p.getReplicateNumber())));

// order the list
for(List values : groupedPlants.values()) {
    Collections.sort(values, new Comparator<Plant>(){
                       @Override
                       public int compare(Plant p1, Plant p2) {
                           return p1.getDay() - p2.getDay();
                       }
                     });
}

【讨论】:

  • 你可以创建一个构造函数PlantKey(Plant p),然后你的groupBy变成:groupBy(PlantKey::new)
  • 你是对的,它会更优雅。使用流也可以使最终的排序更加优雅,并且只需将比较器实例化一次就更有效。
【解决方案3】:

我会按共同特征对它们进行分组并比较相似的结果。

for(List<Plant> plantGroup : allPlants.stream().collect(Collectors.groupingBy(
                p -> p.camera+'/'+p.measurement+'/'+p.replicate)).values()) {
    // compare the plants in the same group
}

【讨论】:

    【解决方案4】:

    有一个名为sorted 的函数在stream 上运行

    selectPlants.stream().sorted(Comparator.comparingInt(i -> i.day)).collect(Collectors.toList());
    

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2019-05-21
      • 2022-11-11
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      相关资源
      最近更新 更多