内容简介:本次学习除了基本内容之外主要思考三个问题:why(为什么)、what(原理是什么)、which(同类中还有哪些类似的东西,相比有什么区别)。由于我对 java 比较熟悉,并且 java 中也有字符串和链表。所以本篇暂拿 redis 中的字符串和链表与 java 进行对比。先看几个问题:
本次学习除了基本内容之外主要思考三个问题:why(为什么)、what(原理是什么)、which(同类中还有哪些类似的东西,相比有什么区别)。
由于我对 java 比较熟悉,并且 java 中也有字符串和链表。所以本篇暂拿 redis 中的字符串和链表与 java 进行对比。
字符串
先看几个问题:
- redis 中有没有使用 C 语言的字符数组作为字符串? 答案:没有
- 那么 C 语言的字符数组有着什么局限性以至于java和redis都重新定义了自己的字符串呢?
- redis 定义的字符串结构是什么?java的呢?
- java 和 redis 的字符串结构有什么区别?为什么会形成这样的区别?两者哪一个更好呢?
C 语言数组的局限性
- 取字符串长度不方便,c语言的字符数组没有存储字符串的长度,所以需要遍历。Time(时间复杂度):O(n)
- 不安全,两个字符数组进行拼接时可能会造成缓存区溢出,导致别的数据受损。
- 每次增删字符串都会导致重新分配或者回收内存,影响效率。
- 以\0判断是否结尾,只能存储文本数据。
- 比较两个字符串是否相等,需要循环比较每个字符,Time:O(n)。
由于 C 语言的字符数组有着以上的局限性,所以 redis 和 java 都重新定义了自己的字符串结构。
redis 定义的字符串结构是什么?java 的呢?
redis 中的字符串是自己构建了一种叫做简单动态字符串(SDS)的抽象类型标识的。它的具体结构如下:
struct sdshdr { //字符串长度 int len; // buf中未使用的字节数 int free; // 字节数组,用于保存字符串 char buf[]; }
redis 的这个数据结构,很好的解决了 C 语言字符数组的第1、2、3、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 可以看成可变的字符数组)?
java 和 redis 的字符串比较
-
redis 字符串中的字符数组(buf)是以“\0”结尾的,Java 中的不是。为什么呢?
因为 redis 的设计之初就是效率高、运行快的缓存。所以才会选择以 C 语言实现(因为c是所有结构化语言中运行最快的),并且和c的字符数组一样的结尾,以便可以直接使用c的函数而不重复造轮子。
java 的设计之初就是一门跨平台的语言,所以字符串的所有函数都是自己实现的,所以不需要额外浪费一个字节的空间。 - redis 定义了一个 sds 的字符串,但也是基于 C 的字符数组。而java直接把c的数组都重新定义了( C 的数组不能直接获得数组长度)。
-
关于字符串比较是否相等的问题:
java 中有 hashcode 的概念来提升字符串比较是否相等的速度。redis 中又是如何做的呢?比如 get key 时,如何定位的一个具体的 key,而得到的 value 呢? 这个问题,暂时还不知道。
造成这些差异的原因,主要就是其定位不一样。redis 的定位是缓存、要求快。java 的定位是语言,最重要的是跨平台及扩展性。
链表
也同样看三个问题:
- 链表为什么产生?
- 原理是什么,和别的数据结构再来个对比。
- 和 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 的链表有什么不一样?
基本一致,都是双向不循环链表。
双向的优点就是可以向前遍历,也可以向后遍历。缺点就是每个节点都需要浪费一个指针去指向前一个节点。
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- 查找一个字符串中最长不含重复字符的子字符串,计算该最长子字符串的长度
- 字符串、字符处理总结
- 高频算法面试题(字符串)leetcode 387. 字符串中的第一个唯一字符
- php删除字符串最后一个字符
- (三)C语言之字符串与字符串函数
- 算法笔记字符串处理问题H:编排字符串(2064)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
深入理解Nginx
陶辉 / 机械工业出版社 / 2013-4-15 / 89.00元
本书是阿里巴巴资深Nginx技术专家呕心沥血之作,是作者多年的经验结晶,也是目前市场上唯一一本通过还原Nginx设计思想,剖析Nginx架构来帮助读者快速高效开发HTTP模块的图书。 本书首先通过介绍官方Nginx的基本用法和配置规则,帮助读者了解一般Nginx模块的用法,然后重点介绍如何开发HTTP模块(含HTTP过滤模块)来得到定制的Nginx,其中包括开发一个功能复杂的模块所需要了解的......一起来看看 《深入理解Nginx》 这本书的介绍吧!