按对象值分组,计数然后按最大对象属性设置组密钥

Jer*_*rin 18 java grouping java-8 java-stream

我已经设法使用Java 8 Streams API编写解决方案,该解决方案首先按对象组合对象Route列表,然后计算每个组中对象的数量.它返回一个映射Route - > Long.这是代码:

Map<Route, Long> routesCounted = routes.stream()
                .collect(Collectors.groupingBy(gr -> gr, Collectors.counting()));
Run Code Online (Sandbox Code Playgroud)

而Route类:

public class Route implements Comparable<Route> {
    private long lastUpdated;
    private Cell startCell;
    private Cell endCell;
    private int dropOffSize;

    public Route(Cell startCell, Cell endCell, long lastUpdated) {
        this.startCell = startCell;
        this.endCell = endCell;
        this.lastUpdated = lastUpdated;
    }

    public long getLastUpdated() {
        return this.lastUpdated;
    }

    public void setLastUpdated(long lastUpdated) {
        this.lastUpdated = lastUpdated;
    }

    public Cell getStartCell() {
        return startCell;
    }

    public void setStartCell(Cell startCell) {
        this.startCell = startCell;
    }

    public Cell getEndCell() {
        return endCell;
    }

    public void setEndCell(Cell endCell) {
        this.endCell = endCell;
    }

    public int getDropOffSize() {
        return this.dropOffSize;
    }

    public void setDropOffSize(int dropOffSize) {
        this.dropOffSize = dropOffSize;
    }

    @Override
    /**
     * Compute hash code by using Apache Commons Lang HashCodeBuilder.
     */
    public int hashCode() {
        return new HashCodeBuilder(43, 59)
                .append(this.startCell)
                .append(this.endCell)
                .toHashCode();
    }

    @Override
    /**
     * Compute equals by using Apache Commons Lang EqualsBuilder.
     */
    public boolean equals(Object obj) {
        if (!(obj instanceof Route))
            return false;
        if (obj == this)
            return true;

        Route route = (Route) obj;
        return new EqualsBuilder()
                .append(this.startCell, route.startCell)
                .append(this.endCell, route.endCell)
                .isEquals();
    }

    @Override
    public int compareTo(Route route) {
        if (this.dropOffSize < route.dropOffSize)
            return -1;
        else if (this.dropOffSize > route.dropOffSize)
            return 1;
        else {
                // if contains drop off timestamps, order by last timestamp in drop off
                // the highest timestamp has preceding
            if (this.lastUpdated < route.lastUpdated)
                return -1;
            else if (this.lastUpdated > route.lastUpdated)
                return 1;
            else
                return 0;
        }
    }
}
Run Code Online (Sandbox Code Playgroud)

我想要另外实现的是每个组的密钥将是具有最大lastUpdated值的密钥.我已经在考虑这个解决方案,但我不知道如何将值和路由最大lastUpdated值组合在一起计算和分组.以下是我想要实现的示例数据:

例:

List<Route> routes = new ArrayList<>();
routes.add(new Route(new Cell(1, 2), new Cell(2, 1), 1200L));
routes.add(new Route(new Cell(3, 2), new Cell(2, 5), 1800L));
routes.add(new Route(new Cell(1, 2), new Cell(2, 1), 1700L));
Run Code Online (Sandbox Code Playgroud)

应该被转换为:

Map<Route, Long> routesCounted = new HashMap<>();
routesCounted.put(new Route(new Cell(1, 2), new Cell(2, 1), 1700L), 2);
routesCounted.put(new Route(new Cell(3, 2), new Cell(2, 5), 1800L), 1);
Run Code Online (Sandbox Code Playgroud)

请注意,映射的密钥(计算2个路由)是lastUpdated值最大的密钥.

Mis*_*sha 8

这是一种方法.首先将组分组到列表中,然后将列表处理为您实际需要的值:

import static java.util.Comparator.comparingLong;
import static java.util.stream.Collectors.groupingBy;
import static java.util.stream.Collectors.toMap;


Map<Route,Integer> routeCounts = routes.stream()
        .collect(groupingBy(x -> x))
        .values().stream()
        .collect(toMap(
            lst -> lst.stream().max(comparingLong(Route::getLastUpdated)).get(),
            List::size
        ));
Run Code Online (Sandbox Code Playgroud)


Tag*_*eev 7

您可以定义一个抽象的"库"方法,它将两个收集器合并为一个:

static <T, A1, A2, R1, R2, R> Collector<T, ?, R> pairing(Collector<T, A1, R1> c1, 
        Collector<T, A2, R2> c2, BiFunction<R1, R2, R> finisher) {
    EnumSet<Characteristics> c = EnumSet.noneOf(Characteristics.class);
    c.addAll(c1.characteristics());
    c.retainAll(c2.characteristics());
    c.remove(Characteristics.IDENTITY_FINISH);
    return Collector.of(() -> new Object[] {c1.supplier().get(), c2.supplier().get()},
            (acc, v) -> {
                c1.accumulator().accept((A1)acc[0], v);
                c2.accumulator().accept((A2)acc[1], v);
            },
            (acc1, acc2) -> {
                acc1[0] = c1.combiner().apply((A1)acc1[0], (A1)acc2[0]);
                acc1[1] = c2.combiner().apply((A2)acc1[1], (A2)acc2[1]);
                return acc1;
            },
            acc -> {
                R1 r1 = c1.finisher().apply((A1)acc[0]);
                R2 r2 = c2.finisher().apply((A2)acc[1]);
                return finisher.apply(r1, r2);
            }, c.toArray(new Characteristics[c.size()]));
}
Run Code Online (Sandbox Code Playgroud)

之后,实际操作可能如下所示:

Map<Route, Long> result = routes.stream()
        .collect(Collectors.groupingBy(Function.identity(),
            pairing(Collectors.maxBy(Comparator.comparingLong(Route::getLastUpdated)), 
                    Collectors.counting(), 
                    (route, count) -> new AbstractMap.SimpleEntry<>(route.get(), count))
            ))
        .values().stream().collect(Collectors.toMap(e -> e.getKey(), e -> e.getValue()));
Run Code Online (Sandbox Code Playgroud)

更新:我的StreamEx库中提供了这样的收集器:MoreCollectors.pairing().类似的收集器也在jOOL库中实现,因此您可以使用Tuple.collectors而不是pairing.

  • 好吧,用`Pair`或tuple类型替换`Object []`可以解决它.现在是JRE开发人员了... (3认同)
  • 这不是愚人节的笑话吗?哦等等,就是这样:http://blog.jooq.org/2015/04/01/dont-be-fooled-by-generics-and-backwards-compatibility-use-generic-generic-types/ (3认同)
  • @TagirValeev Plus一个用于努力和使用六个泛型类型变量.:-) (2认同)
  • @Holger在Java 9之后出现实际值类型之前,我们可能不会做这样的事情. (2认同)
  • 顺便说一句,刚发现类似的收集器出现在[jOOL](https://github.com/jOOQ/jOOL/blob/master/src/main/java/org/jooq/lambda/tuple/Tuple.java)库中.他们可以使用17个通用参数合并多达8个收藏家! (2认同)