java TreeMap 數(shù)據(jù)丟失

在項(xiàng)目中需要對(duì)一個(gè)HashMap<String, User> userMap進(jìn)行排序,排序規(guī)則為:HashSet<String> keySet中存在的key優(yōu)先級(jí)高于其它的key,使用TreeMap進(jìn)行保存 测秸,
JDK8 + Lombok1.18

一羡榴、代碼

public class MapTest {

    @Test
    public void testTreeMap() {
        // 里面保存的數(shù)據(jù)優(yōu)先級(jí)要高些
        Set<String> keySet = new HashSet<>();
        keySet.add("D") ;
        keySet.add("B") ;

        // 要排序的數(shù)據(jù)
        Map<String, User> userMap = new HashMap<>();
        userMap.put("A",new User("A",18));
        userMap.put("B",new User("B",15));
        userMap.put("C",new User("C",18));
        userMap.put("D",new User("D",12));

        // 排序后的map
        Map<String,User> sortMap = new TreeMap<>((k1, k2) -> {
            boolean exist1 = keySet.contains(k1);
            boolean exist2 = keySet.contains(k2);

           if (!exist1 && exist2) {
                return -1;
            } else if (exist1 && !exist2) {
                return -1;
            }
            return 0;
        });

        sortMap.putAll(userMap);

        System.out.println("");
    }

    @Setter
    @Getter
    @NoArgsConstructor
    @AllArgsConstructor
    public static class User{
        private String name ;
        private Integer age ;
    }
}
以上排序后的結(jié)果可能是什么鳞疲?期望結(jié)果是B和D排在前面惧磺,其它的排后面

二青责、非預(yù)期輸出:

結(jié)果如下:


image.png
  • 數(shù)據(jù)丟失了兩個(gè)均抽。并且數(shù)據(jù)不對(duì)


    image.png

不得不吐槽簡(jiǎn)書這個(gè)代碼塊的配色嫁赏,黑挫挫的,一點(diǎn)都不利于閱讀油挥,注釋什么的看都看不清A视!I盍取H疗埂!

三惋鹅、通過(guò)源碼查找原因

  • 直接打開TreeMap源碼部分, 看 put()方法就可以了
    public V put(K key, V value) {
        Entry<K,V> t = root;
        if (t == null) {
            compare(key, key); // type (and possibly null) check

            root = new Entry<>(key, value, null);
            size = 1;
            modCount++;
            return null;
        }
        int cmp;
        Entry<K,V> parent;
        // split comparator and comparable paths
        Comparator<? super K> cpr = comparator;
        if (cpr != null) {
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        else {
            if (key == null)
                throw new NullPointerException();
            @SuppressWarnings("unchecked")
                Comparable<? super K> k = (Comparable<? super K>) key;
            do {
                parent = t;
                cmp = k.compareTo(t.key);
                if (cmp < 0)
                    t = t.left;
                else if (cmp > 0)
                    t = t.right;
                else
                    return t.setValue(value);
            } while (t != null);
        }
        Entry<K,V> e = new Entry<>(key, value, parent);
        if (cmp < 0) 
            parent.left = e;
        else
            parent.right = e;
        fixAfterInsertion(e);
        size++;
        modCount++;
        return null;
    }
  • 因?yàn)槭亲远x了comparator , 所以只看下面部分代碼片段
        int cmp;
        Entry<K,V> parent;
        // split comparator and comparable paths
        Comparator<? super K> cpr = comparator;
        if (cpr != null) {
            do {
                parent = t;
                cmp = cpr.compare(key, t.key);
                if (cmp < 0)
                    t = t.left;  // 標(biāo)記1
                else if (cmp > 0)
                    t = t.right;  // 標(biāo)記2
                else
                    return t.setValue(value); // 標(biāo)記3
            } while (t != null);
        }
  • TreeMap 使用了紅黑樹結(jié)構(gòu)保存數(shù)據(jù)则酝。喜歡磕一下紅黑樹細(xì)節(jié)的先看這個(gè) 紅黑樹實(shí)現(xiàn)分析
    標(biāo)記1comparator.compare(k1, k2) 返回值小于0 闰集,則把值放在左子樹沽讹,相當(dāng)于排在前面.
    標(biāo)記2comparator.compare(k1, k2) 返回值大于0 般卑,則把值放在右子樹,相當(dāng)于排在后面.
    標(biāo)記3comparator.compare(k1, k2) 返回值等于0 爽雄,把k1k2當(dāng)成相同key蝠检,并替換值。這就造成了上圖中key和具體數(shù)據(jù)不匹配的情況 挚瘟。

四叹谁、解決方法

  • 修復(fù)BUG : TreeMap 的比較器不返回0
       Map<String,User> sortMap = new TreeMap<>((k1, k2) -> {
            boolean exist1 = keySet.contains(k1);
            boolean exist2 = keySet.contains(k2);

            if (exist1 && !exist2) {
                return -1;
            }
            return 1;
        });
  • 修改后的結(jié)果如下:


    image.png

    image.png

五、擴(kuò)展compareTo

public class MapTest {

    @Test
    public void testTreeMap() {
        Map<User, User> userMap = new HashMap<>();
        User a = new User("A", 18);
        User b = new User("B", 15);
        User c = new User("C", 18);
        User d = new User("D", 12);
        userMap.put(a, a);
        userMap.put(b, b);
        userMap.put(c, c);
        userMap.put(d, d);

        Map<User,User> sortMap = new TreeMap<>();

        sortMap.putAll(userMap);

        System.out.println("");
    }

    @Setter
    @Getter
    @NoArgsConstructor
    @AllArgsConstructor
    private static class User implements Comparable<User>{
        private String name ;
        private Integer age ;

        @Override
        public int compareTo(User u) {
            return this.getAge() - u.age ;
        }
    }
}
  • 上面這個(gè)例子中TreeMapkey使用的是User對(duì)象并實(shí)現(xiàn)了Comparable接口乘盖,通過(guò)age的排序焰檩。實(shí)際排序結(jié)果還是會(huì)丟數(shù)據(jù)、數(shù)據(jù)錯(cuò)亂订框,解決方案就是compareTo中不要返回0 锅尘,前提條件是user.compareTo()只用于TreeMap排序

例子中只是為了說(shuō)明compareTo用于TreeMap排序時(shí)也不能返回0布蔗,所以就不管Map<User, User>結(jié)構(gòu)是否合理、hashCodeequals是否規(guī)范


總結(jié):TreeMap 自定義比較器浪腐,Comparator不能返回0 W葑帷!议街!compareTo()也不能返回0 T蠼鳌!特漩!僅限于要比較的對(duì)象只用于TreeMap才要注意

