【月光寶盒get√】用時間置換空間,聊聊稀疏數組的那些事兒


背景

數據結構是指帶有結構特性的數據元素的集合。在數據結構中,數據之間通過一定的組織結構關聯在一起,便於計算機存儲和使用。從大類划分,數據結構可以分為線性結構和非線性結構,適用於不同的應用場景。

  • 線性結構:

  線性結構作為最常用的數據結構,它的特點是單個數據之間存在一對一的線性關系。包含兩種不同的存儲結構:順序存儲結構和鏈式存儲結構。順序存儲的線性表稱為順序表,順序表中的存儲元素是連續的。

(線性結構)

  鏈式存儲的線性表被叫做鏈表,鏈表中的存儲元素不一定是連續的,元素節點中存放數據元素以及相鄰元素的地址信息

  線性結構常見的有:數組、隊列、鏈表和棧。

  • 非線性結構:

除了線性結構,其他的數據結構均為非線性結構,特點是單個數據之間存在多個對應關系,常見的有:二維數組,多維數組,廣義表,樹結構,圖結構

(常見的非線性結構)

稀疏數組(Sparse Array)

在各種各樣的數據結構中,最基礎、最常用的是數組。數組可以非常直觀的表示數據在一維或多維空間中的關系,與現實中的情形更接近,所以被大多數程序員當做"首選"的數據結構,然而,在部分應用場景中使用數組存儲數據時會出現各種各樣的情況,這是就需要在數組的基礎上,對數據結構進行優化,衍生出稀疏數組等新的數據結構。

以五子棋局為例,我們應該如何存儲棋盤上的落子情況呢?

(使用二位數組存儲五子棋盤)

如果使用一個二維數組對棋盤落子進行存儲,當我們拿到一個棋盤類數據內容時,大部分內容都是沒有意義的0,有意義數據並不相鄰,很多空間被浪費。對於五子棋來說,這個問題可能不是很明顯,但如果"棋盤"足夠大,被浪費的空間就會影響到軟件的功能實現,此時引入稀疏數組(SparseArray)就具有了重要的意義。

稀疏數組將數組中的內容進行壓縮,存儲在一個更為精練的二維數組中,稀疏數組的本質其實就是用時間置換空間。

具體的處理的方法是:

  1. 該數組之中一共有幾行幾列進行記錄
  2. 把相同元素內容忽略后,只記錄具有不同內容單元的位置

稀疏數組的實現

節約存儲空間顯然是稀疏數組的一個優勢,但是讀取性能是否可以會比二維數組差很多?

為了講清這個問題,我們可以先看一下Android中SparseArray的實現邏輯。SparseArray內部是通過兩個數組來進行數據存儲的。一個存儲key,另外一個存儲value。我們從源代碼中能夠看到key和value各自是用數組表示:

  private int[] mKeys;

   private Object[] mValues;

同時,SparseArray在存儲和讀取數據時候,使用的是二分查找法:

 public void put(int key, E value) {
        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
        ...
        }
 public E get(int key, E valueIfKeyNotFound) {
        int i = ContainerHelpers.binarySearch(mKeys, mSize, key);
        ...
        }

在put添加數據的時候,會使用二分查找法和之前的key比較當前我們添加的元素的key的大小,然后按照從小到大的順序排列好。所以,SparseArray存儲的元素都是按元素的key值從小到大排列好的。 而在獲取數據的時候,也是使用二分查找法判斷元素的位置,這樣可以使數據的獲取變得更加高效。所以,在key的數據量(可以理解為棋盤上去掉空白后的棋子數量)不大時,稀疏數組讀取性能是有保障的。

典型應用場景

做開發的都知道,想讓系統變快有個最簡單的辦法就是加內存。對於程序可以做大量的緩存來加速,即所謂"空間換時間"。但是在特定環境,程序可使用的內存是有限的。

在移動設備上,內存是個稀缺資源,例如iPhone 7的內存為2G,而最新款的iPhone 13也僅為4G。所以,稀疏數組這種"時間換空間"的技術最早被廣泛應用在移動開發領域。

除了移動端,另一個內存緊缺的運行環境是瀏覽器。雖然沒有明文規定,但在業界的共同認知里,瀏覽器會對單一線程進行內存限制,例如64位的chrome,每個tab頁的內存消耗不允許超過4G。這個限制,在單頁面應用還不成熟的十幾年前,不會成為問題。因為,那時大家所關注的,還是如何提升后端的處理性能,前端只是一種靜態的網頁表達方式。

隨着前端工程化的高速發展,各種前端工程腳手架日漸成熟,WebComponent標准被提上日程,企業開始由C/S向B/S應用轉型。這就要求前端開發者,需要面對單頁面處理復雜業務數據的挑戰。前端程序從最開始設計以及整個開發過程中都需要考慮內存的使用情況,盡可能的降低內存占用,防止網頁崩潰。以前端電子表格為例,我們通常需要為用戶提供上百萬個單元格(100列 x 1萬行),但其中有數據的單元格可能只有幾百個。為了減少數據模型占用的內存,我們最終的解決方式是將表格的數據存儲方式由常規數組改成稀疏數組,內存占用可以降低到幾十分之一,以確保瀏覽器內存不會被撐爆。

(稀疏矩陣存儲策略)

不只是“時間換空間”;

相較於傳統的鏈式存儲或是數組存儲,稀疏矩陣存儲構建了基於索引Key的數據字典。在松散布局的表格數據中,稀疏矩陣只會對非空數據進行存儲,而不需要對空數據開辟額外的內存空間。

使用這種特殊的存儲策略,除了可以降低內存占用,還使得數據片段化變得容易,可以隨時框取整個數據層中的一片數據,進行序列化或反序列化,而無需處理同一數據結構內的其他數據。

借用這樣的特性,我們可以隨時替換或恢復整個存儲結構中的任何一個級別的節點,以改變引用的方式高效解決了表格數據回滾和恢復,而這一點也是電子表格支持在線協同的技術基礎。

總結

本節為大家介紹了稀疏數組的基礎知識,技術實現和應用場景,以前端電子表格為例,展示了這個技術在節約內存空間,實現回滾恢復等領域的優勢。

在后續我們還會繼續為大家介紹更多嚴肅和有趣的內容~

覺得不錯點個贊再走吧~


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM