摘要:寫完上一篇中實(shí)現(xiàn)集合的后,自己又進(jìn)行了一番探索,結(jié)合在公司項(xiàng)目的實(shí)際測試后,總結(jié)了一個更加有效地基于紅黑樹的結(jié)構(gòu)來實(shí)現(xiàn)集合的,由于使用二叉樹來保存有序集合,因此對集合的增加刪除查找的時間復(fù)雜度均為。
寫完上一篇「Java 中實(shí)現(xiàn)集合的 keep in order」后,自己又進(jìn)行了一番探索,結(jié)合在公司項(xiàng)目的實(shí)際測試后,總結(jié)了一個更加有效地、基于 TreeSet(紅黑樹)的結(jié)構(gòu)來實(shí)現(xiàn)集合的 keep in order,由于使用二叉樹來保存有序集合,因此對集合的增加、刪除、查找的時間復(fù)雜度均為 log(n)。
集合(Set)的約定Java 中對集合(Set)一般做法是:為了代碼的健壯性,盡量在 Set 中保存不可變的對象。因?yàn)椴还苁?HashSet 還是 TreeSet,他們都依賴元素自身的特性來保證集合的不重復(fù)性。如 HashSet 依賴元素的 equals 和 hashCode 方法,TreeSet 依賴元素的 compareTo 方法或自定義的 Comparator。
元素改變情況下保持集合有序首先,公司項(xiàng)目里,服務(wù)器維護(hù)了一個玩家的集合:
class PlayerManager { // 玩家的 Map,<玩家 id -> 玩家實(shí)體> Mapmap = new ConcurrentHashMap<>(); } // 玩家實(shí)體 interface Player { int getId(); int getMoney(); void setMoney(int money); }
這個 map 將成為數(shù)據(jù)源,它不一定是有序?,F(xiàn)在增加一個成員:
// 可重排序的集合,保存不可變對象:玩家 id SetreorderableSet;
這個集合記錄這所有玩家的 id 值,并以某種順序進(jìn)行排序,排序的規(guī)則視需求而定,假如要以玩家擁有的金錢數(shù)來做財(cái)富排行榜。
由于玩家的財(cái)富值始終在發(fā)生變化,因此我定義一個接口來感知這種變化:
interface Reorderable{ // 這個方法用于重新計(jì)算元素 E 在集合中的索引 void recomputeOrder(E element, ElementChangedCallback callback); // 這是一個回調(diào),處理元素值改變的代碼 interface ElementChangedCallback { void onElementChanged(E element); } }
然后用 TreeSet 的子類實(shí)現(xiàn)上面的 Reorderable 接口來構(gòu)造一個可重排序的集合:
class ReorderableSetextends TreeSet implements Reorderable { // 使用自定義的比較器 public ReorderableSet(Comparator super E> comparator) { super(comparator); } @Override public void recomputeOrder(E element, ElementChangedCallback callback) { if (contains(element)) { // 先將元素從集合中移除,時間復(fù)雜度 log(n) remove(element); // 再使用回調(diào)去改變元素的值 callback.onElementChanged(element); // 在將元素添加到集合里,時間復(fù)雜度 log(n) add(element); } } }
因?yàn)槭菍?shí)現(xiàn)一個財(cái)富排行榜,所以定義排序規(guī)則為簡單的比較一下金錢數(shù)目即可:
// 這里的 Integer 代表玩家 id class MoneyComparator implements Comparator{ @Override public int compare(Integer A, Integer B) { // 從服務(wù)器維護(hù)的玩家集合中獲取玩家的引用 Player playerA = map.get(A); Player playerB = map.get(B); // 降序排列 return playerB.getMoney() - playerA.getMoney(); } }
這時候可以構(gòu)造之前定義的 Set
SetreorderableSet = new ReorderableSet<>(new MoneyComparator());
要響應(yīng)玩家金錢的變化,則構(gòu)造一個實(shí)現(xiàn) ElementChangedCallback 接口的類,并把它放在任何玩家金錢改變的地方:
class UpdateMoney implements Reorderable.ElementChangedCallback{ int money; UpdateMoney(int money) { this.money= money; } @Override public void onElementChanged(Integer playerId) { // 玩家金錢改變 Player player = map.get(playerId); player.setMoney(money); } }
玩家金錢改變的時候調(diào)用一下代碼,比如買東西的時候
void buyGood(Player player, int cost) { ReorderablereorderableSet = ......; // 獲得引用 int moneyRemain = player.getMoney() - cost; // 構(gòu)造 UpdateMoney 回調(diào) reorderableSet .recomputeOrder(player.getId(), new UpdateMoney(moneyRemain); }
因此,就可以實(shí)現(xiàn)集合(Set)在元素值改變時保持有序了,由于 TreeSet 基于紅黑樹實(shí)現(xiàn),對它的查找添加刪除均很快。
總結(jié)通用的框架就像下面這樣:
class ReorderableSetextends TreeSet implements Reorderable { public ReorderableSet() { } public ReorderableSet(Comparator super E> comparator) { super(comparator); } public ReorderableSet(Collection extends E> c) { super(c); } public ReorderableSet(SortedSet s) { super(s); } @Override public void recomputeOrder(E element, ElementChangedCallback callback) { if (contains(element)) { // 先將元素從集合中移除,時間復(fù)雜度 log(n) remove(element); // 再使用回調(diào)去改變元素的值 callback.onElementChanged(element); // 在將元素添加到集合里,時間復(fù)雜度 log(n) add(element); } } } interface Reorderable { void recomputeOrder(E element, ElementChangedCallback callback); interface ElementChangedCallback { void onElementChanged(E element); } }
考慮到線程安全,可以在 recomputeOrder 中進(jìn)行加鎖操作。
文章版權(quán)歸作者所有,未經(jīng)允許請勿轉(zhuǎn)載,若此文章存在違規(guī)行為,您可以聯(lián)系管理員刪除。
轉(zhuǎn)載請注明本文地址:http://m.specialneedsforspecialkids.com/yun/65812.html
摘要:此項(xiàng)禁止的一個特殊情況是不允許某個包含其自身作為元素。即使的順序與不一致,其行為也是定義良好的它只是違背了接口的常規(guī)協(xié)定。 原問題 Java 中怎樣實(shí)現(xiàn)一種即使元素改變依然有序的集合? 問題由來 起因是在公司做游戲項(xiàng)目的時候遇到一個需求需要實(shí)現(xiàn): 服務(wù)器要維護(hù)一個幫派成員(Member)的集合,這個集合要按照在線狀態(tài)、成員等級和名稱依次有序排列。 由于每時每刻都有玩家在不斷上下線,成員...
摘要:一接口介紹中提供了一個接口。從單詞意思就知道接口的作用就是用來排序的。類實(shí)現(xiàn)了接口的一個比較器。三中使用接口在的例子在配置文件中添加,那么默認(rèn)會注入和這兩個類。 一、Ordered接口介紹Spring中提供了一個Ordered接口。從單詞意思就知道Ordered接口的作用就是用來排序的。Spring框架是一個大量使用策略設(shè)計(jì)模式的框架,這意味著有很多相同接口的實(shí)現(xiàn)類,那么必定會有優(yōu)先級...
摘要:關(guān)于的源碼分析,本文并不打算展開講了。大家可以參考我之前的一篇文章源碼詳細(xì)分析。在刪除節(jié)點(diǎn)時,父類的刪除邏輯并不會修復(fù)所維護(hù)的雙向鏈表,這不是它的職責(zé)。在節(jié)分析鏈表建立過程時,我故意忽略了部分源碼分析。 1. 概述 LinkedHashMap 繼承自 HashMap,在 HashMap 基礎(chǔ)上,通過維護(hù)一條雙向鏈表,解決了 HashMap 不能隨時保持遍歷順序和插入順序一致的問題。除此...
摘要:與基于數(shù)組的隊(duì)列相同,重載的構(gòu)造函數(shù)可以接受集合指定的初始值。這種隊(duì)列比基于數(shù)組阻塞隊(duì)列具有更高的吞吐量。創(chuàng)建個交易者實(shí)例,將自己出售的訂單放入隊(duì)列中,每個出售訂單都將會有隨機(jī)的交易量。要使用基于優(yōu)先級的隊(duì)列,需要提供適當(dāng)?shù)谋容^器。 阻塞隊(duì)列 在阻塞隊(duì)列的幫助下,許多同步問題都可以被公式化。阻塞隊(duì)列是隊(duì)列,當(dāng)線程試圖對空隊(duì)列進(jìn)行出列操作,或試圖向滿的隊(duì)列中插入條目時,隊(duì)列就會阻塞。直到...
高級并發(fā)對象 到目前為止,本課程重點(diǎn)關(guān)注從一開始就是Java平臺一部分的低級別API,這些API適用于非常基礎(chǔ)的任務(wù),但更高級的任務(wù)需要更高級別的構(gòu)建塊,對于充分利用當(dāng)今多處理器和多核系統(tǒng)的大規(guī)模并發(fā)應(yīng)用程序尤其如此。 在本節(jié)中,我們將介紹Java平臺5.0版中引入的一些高級并發(fā)功能,大多數(shù)這些功能都在新的java.util.concurrent包中實(shí)現(xiàn),Java集合框架中還有新的并發(fā)數(shù)據(jù)結(jié)構(gòu)。 ...
閱讀 4012·2021-09-24 10:24
閱讀 1402·2021-09-22 16:01
閱讀 2724·2021-09-06 15:02
閱讀 1022·2019-08-30 13:01
閱讀 1012·2019-08-30 10:52
閱讀 639·2019-08-29 16:36
閱讀 2240·2019-08-29 12:51
閱讀 2340·2019-08-28 18:29