文章目錄
- List的特點介紹
- lpush,lpushx,rpush,rpushx命令
- lrange命令
- lpop和rpop
- lindex命令
- linsert命令
- llen命令
- lrem 命令
- ltrim命令
- lset命令
- 阻塞版本的命令
- blpop和brpop
- 命令小結
- list的內部編碼
- List的應用場景
List的特點介紹
列表相當于一個數組或者順序表,但是不是一個簡單的數組,而是接近雙端隊列(deque)的結構。
對于雙端隊列來說,頭插頭刪和尾插尾刪的效率都很高,時間復雜度是O(1).
對于普通的數組來說,只有尾插尾刪的效率高一點,但是頭插和頭刪會涉及到內存的大量操作,效率不高。
列表類型的特點:
-
- 列表中的元素是有序的,這意味著可以通過索引下標獲取某個元素或者某個范圍的元素列表,例如要獲取上圖第 5 個元素,可以執? lindex user:1:messages 4 或者倒數第 1 個元素,lindex
user:1:messages -1 就可以得到元素 e。
- 列表中的元素是有序的,這意味著可以通過索引下標獲取某個元素或者某個范圍的元素列表,例如要獲取上圖第 5 個元素,可以執? lindex user:1:messages 4 或者倒數第 1 個元素,lindex
這里的有序兩個字,要根據上下文區分。
很明顯,列表的有序指的是元素按照一定的順序排列的。
但是實際中的有序,可能是升序和降序,也有可能是按照某個條件進行的有序。
舉個例子:假如面試官問你堆棧的區別,你這時候就先不要著急回答。
而是先反問面試官,您這里說的堆棧,是數據結構的堆棧呢,還是操作系統的堆棧呢,還是JVM中的堆棧呢?亦或者是進程地址空間的堆棧呢?
還有一個同步,同步是指通過加鎖來保證線程安全的同步呢,還是IO中的同步和異步(這里的異步其實就是并發)中的同步呢?
-
- 區分獲取和刪除的區別,例如 lrem 1 b 是從列表中把從左數遇到的前 1 個 b 元素刪除,這個操作會導致列表的?度從 5 變成 4;但是執? lindex 4 只會獲取元素,但列表?度是不會變化的。
-
- 列表中的元素是允許重復的。
deque的底層結構介紹如下:
注意區分lindex和lrem,即獲取元素和刪除元素的區別。
lindex 能獲取到元素的值,lrem也能返回被刪除的元素的值。
因為當前的list的頭和尾都能高效地插入和刪除元素,所以當前的List可以用來作棧和隊列使用。
后端開發中,棧用的比較少,但是隊列就非常重要。比如使用隊列實現生產消費者模型。
還有實現消息隊列。
lpush,lpushx,rpush,rpushx命令
將一個或多個元素頭插到list中
lpush key element [element …]
比如:lpush key 1 2 3 4 ,插入完成后,列表的元素是4 3 2 1
返回值:插入后的list的長度。
如果key已經存在,且key對應的value不是list類型,則lpush命令會報錯。
Redis中所有類型的容器都是類似效果。
時間復雜度O(1).
lpushx命令:
在 key 存在時,將?個或者多個元素從左側放?(頭插)到 list 中。不存在,直接返回
lpushx key element [element … ]
時間復雜度O(1)
rpush和rpushx,其實就是尾插入,其他的完全相同。
操作的時間復雜度也是O(1)
lrange命令
獲取key對應的[start stop]區間的元素,左閉右閉。
lrange key start stop
時間復雜度O(n)
如果給出的下標超出范圍了,在Redis中的做法是:
直接盡可能給到區間內的元素,如果下標非法,就盡可能獲取對應的內容。
這就是所謂的 ”魯棒性“:你對我越粗魯,我表現的越棒。(其實就是容錯性強了)
然而在C++中,下標超出范圍,這是一個未定義行為:
1.可能導致程序崩潰
2.可能得到不合法的數據
3.可能得到看起來合法,但是是錯誤的數據。
4.可能得到一個恰好符合結果的數據
就像是開盲盒一樣。
缺點:程序員不能第一時間發現問題,可能會出現連鎖反應。
優點:效率是最高的。相比其他編程語言,比如java,對下標超范圍行為會多了一個合法性驗證,然后拋異常,這就導致多了一些動作。
但是人家java這樣做也有優點,就是對程序員來說能更快發現問題,也就能更高效開發代碼。
所以就產生了兩個問題:
是機器跑得快重要呢,還是程序員開發代碼更快重要呢?
肯定是程序員開發代碼重要,因為涉及到了利益問題,程序員如果開發的慢了,可能要加班修bug開發代碼,甚至如果搞砸了,可能要丟失年終獎。但是對程序員來說,機器跑的快不快跟我有啥關系呢,跑的慢的話,讓老板多搞兩臺機器過來不就行了嘛。
lpop和rpop
lpop相當于頭刪
lpop key
返回值:返回刪除后的元素,如果列表沒有元素了,則返回nil
時間復雜度O(1)
rpop相當于尾刪
rpop key
返回值:返回刪除后的元素,如果列表沒有元素了,則返回nil
時間復雜度O(1)
從Redis 6.2版本中,新增了一個count選項(當前我用的是Redis 5,暫不考慮)
lpop key [count]
表明要頭刪幾次
lindex命令
給定下標,獲取到指定下標的元素
lindex key index
支持負數下標,-1表示倒數第一個了。依次類推。
如果下標非法,返回nil
時間復雜度O(n),因為在redis的list列表不是一個簡單的數組,所以不能理解成O(1)的復雜度。
linsert命令
在指定位置插入元素
linsert key <before | after> pivot element
在指定的元素pivot之前/之后,插入元素element
如果指定的元素pivot在列表中存在多個,則在從左往右搜索到的第一個pivot之前/之后插入。
llen命令
獲取key對應的list的長度
llen key
lrem 命令
rem就是remove命令的縮寫,就是刪除命令。
lrem key count element
count是要刪除的個數,element是刪除的值
官方文檔給定的解釋如下
意思是:
如果count > 0,則是從頭到尾開始刪除等于element的元素,刪count個。
如果count < 0, 則是從尾到頭開始刪除等于element的元素,刪count個。
如果count = 0, 則是刪除所有等于element的元素。
lrem返回值是被刪除的元素個數。
ltrim命令
該命令是保留key對應的list的[start stop]區間內的元素(同樣是左閉右閉),區間外的元素都刪除。
ltrim key start stop
時間復雜度O(1),也可以理解為O(N),這個N是要刪除的元素個數。
lset命令
把key對應的list列表中的index下標的元素修改成element
lset key index element
時間復雜度O(N),如果是修改頭或者尾,則時間復雜度是O(1)
如果給的下標非法,則直接報錯。
相比于lindex命令不同的是,lindex命令對于非法的下標不報錯,而是盡可能滿足。。。
阻塞版本的命令
blpop和brpop
blpop key [key …] timeout
意思是可以同時對多個key的list進行頭刪
返回值是成功執行blpop命令的key和其刪除的元素
brpop key [key …] timeout
同理
這個timeout(必選項,單位是秒)是設置阻塞的時間,下面會詳細介紹
在list中存在元素的情況下,blpop和brpop命令和lpop,rpop命令作用完全相同。
但是如果list中沒有元素,則blpop和brpop會產生阻塞。
這里的阻塞很好理解,就是生產消費者模型中的阻塞。
這里并不是無休止地阻塞,阻塞時間由timeout決定,在阻塞期間,Redis可以執行其他命令(這是經過特殊處理的,畢竟怎么可能讓兩個命令阻塞住Redis處理命令時的單線程模型呢,如果阻塞住了, 其他客戶端發來的命令請求就得不到執行了。
注意事項:
- 1.如果命令設置了多個key,那么會從左到右遍歷這些key,一旦有其中一個key對應的list的元素就緒了,就會馬上頭刪該key對應的list,然后立即返回。
- 2.如果多個客戶端同時對一個key進行blpop/brpop,則最先執行命令的客戶端會得到刪除后的元素
所以返回值是一個二元組,一方面是告訴我這個數據是來自哪個key,一方面是告訴我這個數據是什么。
這種情況就是key不存在,所以阻塞住了,一旦key就緒了,就立即刪除返回。
這個15.17s就是阻塞的時長。
命令小結
list的內部編碼
其實前面的文章講過了。
List的應用場景
Redis 阻塞消息隊列模型
分頻道阻塞消息隊列模型