?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末吧雹,一起剝皮案震驚了整個(gè)濱河市,隨后出現(xiàn)的幾起案子涂身,更是在濱河造成了極大的恐慌雄卷,老刑警劉巖,帶你破解...
    沈念sama閱讀 216,651評(píng)論 6 501
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件蛤售,死亡現(xiàn)場(chǎng)離奇詭異丁鹉,居然都是意外死亡,警方通過(guò)查閱死者的電腦和手機(jī)悴能,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 92,468評(píng)論 3 392
  • 文/潘曉璐 我一進(jìn)店門揣钦,熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái),“玉大人漠酿,你說(shuō)我怎么就攤上這事冯凹。” “怎么了炒嘲?”我有些...
    開封第一講書人閱讀 162,931評(píng)論 0 353
  • 文/不壞的土叔 我叫張陵宇姚,是天一觀的道長(zhǎng)匈庭。 經(jīng)常有香客問(wèn)我,道長(zhǎng)空凸,這世上最難降的妖魔是什么嚎花? 我笑而不...
    開封第一講書人閱讀 58,218評(píng)論 1 292
  • 正文 為了忘掉前任,我火速辦了婚禮呀洲,結(jié)果婚禮上紊选,老公的妹妹穿的比我還像新娘。我一直安慰自己道逗,他們只是感情好兵罢,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,234評(píng)論 6 388
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著滓窍,像睡著了一般卖词。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發(fā)上吏夯,一...
    開封第一講書人閱讀 51,198評(píng)論 1 299
  • 那天此蜈,我揣著相機(jī)與錄音,去河邊找鬼噪生。 笑死裆赵,一個(gè)胖子當(dāng)著我的面吹牛,可吹牛的內(nèi)容都是我干的跺嗽。 我是一名探鬼主播战授,決...
    沈念sama閱讀 40,084評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開眼,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼桨嫁!你這毒婦竟也來(lái)了植兰?” 一聲冷哼從身側(cè)響起,我...
    開封第一講書人閱讀 38,926評(píng)論 0 274
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤璃吧,失蹤者是張志新(化名)和其女友劉穎楣导,沒想到半個(gè)月后,有當(dāng)?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體肚逸,經(jīng)...
    沈念sama閱讀 45,341評(píng)論 1 311
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡爷辙,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,563評(píng)論 2 333
  • 正文 我和宋清朗相戀三年,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了朦促。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片膝晾。...
    茶點(diǎn)故事閱讀 39,731評(píng)論 1 348
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡,死狀恐怖务冕,靈堂內(nèi)的尸體忽然破棺而出血当,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 35,430評(píng)論 5 343
  • 正文 年R本政府宣布臊旭,位于F島的核電站落恼,受9級(jí)特大地震影響,放射性物質(zhì)發(fā)生泄漏离熏。R本人自食惡果不足惜佳谦,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,036評(píng)論 3 326
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望滋戳。 院中可真熱鬧钻蔑,春花似錦、人聲如沸奸鸯。這莊子的主人今日做“春日...
    開封第一講書人閱讀 31,676評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)娄涩。三九已至窗怒,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間蓄拣,已是汗流浹背扬虚。 一陣腳步聲響...
    開封第一講書人閱讀 32,829評(píng)論 1 269
  • 我被黑心中介騙來(lái)泰國(guó)打工, 沒想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留球恤,地道東北人孔轴。 一個(gè)月前我還...
    沈念sama閱讀 47,743評(píng)論 2 368
  • 正文 我出身青樓,卻偏偏與公主長(zhǎng)得像碎捺,于是被迫代替她去往敵國(guó)和親。 傳聞我的和親對(duì)象是個(gè)殘疾皇子贷洲,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,629評(píng)論 2 354

推薦閱讀更多精彩內(nèi)容

  • 前言 TreeMap TreeMap類繼承圖 TreeMap的域 TreeMap的構(gòu)造函數(shù) TreeMap常見Ap...
    HikariCP閱讀 696評(píng)論 0 1
  • 一优构、基本數(shù)據(jù)類型 注釋 單行注釋:// 區(qū)域注釋:/* */ 文檔注釋:/** */ 數(shù)值 對(duì)于byte類型而言...
    龍貓小爺閱讀 4,259評(píng)論 0 16
  • TreeMap 平衡二叉樹 平衡二叉樹(Self-balancing binary search tree)又被稱...
    史路比閱讀 312評(píng)論 0 1
  • Java集合類可用于存儲(chǔ)數(shù)量不等的對(duì)象,并可以實(shí)現(xiàn)常用的數(shù)據(jù)結(jié)構(gòu)如棧,隊(duì)列等,Java集合還可以用于保存具有映射關(guān)...
    小徐andorid閱讀 1,939評(píng)論 0 13
  • 話說(shuō)二十一世紀(jì)诵叁,公元二零一七年。 在北方一古老又新興之都市——密城钦椭。 隨著時(shí)代發(fā)展拧额,農(nóng)村人口大量向城市轉(zhuǎn)移,在促進(jìn)...
    風(fēng)雨菩提閱讀 305評(píng)論 0 0