我遇到了一個讓我困惑的問題。我有一個類:
class Order{
private long customerId;
private long uniqueId;
現在讓我們假設我們有3個客戶的共9個訂單物件。每個客戶有三個訂單。所有這些物件都在一個串列中。
我需要使用一個比較器對這個串列進行排序,使串列中的每個客戶id首先包含最大的唯一id。然后是每個客戶id的第二大唯一id,以此類推。
最終串列示例
最終串列示例:
- order( customerId:1 , uniqueId:87)
- 訂單( customerId:2 , uniqueId:22)
- order( customerId:3 , uniqueId:57)
- 訂單( customerId:1 , uniqueId:66)
- order( customerId:2 , uniqueId:-4)
- 訂單(customerId:3 , uniqueId:41)
- 訂單(customerId:1 , uniqueId:10)
- order( customerId:2 , uniqueId:-11)
- order( customerId:3 , uniqueId:10)
我如何在'int compare(o1, o2)'方法中實作這一點,以按照要求對串列進行排序?
是否有其他更好的方法來實作這個目標?
uj5u.com熱心網友回復:
一個單一的compare方法可能不是在這里使用的正確方法。在一個單一的Order中的資訊并不足以確定其相對的排序。你在compare中要做的是:
- 獲取所有具有
Order的訂單。 - 獲取所有具有相同客戶ID的訂單,并按其訂單ID降序排序 。
- 對另一個訂單做同樣的事情 。
- 比較等級,如果它們是相同的,則比較客戶ID本身 。
而且你必須為每個被比較的訂單對重復這樣做。
相反,你應該使這成為一個多階段的程序:
- 首先,按客戶ID分割訂單,例如,使用
Collector.groupingBy。
- 按訂單ID對這些批次逐一排序 。
- 使用兩個嵌套回圈來合并這些訂單,取第一個客戶的第一個訂單,第二個客戶的第一個訂單,等等;確保處理不同客戶有不同數量訂單的情況 。
另外,你可以將第一種和第二種方法結合起來,而不是最后的合并步驟(這可能是上述方法中最棘手的部分):
- 按客戶分組訂單,對其進行排序,在地圖中存盤每個訂單的 "等級" 使用
compare函式對所有訂單進行排序,比較這些等級(和客戶ID作為平局)。
下面是最后一種(混合)方法的一些Java代碼。不太漂亮;有一段時間沒有做Java了,找不到第二步的好方法。
// group by CustomerId
Map<Long, List<Order>> byCustomer = orders.stream()
.collect(Collectors.groupingBy(Order::getCustomerId))。
//build Map of Ranks per CustomerId.
Map<Order, Integer> ranks = new HashMap<>()。
for (List<Order> group : byCustomer.values() ) {
group.sort(Comparator.comparing(Order::getUniqueId).reversed())。
for (int i = 0; i < group.size(); i ) {
ranks.put(group.get(i), i);
}
}
//按等級排序,然后按客戶身份排序。
List<Order> orderedOrders = orders.stream()
.sorted(Comparator.comparing((Order o) -> ranks.get(o))
.thenComparing(Order::getCustomerId))
.collect(Collectors.toList())。
uj5u.com熱心網友回復:
這將使用Guava和它的TreeMultimap
注意如果你不能使用Treemultimap,你可以用一個經典的Map<Long, List<Long>> sortedMap = new TreeMap<>(Long::compare),用list.sort(Long::compareTo);對不同的串列進行排序。
uj5u.com熱心網友回復:
好吧,這是我想出的辦法。我分兩個階段完成了它。
首先創建一個
TreeMap<Long,List<Order>>,其中的串列是根據uniqueID以降序排序的。
使用一個記錄來簡化這個例子。
record Order(long getCustomerId, long getUniqueId) {
@Override {
public String toString() {
return String.format("Cid = %s, Uid = %s"/span>, 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)。
現在開始創建地圖
groupingBy的執行,每個串列將以遇到的順序建立,保留了排序的串列。 由于這是一個TreeMap,每個串列將以相對于鍵的正確順序排列。
Map<Long, List<Order>> map = orders.stream()
.sorted(Comparator.compararing(Order::getUniqueId)
.反轉())
.collect(Collectors.groupingBy(Order::getCustomerId,
TreeMap::new,
Collectors.toList())。
現在是最后的串列創建
keys不再重要,所以需要的只是值(即List<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)。
prints
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。
轉載請註明出處,本文鏈接:https://www.uj5u.com/yidong/310850.html
標籤:
