爲家庭地位而戰的:怎樣合併兩個map

背景
  • 在最近做課程需求中,有一個小需求的實現,需要從第三方直播平臺拉取數據,對某一個學生的數據進行綜合統計。 總之,在這裏邊就涉及到一個小算法:合併兩個Map。 其Map的結構是Map<long,Integer>,之所以需要合併,是因爲一個用戶的綜合統計值,存在於多批數據中。也因此,這個騷操作的具體要求就是:合併兩個Map,取key的並集最大值,如果有重複的key值,其value的處理方案爲:MapA.value+MapB.value
  • say say 爲啥又灌了這篇水文吧。話說,別人家的家庭地位可能是以經濟成分來算,或者是別的,反正我是不知道。但我的家庭地位,總是莫名其妙的來源於 技術 咖位。當然啦,我是從來沒勝利過,除了這一次的map合併。PS: 不排除這兩天李煩煩生病了, 腦袋不清醒。 所以,這次的勝利,真是天時+地利+人和,哈哈哈哈,我就建立我的快樂到別人的痛苦之上,真的,就這一次。 神會原諒我的,阿門,遂作文紀念。
實現經過
  • 第一種方案
  1. 思路: 遇到這個需求時,我的第一反應是把兩個Map的key值收集起來,然後放入一個Set裏獲取key的最大並集,然後遍歷Set,組合兩個map的數據,存入最終的返回map中
  2. 思考: 猛然間,想了想時間複雜度和空間複雜度的問題。(主要是老被對象吐槽代碼寫的不好,就特別想優化自己的每一行代碼) 如果是以這種方式實現,那我,要初始化一個Set,還要初始化一個返回Map,而如果這個map不設置初始大小,勢必會面臨着擴容。 所以,以兩個原Map的大小,並結合擴容因子設置,初始化這個Map的大小。 這樣的空間複雜度就是O(n),時間複雜度也是O(n)
  3. 強行加戲: 有沒有其他方案,可以從時間複雜度或者空間複雜度上來說,更爲優良的方案呢。 如果我不新增臨時變量,是不是也可以實現。因此,我想到了循環。
  • 第二種方案
  1. 思路: 循環一個mapA,從另一個mapB中查看是否包含當前key值,如果不包括,則把當前的key,添加到mapB中;如果包括,則按照規則覆蓋mapB中key的value值
  2. 思考: 如果是利用這樣的循環,那麼,我就可以不新增臨時變量了。而且,相對於第一種,儘管時間複雜度均爲O(n),但就其數量來講-循環次數,這種循環的次數少於第一種遍歷set的次數。問題是:當一個map中不存在key值,而往其中添加時,往往會引發擴容機制,怎麼樣,能夠減少擴容的次數呢?
  3. 解決方案: 擴容是當容量達到75%時,會增加一倍的容量。如果我循環的mapA是長度較短的一個,那麼當mapB因爲新增key值發生擴容,最多也只會有一次。
  • 綜上: 是我想太多,我也這樣說。可我忍不住,就愛想太多。 我把我這一堆猛如虎的騷操作,第一時間跟我對象-李煩煩分享了。本想獲得一頓誇,誰知道:理解沒啥大問題,但不用想這麼複雜。 我靠靠,當時的心情,難以言表。這是我想複雜了嗎?這是我想複雜了嗎?這是我想複雜了嗎?讓我搞大技術,大架構是沒啥大指望的,我就想着能夠在細節上,偶爾能勝我對象一點。唉,我怎麼這麼難呢。 。。。。。。。以爲我就這樣敗了麼,事實並沒有。。。。。。。。接着記錄我這次大捷。。。。。。。
代碼

這是我的代碼,因爲業務關係,在使用中我會根據數據情況,選取最小長度的map作爲mapA:

mapA.forEach((k, v) -> {
             if (mapB.containsKey(k)) {
                 Integer bValue = mapB.get(k);
                 mapB.put(k, bValue + v);
             } else {
                 mapB.put(k, v);
             }
         });

後來,李煩煩看了我給他發的這段代碼,給我整了以下的代碼:

        return Stream.of(mapA.entrySet(), mapB.entrySet())
                .flatMap(Collection::stream)
                .collect(Collectors.toMap(
                        Map.Entry::getKey,
                        Map.Entry::getValue,
                        (m1,m2) -> m1 + m2));

好吧,其實我也不知道這個裏邊具體怎麼實現的,也沒有看出來這段代碼跟我寫的瞎循環相比有什麼優良性。怪我水平low,不過出於李煩煩的崇拜,我最終提交的代碼,就是如上的。

敗局轉勝

爲了方法的健壯性和複用性(我自己的業務場景,不存在參數爲空的情況),我需要對參數map進行非空校驗。哈哈哈哈,正所謂:智者千慮,必有一失;愚者千慮,必有一得。我寫的非空校驗是這樣的:

  if(CollectionUtils.isEmpty(mapA)){
            return mapB;
        }
        if(CollectionUtils.isEmpty(mapB)){
            return mapA;
        }

李煩煩說我應該將這兩個非空校驗寫在一起,因爲它們的語義相同,就應該寫成:

if(CollectionUtils.isEmpty(mapA) || CollectionUtils.isEmpty(mapB)){
            return null;
        }

哈哈哈,這麼大個bug被抓住,不大肆嘲諷簡直對不住我多次慘敗的心。太開心了。。。。。。。這樣寫在一起,當其中一個爲空map的時候,全部返回了null。 然而我的目的是取兩個map合併的最大並集,其中一個爲null爲空,不代表另一個也爲空爲null。 只有兩個參數都爲空時,才能返回一個空map。 所以綜合考慮, 就應該分別校驗更合理,嘿嘿嘿嘿嘿嘿。

獲勝感言
  • 根據我屢敗屢戰的經驗,從戰略上勝出李煩煩的可能不大,但從細節上略勝一籌還是可期可盼的。要對未來的家庭地位上升有信心,道路雖曲折,但局勢肯定是明朗的,哼哼哈嘿,叫我大王。
  • 另外,對於代碼而言。我覺得技術本身沒有價值。也因此代碼的第一要義,應該是實現業務需求,運行準確、穩定。其次是,性能優良;再者是代碼優雅。 後兩者,往往相輔相成。覺得自己又進步了一點點,會考慮每行代碼怎麼寫運行效率更高。也許是由於知識儲備,即使我想了也沒對,但有了想法和實踐,就離正確的不遠了。加油。。。。。PS:李煩煩說其實合併這個還有很多個方法,讓我寫總結的時候想想。。。。。還有啥?
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章