Redis 學習筆記(篇一):字符串和鏈表

本次學習除了基本內容以外主要思考三個問題:why(爲何)、what(原理是什麼)、which(同類中還有哪些相似的東西,相比有什麼區別)。html

因爲我對 java 比較熟悉,而且 java 中也有字符串和鏈表。因此本篇暫拿 redis 中的字符串和鏈表與 java 進行對比。java

字符串

先看幾個問題:node

  • redis 中有沒有使用 C 語言的字符數組做爲字符串? 答案:沒有
  • 那麼 C 語言的字符數組有着什麼侷限性以致於java和redis都從新定義了本身的字符串呢?
  • redis 定義的字符串結構是什麼?java的呢?
  • java 和 redis 的字符串結構有什麼區別?爲何會造成這樣的區別?二者哪個更好呢?

C 語言數組的侷限性

  1. 取字符串長度不方便,c語言的字符數組沒有存儲字符串的長度,因此須要遍歷。Time(時間複雜度):O(n)
  2. 不安全,兩個字符數組進行拼接時可能會形成緩存區溢出,致使別的數據受損。
  3. 每次增刪字符串都會致使從新分配或者回收內存,影響效率。
  4. 以\0判斷是否結尾,只能存儲文本數據。
  5. 比較兩個字符串是否相等,須要循環比較每一個字符,Time:O(n)。

因爲 C 語言的字符數組有着以上的侷限性,因此 redis 和 java 都從新定義了本身的字符串結構。redis

redis 定義的字符串結構是什麼?java 的呢?

redis 中的字符串是本身構建了一種叫作簡單動態字符串(SDS)的抽象類型標識的。它的具體結構以下:數組

struct sdshdr {
    //字符串長度
    int len;
    // buf中未使用的字節數
    int free;
    // 字節數組,用於保存字符串
    char buf[];
}

redis 的這個數據結構,很好的解決了 C 語言字符數組的第一、二、三、4點侷限性。並且值得注意的是 sdshdr 中的 buf 數組,也是以「\0」結尾的,因此buf的總長度爲:len + free + 1。那麼爲何要浪費這一個字節呢?主要是想重用 C 語言對於字符串的一些函數,好比鏈接、輸出等等。緩存

這裏有一個點須要注意一下,就是 redis 不會對存儲的字符串進行編碼,存儲和獲取都是直接經過二進制進行傳輸的,因此理論上來講只要客戶端的編碼和解碼方式同樣,是不會出現亂碼的。這也是上面 buf 的註釋裏寫字節數組的緣由。安全

java 的字符串呢?則是直接把字符串搞成了一個常量。以下:數據結構

public final class String
    implements java.io.Serializable, Comparable<String>, CharSequence {
    /** The value is used for character storage. */
    private final char value[];

    /** Cache the hash code for the string */
    private int hash; // Default to 0
    ......
}

java 的字符串也解決了c語言字符數組的第 1/2/3/4 點。函數

  • 由於 java 中數組直接存儲了長度,因此規避了 C 語言數組的第 1/4 點侷限性。
  • 由於 String 是常量,因此c語言字符數組的第 2/3 點侷限性不存在。
  • 爲了解決第 5 點侷限性,因此java中的字符串引入了 hashCode 的概念。
    正是由於 hash 直接存儲在字符串中,因此才把字符串設置成常量(不然,每改一次字符串的值,都須要從新計算 hashcode 的值,太浪費效率)。正由於字符串是常量,因此纔有了 StringBuilder、StringBuffer 的可變字符串。。。。。。

如今咱們再回去看一下 redis 中字符串的結構,有一個數組,有數組中存儲數據的長度。想到了什麼?Java中 StringBuilder、StringBuffer、ArrayList 是否是都是這個結構?他們的共同點呢?是否是都是可變的數組( StringBuilder、StringBuffer 能夠當作可變的字符數組)?說到可變,這裏遺留一個問題:redis中的SDS、Java中的 (StringBuilder、StringBuffer、ArrayList ),他們的擴容規則分別是什麼呢?學習

java 和 redis 的字符串比較

  1. redis 字符串中的字符數組(buf)是以「\0」結尾的,Java 中的不是。爲何呢?
    由於 redis 的設計之初就是效率高、運行快的緩存。因此纔會選擇以 C 語言實現(由於c是全部結構化語言中運行最快的),而且和c的字符數組同樣的結尾,以即可以直接使用c的函數而不重複造輪子。
    java 的設計之初就是一門跨平臺的語言,因此字符串的全部函數都是本身實現的,因此不須要額外浪費一個字節的空間。
  2. redis 定義了一個 sds 的字符串,但也是基於 C 的字符數組。而java直接把c的數組都從新定義了( C 的數組不能直接得到數組長度)。
  3. 關於字符串比較是否相等的問題: java 中有 hashcode 的概念來提高字符串比較是否相等的速度。redis 中又是如何作的呢?好比 get key 時,如何定位的一個具體的 key,而獲得的 value 呢? 這個問題,暫時還不知道。

形成這些差別的緣由,主要就是其定位不同。redis 的定位是緩存、要求快。java 的定位是語言,最重要的是跨平臺及擴展性。

鏈表

也一樣看三個問題:

  1. 鏈表爲何產生?
  2. 原理是什麼,和別的數據結構再來個對比。
  3. 和 java 的鏈表有什麼不同?

鏈表爲何產生?

鏈表的產生主要是由於數組的侷限性。 好比:插入、刪除慢。不能動態增長元素。

原理是什麼

redis 的鏈表結構以下:

typedef struct listNode {
    struct listNode *prev; //前驅節點,若是是list的頭結點,則prev指向NULL
    struct listNode *next;//後繼節點,若是是list尾部結點,則next指向NULL
    void *value;            //萬能指針,可以存聽任何信息
} listNode;

typedef struct list {
    listNode *head;     //鏈表頭結點指針
    listNode *tail;     //鏈表尾結點指針

    //下面的三個函數指針就像類中的成員函數同樣
    void *(*dup)(void *ptr);    //複製鏈表節點保存的值
    void (*free)(void *ptr);    //釋放鏈表節點保存的值
    int (*match)(void *ptr, void *key); //比較鏈表節點所保存的節點值和另外一個輸入的值是否相等
    unsigned long len;      //鏈表長度計數器
} list;

java 的鏈表結構以下:

public class LinkedList<E>
    extends AbstractSequentialList<E>
    implements List<E>, Deque<E>, Cloneable, java.io.Serializable{
    transient int size = 0;

    /**
    * Pointer to first node.
    * Invariant: (first == null && last == null) ||
    *            (first.prev == null && first.item != null)
    */
    transient Node<E> first;

    /**
    * Pointer to last node.
    * Invariant: (first == null && last == null) ||
    *            (last.next == null && last.item != null)
    */
    transient Node<E> last;
    .......
}

redis 的鏈表和 java 的鏈表有什麼不同?

基本一致,都是雙向不循環鏈表。

雙向的優勢就是能夠向前遍歷,也能夠向後遍歷。缺點就是每一個節點都須要浪費一個指針去指向前一個節點。

原文出處:https://www.cnblogs.com/wind-snow/p/11019260.html

相關文章
相關標籤/搜索