前段開發項目試就發現,一部分的代碼實現存在着一些性能上的隱患。但當時忙於趕進度和因爲卡發中的不穩定因素,想了許多解決方案也沒有機會實施。最近,正好趁個機會進行一系列的改進。java
我在團隊開發中負責開發服務器端。因此在編寫業務邏輯層時,經常遇到如下這樣的業務邏輯:
1. 判斷一個用戶是否爲在本身的好友列表中
2. 判斷一條動態是否已被用戶翻閱
3. 判斷兩個用戶的標籤的匹配度
4. .....等等
這些狀況,我以前的方案是採用數據庫來解決,爲每條記錄添加標記,須要查詢時則遍歷返回相應的集合。數據庫
可是隨着用戶量的不斷增多、各個用戶之間的關係不斷地增長、以及用戶使用軟件的一系列行爲中這些狀況是很是頻繁的,這樣頻繁遍歷大量的記錄的讀操做會給數據庫帶來難以承受的壓力。
數組
那麼如何需找一種更好的解決方案?
既能減小數據庫須要遍歷的記錄數量且快速索引,又能用少許的內存表示大量的數據。
其實若是咱們對這一類型的業務邏輯進行抽象,能夠獲得:本質上就是判斷一個元素是否存在於集合中
因此咱們能夠採用位數組,經過數組的下標能快速地定位某個元素,用bit表示相應的內容可以節省大量的空間。
服務器
可是這樣結構依舊不夠完美,若是數據量相對較少,數組中會存在大量的無用數據, 如長度爲1024的byte數組中的只有少許位被表示爲1,大量位依然是0。
此時咱們能夠採用遊程編碼壓縮byte數組。如上圖的遊程編碼後的結果能夠表示爲[3, 0, 2, 0, 2, 0, 1, 0 ]數據結構
Bitmap:被設計爲一種用bit數組來儲存表示2種狀態的緊湊、快速索引的數據結構(固然Java的util包中也實現這類型的數據結構—BitSet(不過並非Set))app
其實說開來,Bitmap就是一個位數組而已,有着快速訪問優點(下標訪問),以及極小佔用(用1bit來表示)post
有點美中不足的是,Java中並無提供bit這樣的數據類型,即使是最小的數據類型byte也要佔用8bit。這樣就須要進行一些位運算來完成相應的操做,使得代碼變得稍微複雜。
1. BitMap的內部經過byte數組實現
2. BitMap的基本操做:增刪改查性能
void Set(int position); /* 將某位置"1" */
boolean Get(int position); /* 判斷某位的值 */
void Clear(int postion); /* 將某位置"0" */
廢話很少說,直接看圖
主要分爲兩個步驟:
1. 先將一個byte類型的」1」左移4位,獲得結果
2. 再進行簡單的或運算,獲得結果並覆蓋原來的值ui
理解了上面的例子,相信這個應該就很簡單了
一樣是兩步:
1. 先將一個byte類型的」1」左移3位,獲得結果
2. 再進行與操做,獲得結果並覆蓋原來的值this
或許這裏會有些疑問,爲何不考慮用boolean?
首先,Java規範中沒有強制規定boolean所佔內存的大小。並且大部分計算機容許分配的最小內存單元爲8bit
大多能夠運用的場景主要是兩個方面:
這裏以標籤匹配爲例子,開發中一個用戶與各個用戶之間的標籤匹配度是使人頭疼的問題,經過匹配標籤字符串或者標籤ID,這樣的效果都不能太讓人滿意,在數據庫中的保存也頗爲麻煩。
假如,每一個用戶都有一個這樣小小的長度爲40的byte數組,那麼用戶就能夠用它來表示320種標籤。並且可以快速的查詢,經過bitarray[tag_id]這樣的訪問方式能夠極快查到,用戶是否選取了這個標籤,可以快速地計算與各個用戶之間的標籤匹配度
那麼像第一點說的那樣,長度爲40的byte數據即可以保存320種標籤信息,但它內存大小隻有40B。並且這仍是沒有進行遊程編碼壓縮以前的大小
/** * Created by auhnayuil on 17-6-7. */ public class BitMap { public static final int DEFAULT_SIZE = 1024; public static final boolean EXIST = true; public static final boolean NULL = false; public static final short bits = 8; private byte[] bitArray; private int size; public BitMap(){ this(DEFAULT_SIZE); } public BitMap(byte[] bitArray){ this.size = bitArray.length * bits; this.bitArray = bitArray; } public BitMap(int defaultSize) { this.size = defaultSize * bits; this.bitArray = new byte[defaultSize]; } public BitMap(int size, boolean elem){ this(size); if(EXIST == elem) { for (int i = 0; i < bitArray.length; i++) bitArray[i] = (byte) ~bitArray[i]; } } public int size(){ return size; } public int index(int position){ int idx = (position + bits - 1) / bits; return idx - 1; } public int offset(int position){ int ofs = position % 8; return (ofs == 0 ? ofs : 8 - ofs); } public void setBit(int position){ if(position > size) return ; int idx = index(position); int ofs = offset(position); bitArray[idx] |= (byte)(1 << ofs); } public boolean getBit(int position){ if(position > size) return false; int idx = index(position); int ofs = offset(position); byte tmp = (byte)(bitArray[idx] & (1 << ofs)); return tmp != 0; } public void setBitArray(byte[] bitArray){ this.bitArray = bitArray; } public byte[] getBitArray(){ return bitArray; } public String byteToStr(int position){ byte b = bitArray[index(position)]; StringBuffer sb = new StringBuffer(""); for(int i=bits-1; i>-1; i--) sb.append((byte)((b >> i) & 0x1)); return sb.toString(); } }