如何创建对象列表的摘要?

时间:2015-12-02 23:57:22

标签: java list sorting arraylist

我认为这是一个非常基本的问题,应该有解决方案,但我没有设法找到任何解决方案。我想我需要在术语方面提供更多帮助,关于我应该从哪个方面了解我的问题,但我真的很感激任何帮助。

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

我有一个对象列表。每个对象的形式如下:

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

    + constructor and a bunch of methods

}

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

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

让我们假设我的列表中有以下ExampleClasses:

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。

提前感谢您的帮助!

编辑:我可以通过一堆循环来解决它。但是,我需要我的代码具有可读性和高效性。我不是在寻找带有嵌套循环的强力解决方案,如果有一个好的或更好的解决方案我会感兴趣。

3 个答案:

答案 0 :(得分:2)

在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;
    }
}

答案 1 :(得分:1)

而不是

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等的内容。

答案 2 :(得分:1)

  

使用相同的名称过滤掉ExampleClass的每个实例,因此在我的最终对象列表中,我没有两个具有相同名称变量的对象。

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

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

  3.   

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

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