嗯,这就是我想出的。我分两个阶段完成。
- 首先创建一个
TreeMap<Long,List<Order>>,其中列表根据uniqueID按降序排列。
- 然后遍历映射值并提取每个 List 元素的相同位置。先是 1,然后是 2,...
使用记录来简化示例。
record Order(long getCustomerId, long getUniqueId) {
@Override
public String toString() {
return String.format("Cid = %s, Uid = %s", getCustomerId,
getUniqueId);
}
}
数据和随机播放以确保随机化。添加了额外的值来为每个CustomerId创建不同数量的订单
List<Order> orders = new ArrayList<>(List.of(new Order(1, 87),
new Order(2, 22), new Order(3, 57), new Order(1, 66),
new Order(2, -11), new Order(3, 98), new Order(2, -4),
new Order(3, 41), new Order(1, 10), new Order(2, -14),
new Order(3, 10)));
Collections.shuffle(orders);
现在创建地图
- 按相反顺序排序,唯一 ID
- 在执行
groupingBy 时,每个列表将按照遇到的顺序构建,保留已排序的列表。由于这是一个TreeMap,因此每个列表都将按照与键相关的正确顺序。
Map<Long, List<Order>> map = orders.stream()
.sorted(Comparator.comparing(Order::getUniqueId)
.reversed())
.collect(Collectors.groupingBy(Order::getCustomerId,
TreeMap::new,
Collectors.toList()));
现在是最终列表的创建
- 首先迭代一个从 0 开始的索引。这是针对每个列表的
-
keys 不再重要,所以只需要值(即List<Order>)
- 随着每个索引的迭代,每个列表中的 Order 条目被映射到一个流
以及随后的新列表。
- 由于每个
CustomerId 的订单数量可能不同,因此会根据索引检查大小以过滤掉较短的列表。否则会抛出 IndexOutOfBounds 异常。
- 然后通过
takeWhile 检查新列表的大小。如果为空,则所有元素都已处理。然后,所有这些列表都按要求的顺序将flatMapped 转换为单个返回的List<Order>。
List<Order> result = Stream.iterate(0, i -> i + 1)
.map(i -> map.values().stream()
.filter(lst -> lst.size() > i)
.map(lst -> lst.get(i)).toList())
.takeWhile(lst -> lst.size() > 0)
.flatMap(List::stream).toList();
result.forEach(System.out::println);
打印
Cid = 1, Uid = 87
Cid = 2, Uid = 22
Cid = 3, Uid = 98
Cid = 1, Uid = 66
Cid = 2, Uid = -4
Cid = 3, Uid = 57
Cid = 1, Uid = 10
Cid = 2, Uid = -11
Cid = 3, Uid = 41
Cid = 2, Uid = -14
Cid = 3, Uid = 10