java 8 stream grouping由复合变量之和组成

时间:2015-06-17 10:10:56

标签: java sorting java-8 grouping java-stream

我有一个类Something,其中包含一个实例变量Anything

class Anything {
    private final int id;
    private final int noThings;

    public Anything(int id, int noThings) {
        this.id = id;
        this.noThings = noThings;
    }
}

class Something {
    private final int parentId;
    private final List<Anything> anythings;

    private int getParentId() {
        return parentId;
    }

    private List<Anything> getAnythings() {
        return anythings;
    }

    public Something(int parentId, List<Anything> anythings) {
        this.parentId = parentId;
        this.anythings = anythings;
    }
}

给出Something s

列表
List<Something> mySomethings = Arrays.asList(
    new Something(123, Arrays.asList(new Anything(45, 65),
                                     new Anything(568, 15), 
                                     new Anything(145, 27))),
    new Something(547, Arrays.asList(new Anything(12, 123),
                                     new Anything(678, 76), 
                                     new Anything(98, 81))),
    new Something(685, Arrays.asList(new Anything(23, 57),
                                     new Anything(324, 67), 
                                     new Anything(457, 87))));

我想对它们进行排序,使得Something对象的排序取决于(Anything对象)noThings的总降序总和,然后是降序值( Anything对象)noThings

123 = 65+15+27 = 107(3rd)
547 = 123+76+81 = 280 (1st)
685 = 57+67+87 = 211 (2nd)

所以我最终得到了

List<Something> orderedSomethings = Arrays.asList(
    new Something(547, Arrays.asList(new Anything(12, 123),
                                     new Anything(98, 81), 
                                     new Anything(678, 76))),
    new Something(685, Arrays.asList(new Anything(457, 87),
                                     new Anything(324, 67), 
                                     new Anything(23, 57))),
    new Something(123, Arrays.asList(new Anything(45, 65),
                                     new Anything(145, 27), 
                                     new Anything(568, 15))));

我知道我可以获得每个Anything

Id个列表
Map<Integer, List<Anythings>> anythings
            = mySomethings.stream()
            .collect(Collectors.toMap(p->p.getParentId(),
                    p->p.getAnythings()))
            ;

但在那之后我有点卡住了。

3 个答案:

答案 0 :(得分:2)

除非我弄错了,否则你不可能一次完成这两种情况。但由于它们彼此独立(nothingsAnythingsSomething的总和与它们的顺序无关),这并不重要。只需一个接一个地排序。

要按Anytings SomethingsnoThings内的mySomethings.stream().map(Something::getAnythings) .forEach(as -> as.sort(Comparator.comparing(Anything::getNoThings) .reversed())); 进行排序:

Somethings

要按noThings Anythings的{​​{1}}之和对mySomethings.sort(Comparator.comparing((Something s) -> s.getAnythings().stream() .mapToInt(Anything::getNoThings).sum()) .reversed()); 进行排序:

Map<Something, Integer> sumsOfThings = mySomethings.stream()
        .collect(Collectors.toMap(s -> s, s -> s.getAnythings().stream()
                                                .mapToInt(Anything::getNoThings).sum()));

mySomethings.sort(Comparator.comparing(sumsOfThings::get).reversed());

请注意,这两种排序都会就地修改相应的列表。

正如@Tagir指出的那样,第二种类型将再次为排序中比较的每对Somethings计算Anythings的总和。如果列表很长,这可能非常浪费。相反,您可以先计算地图中的总和,然后只查找该值。

<head>

答案 1 :(得分:0)

最后,我向Something类添加了一个额外的方法。

public int getTotalNoThings() {
  return anythings.stream().collect(Collectors.summingInt(Anything::getNoThings));
}

然后我用这个方法按总noThings(desc)

排序
somethings = somethings.stream()
            .sorted(Comparator.comparing(Something::getTotalNoThings).reversed())
            .collect(Collectors.toList());

然后我使用上面建议的代码(谢谢!)按Anything实例noThings

排序
    somethings .stream().map(Something::getAnythings)
            .forEach(as -> as.sort(Comparator.comparing(Anything::getNoThings).reversed()));

再次感谢您的帮助。

答案 2 :(得分:0)

其他解决方案的问题在于,在排序期间总和不会存储在任何位置,因此在对大输入进行排序时,将对每行计算总和数次,从而降低性能。另一种解决方案是创建中间对(某事,总和),按总和排序,然后提取一些东西并忘记总和。以下是使用Stream API和SimpleImmutableEntry作为对类的方法:

List<Something> orderedSomethings = mySomethings.stream()
        .map(smth -> new AbstractMap.SimpleImmutableEntry<>(smth, smth
                .getAnythings().stream()
                .mapToInt(Anything::getNoThings).sum()))
        .sorted(Entry.<Something, Integer>comparingByValue().reversed())
        .map(Entry::getKey)
        .collect(Collectors.toList());

我的免费StreamEx库中有一些语法糖可以使代码更清晰:

List<Something> orderedSomethings = StreamEx.of(mySomethings)
        .mapToEntry(smth -> smth
                .getAnythings().stream()
                .mapToInt(Anything::getNoThings).sum())
        .reverseSorted(Entry.comparingByValue())
        .keys().toList();

至于对Anything内部进行排序:其他解决方案都可以。