java按对象值分组,计数,然后按最大对象属性设置组键
我已经成功地使用Java8StreamsAPI编写了一个解决方案,该解决方案首先根据对象路由的值对对象路由列表进行分组,然后统计每个组中的对象数。它返回一个映射路由->;长的代码如下:
Map<Route, Long> routesCounted = routes.stream()
.collect(Collectors.groupingBy(gr -> gr, Collectors.counting()));
路线类别:
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;
}
}
}
我想另外实现的是,每个组的关键是具有最大lastUpdated值的组。我已经在看this solution,但我不知道如何结合计数和分组(按值)以及路由最大LastUpdate值。以下是我想要实现的示例数据:
示例:
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));
应转换为:
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);
请注意,映射键(计为2条路由)是具有最大lastUpdated值的键
# 1 楼答案
将equals和hashcode更改为仅依赖于开始单元格和结束单元格
我的解决方案如下所示:
当然,对int的转换应该用更合适的东西来代替
# 2 楼答案
您可以定义一个抽象的“库”方法,该方法将两个收集器合并为一个:
之后,实际操作可能如下所示:
更新:此类收集器在myStreamEx库中提供:^{} 。在jOOL库中也实现了类似的收集器,因此您可以使用
Tuple.collectors
而不是pairing
# 3 楼答案
从原则上讲,这似乎应该是一次性的。通常的缺点是,这需要一个特别的元组或元组对,在本例中是一个
Route
和一个计数。由于Java缺少这些,我们最终使用了长度为2的对象数组(如Tagir Valeev's answer)、或AbstractMap.SimpleImmutableEntry
、或假设的Pair<A,B>
类另一种方法是编写一个包含
Route
和计数的小值类。当然,这样做会有一些痛苦,但在这种情况下,我认为这是值得的,因为它提供了一个放置组合逻辑的地方。这反过来又简化了流操作下面是包含
Route
和计数的value类:非常简单,但是请注意
combine
方法。它通过选择最近更新的Route
并使用计数总和来组合两个RouteCount
值。现在我们有了这个值类,我们可以编写一个单通道流来获得我们想要的结果:与其他答案一样,这会根据起始单元格和结束单元格将路由分组为等价类。用作键的实际
Route
实例并不重要;它只是这个阶层的代表。该值将是单个RouteCount
,其中包含最近更新的Route
实例,以及等效Route
实例的计数其工作方式是,具有相同起始和结束单元的每个
Route
实例随后被馈送到groupingBy
的下游收集器。此mapping
收集器将Route
实例映射到RouteCount
实例,然后将其传递给reducing
收集器,该收集器使用上述组合逻辑减少实例。collectingAndThen
的and then部分从Optional<RouteCount>
收集器生成的reducing
中提取值(通常裸
get
是危险的,但除非至少有一个值可用,否则我们根本无法到达该收集器。因此get
在这种情况下是安全的。)# 4 楼答案
这里有一种方法。首先将列表分组,然后将列表处理为实际需要的值: