【问题标题】:How to create a summary of a list of objects?如何创建对象列表的摘要?
【发布时间】:2016-03-07 10:37:55
【问题描述】:

我认为这是一个非常基本的问题,应该有解决方案,但我没有找到任何解决方案。我想我需要更多术语方面的帮助,我应该查找哪个术语来了解我的问题,但我非常感谢任何帮助。

无论如何,我想实现以下内容:

我有一个对象列表。每个对象都是下面的 ExampleClass 的形式:

public class ExampleClass {
    private String name;
    private Double firstDouble;
    private Double secondDouble;

    + constructor and a bunch of methods

}

基本上,我有一个名称变量和一组与 ExampleClass 的每个实例相关联的数字。 name 变量不是 id,因此列表中可能有多个 ExampleClasses 具有相同的名称,它们都有不同的编号。我想做的是从此列表中创建一个“摘要”:

  • 过滤掉 ExampleClass 的每个同名实例,因此在我的最终对象列表中,我没有两个同名变量的对象。
  • 我想对同名对象的 Double 变量进行操作。

假设我的列表中有以下示例类:

ExampleClass first = new ExampleClass("apple",1,4);
ExampleClass second = new ExampleClass("pear",6,12);
ExampleClass third = new ExampleClass("apple",5,2);
ExampleClass fourth = new ExampleClass("peach",1,2);
ExampleClass fifth = new ExampleClass("plum",10,25);

在这种情况下,我想从列表中删除第一个或第三个元素,因为它们具有相同的名称,并且我想对数字进行运算,例如将 1 和 5 相加以及将 4 和 2 相乘。

提前感谢您的帮助!

编辑:我可以用一堆循环来解决它。但是,我需要我的代码可读且尽可能高效。我不是在寻找具有嵌套循环的蛮力解决方案,如果有更好的解决方案,我很感兴趣。

【问题讨论】:

  • 那么...您尝试自己尝试解决什么问题?与其从 ExampleClass 创建 N 个对象,不如创建一个数组,循环遍历它并将每个元素的名称与数组的其余部分进行比较,然后执行您需要的操作。
  • @Frakcool 我可以用头脑优先的方法来解决它,创建一堆循环并在整个过程中进行交互。没有更好的选择吗?
  • 谷歌:“java 按名称分组元素”-> stackoverflow.com/questions/21678430/…
  • 使用某种排序算法(快速排序,...)并按“水果”排序,然后在一个循环中比较相邻元素并进行操作。

标签: java list sorting arraylist


【解决方案1】:

在 Java 8 中查找按名称分组的所有 X 值的总和非常容易:

// Find the sum of X and group by name
Map<String, Integer> sumXByName = Stream.of(first, second, third, fourth, fifth)
        .collect(groupingBy(ExampleClass::getName, 
                Collectors.<ExampleClass>summingInt(e -> e.getX())));

// Print the results
sumXByName.entrySet().stream()
    .map(e -> e.getKey() + " -> " + e.getValue())
    .forEach(System.out::println);

打印:

plum -> 10
apple -> 6
pear -> 6
peach -> 1

但是,求 X 和 Y 的乘积需要自定义收集器。

static class ExampleStatistics implements Consumer<ExampleClass> {

    private Integer sum = 0;
    private Integer product = null;

    @Override
    public void accept(ExampleClass value) {
        sum += value.getX();

        if (product == null) {
            product = value.getY();
        } else {
            product *= value.getY();
        }
    }

    public ExampleStatistics combine(ExampleStatistics other) {
        sum += other.sum;
        product *= other.product;
        return this;
    }

    public Integer getSum() {
        return sum;
    }

    public Integer getProduct() {
        return product;
    }

    @Override
    public String toString() {
        return String.format("Sum X = %d, Product Y = %d", sum, product);
    }

}

static class ExampleSummarizer 
    implements Collector<ExampleClass, ExampleStatistics, ExampleStatistics> {

    @Override
    public Supplier<ExampleStatistics> supplier() {
        return ExampleStatistics::new;
    }

    @Override
    public BiConsumer<ExampleStatistics, ExampleClass> accumulator() {
        return (r, t) -> r.accept(t);
    }

    @Override
    public BinaryOperator<ExampleStatistics> combiner() {
        return (r, t) -> r.combine(t);
    }

    @Override
    public Function<ExampleStatistics, ExampleStatistics> finisher() {
        return i -> i; // identity finish
    }

    @Override
    public Set<Collector.Characteristics> characteristics() {
        return Stream.of(Characteristics.IDENTITY_FINISH, Characteristics.UNORDERED)
                .collect(toSet());
    }

};

现在您可以轻松地总结对象:

// Summarize all examples and group by name
Map<String, ExampleStatistics> statsByName = Stream.of(first, second, third, fourth, fifth)
        .collect(groupingBy(ExampleClass::getName, new ExampleSummarizer()));

打印此地图将产生以下结果:

plum -> Sum X = 10, Product Y = 25
apple -> Sum X = 6, Product Y = 8
pear -> Sum X = 6, Product Y = 12
peach -> Sum X = 1, Product Y = 2

编辑:为方便起见,我使用整数。但是,有可用于双打的总结等价物,例如summingDouble.

class ExampleClass {
    private final String name;
    private final Integer x;
    private final Integer y;

    public ExampleClass(String name, Integer x, Integer y) {
        super();
        this.name = name;
        this.x = x;
        this.y = y;
    }

    public String getName() {
        return name;
    }

    public Integer getX() {
        return x;
    }

    public Integer getY() {
        return y;
    }
}

【讨论】:

    【解决方案2】:

    过滤掉每个同名的 ExampleClass 实例,所以在我的最终对象列表中,我没有两个同名变量的对象。

    1. 考虑让您的类实现equals(Object obj)Comparable 接口,或者提供一些创建Comparator 对象的方法。

    2. 为您的列表实现SortedSet 集合,例如TreeSet

    我想对对象的 Double 变量进行操作 同名。

    在将项目添加到列表之前,您可以使用任何Set 继承集合提供的contains(Object obj) 方法,以便如果该方法返回true,您可以对重复项执行某些操作。建议您保持equals 实现与Comparable 实现一致。

    【讨论】:

    • HashMap 可能会更好。甚至是HashSet
    【解决方案3】:

    代替

    ExampleClass first = new ExampleClass("apple",1,4);
    ExampleClass second = new ExampleClass("pear",6,12);
    ExampleClass third = new ExampleClass("apple",5,2);
    ExampleClass fourth = new ExampleClass("peach",1,2);
    ExampleClass fifth = new ExampleClass("plum",10,25);
    

    使用列表

    List<ExampleClass> list = new ArrayList<>(5);
    list.add(new ExampleClass("apple",1,4));
    ...
    

    删除第一个或第三个元素

    list.remove(1); 
    list.remove(3);
    

    其他的你可以自己做,我想。您应该只阅读有关 List、Map、Set 等的内容。

    【讨论】:

    • 删除第一个和第三个元素将使用list.remove(0); list.remove(1);(或list.remove(2); list.remove(0);,如果你有心的话)。但我认为这不是问题的重点。
    猜你喜欢
    • 2018-05-26
    • 1970-01-01
    • 1970-01-01
    • 2023-02-25
    • 2011-03-12
    • 2014-07-22
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多