PriorityQueue的實際應用場景

PriorityQueue 名叫優先級隊列,底層由堆結構實現,默認是小根堆。通過 offer 方法添加進去的元素會進行堆排序,最小的元素放在堆頂。通過 peek 方法可以獲得堆頂(最小)元素。通過 poll 方法可以刪除堆頂元素同時獲得堆頂元素,刪除之後剩下的元素中最小的元素仍處於堆頂。

一、應用場景

某電商平臺入駐了大量的商家,商家可以在平臺銷售商品,用戶可以在平臺的商家那裏購買商品。用戶付款後如果對購買到的商品不滿意,可以向平臺發起投訴。用戶對某商家的某件商品的投訴記錄會存儲在一張表中,表結構如下:

列名 類型 備註
store_id int 商家id
product_id int 商品id
remark string 投訴記錄

現在的需求是:找到投訴記錄最多的前 3 個商家,目的是在搜索時對其店鋪進行降權處理。

二、思路分析

  1. 創建一個小根堆(PriorityQueue 默認就是小根堆)
  2. 小根堆中元素的數量小於 3 的時候就直接向集合中添加元素
  3. 當堆中的元素個數等於 3 的時候,通過 peek 方法取出堆頂元素(最小的那個)與當前遍歷到的元素比較
  4. 如果當前遍歷到的元素大於堆頂元素,就把原堆頂元素移除,把當前元素加入堆中
  5. 這樣使得移除的元素都小於堆中的元素
  6. 所以最後堆中保留下來的就是最大的N個元素

三、代碼實現

import java.util.*;

// 投訴日誌實體類
class ComplainLog {
    // 商家id
    public Integer storeId;
    // 商品id
    public Integer productId;
    // 投訴記錄
    public String remark;
    ComplainLog(Integer storeId, Integer productId, String remark) {
        this.storeId = storeId;
        this.productId = productId;
        this.remark = remark;
    }
}

// 商家被投訴次數實體類
class ComplainCount {
    // 商家id
    public Integer storeId;
    // 投訴次數
    public Integer complainCount;
    ComplainCount(Integer storeId, Integer complainCount) {
        this.storeId = storeId;
        this.complainCount = complainCount;
    }
}

public class PriorityQueueTest {
    public static void main(String[] args) {
        // 模擬10條投訴記錄
        ComplainLog log1 = new ComplainLog(101, 8650, "質量不行");
        ComplainLog log2 = new ComplainLog(101, 8651, "書皮爛了,垃圾物流");
        ComplainLog log3 = new ComplainLog(101, 7921, "牛肉不新鮮,懷疑是假貨");
        ComplainLog log4 = new ComplainLog(101, 7963, "賣假貨,封了他家店!!!");
        ComplainLog log5 = new ComplainLog(102, 6217, "店家態度不好,給他降權");
        ComplainLog log6 = new ComplainLog(102, 6245, "衣服撕了。。。");
        ComplainLog log7 = new ComplainLog(102, 5214, "就是想投訴");
        ComplainLog log8 = new ComplainLog(103, 5215, "。。。");
        ComplainLog log9 = new ComplainLog(104, 4632, "2333333");
        ComplainLog log10 = new ComplainLog(104, 4632, "有人嗎");
        ComplainLog log11 = new ComplainLog(104, 4632, "有人嗎,aaaaaa");

        List<ComplainLog> complainLogList = new ArrayList<ComplainLog>();
        complainLogList.add(log1);
        complainLogList.add(log2);
        complainLogList.add(log3);
        complainLogList.add(log4);
        complainLogList.add(log5);
        complainLogList.add(log6);
        complainLogList.add(log7);
        complainLogList.add(log8);
        complainLogList.add(log9);
        complainLogList.add(log10);
        complainLogList.add(log11);

        // 統計出每家店鋪的投訴次數
        HashMap<Integer, Integer> complainCountMap = new HashMap<Integer, Integer>();
        for (ComplainLog complainLog : complainLogList) {
            if (complainCountMap.get(complainLog.storeId) == null) {
                complainCountMap.put(complainLog.storeId, 1);
            } else {
                complainCountMap.put(complainLog.storeId, complainCountMap.get(complainLog.storeId) + 1);
            }
        }
        List<ComplainCount> complainCountList = new ArrayList<ComplainCount>();
        for (Map.Entry<Integer, Integer> entry : complainCountMap.entrySet()) {
            ComplainCount complainCount = new ComplainCount(entry.getKey(), entry.getValue());
            complainCountList.add(complainCount);
        }

        // 通過PriorityQueue找出投訴記錄最多的前3個商家
        PriorityQueue<ComplainCount> queue = new PriorityQueue<ComplainCount>(new Comparator<ComplainCount>() {
            @Override
            public int compare(ComplainCount o1, ComplainCount o2) {
                return o1.complainCount.compareTo(o2.complainCount);
            }
        });

        // 創建一個小根堆(PriorityQueue默認就是小根堆)
        // 小根堆中元素的數量小於3的時候就直接向集合中添加元素
        // 當堆中的元素個數等於3的時候,通過peek方法取出堆頂元素(最小的那個)與當前遍歷到的元素比較
        // 如果當前遍歷到的元素大於堆頂元素,就把原堆頂元素移除,把當前元素加入堆中
        // 移除的元素都小於堆中的元素
        // 所以最後堆中保留下來的就是最大的N個元素
        int topN = 3;
        for (ComplainCount complainCount : complainCountList) {
            if (queue.size() < topN) {
                queue.offer(complainCount);
            } else {
                if (queue.peek().complainCount < complainCount.complainCount) {
                    queue.poll();
                    queue.offer(complainCount);
                }
            }
        }

        for (ComplainCount complainCount : queue) {
            System.out.println(complainCount.storeId + ":" + complainCount.complainCount);
        }

    }
}

四、運行結果

在這裏插入圖片描述
可以看出最終的結果是 101,102,104 這三家店的投訴次數最多,符合預期。

五、總結

PriorityQueue 主要在大數據量求 TopN 這種場景下使用的,少量數據直接排個序就 ok。

關注我的微信公衆號(曲健磊的個人隨筆),獲取更多精彩內容:
在這裏插入圖片描述

發佈了242 篇原創文章 · 獲贊 268 · 訪問量 58萬+
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章