5. redis基本数据结构实现之 list

224 阅读4分钟

我正在参加「掘金·启航计划」。主要内容是对于校招练手项目的解析,促进自己更好地理解项目,理解redis。
redis的五种数据结构:string\ list\ hash\ set \sorted set

链表提供了高效的节点重排能力以及顺序的节点访问方式,并且可以通过增删节点来灵活的调整链表的长度。

列表键的底层实现之一就是链表,当一个列表键中包含数量较多的元素或列表中包含的元素都是较长字符串时,就会使用链表作为列表键的底层实现。

除了链表键之外,发布订阅等功能也使用到了链表,redis服务器本身使用链表来保存多个客户端的状态信息,以及使用链表构建客户端输出缓冲区。

在本项目中实现了linklist和quicklist两种编码

linklist

链表和链表节点的实现

//链表的每一个链表节点
type node struct {
	val  interface{}	//节点的值
	prev *node			//前置节点
	next *node			//后置节点
}
// LinkedList 双向链表
type LinkedList struct {
	first *node //头节点
	last  *node //尾节点
	size  int   //节点长度
}
  • 双端。获取前置节点和后置节点的复杂度都是o(1)
  • 无环
  • 获得表头和表尾节点的复杂度是o(1)

llen -> 返回链表长度

// Len returns the number of elements in list
func (list *LinkedList) Len() int {
	if list == nil {
		panic("list is nil")
	}
	return list.size
}

rpush -> 包含给定值的新节点添加到链表表尾

// Add adds value to the tail
func (list *LinkedList) Add(val interface{}) {
	if list == nil {
		panic("list is nil")
	}
	n := &node{
		val: val,
	}
	if list.last == nil {
		// 对于空链表
		list.first = n
		list.last = n
	} else {
        //对于非空链表 在表尾插入 val
		n.prev = list.last
		list.last.next = n
		list.last = n
	}
	list.size++
}

lindex-> 返回链表在给定索引上的节点

//对外不可见 抽象方法
func (list *LinkedList) find(index int) (n *node) {
	if index < list.size/2 {
        //从头节点寻找 .next
		n = list.first
		for i := 0; i < index; i++ {
			n = n.next
		}
	} else {
        //通过尾节点寻找 .prev
		n = list.last
		for i := list.size - 1; i > index; i-- {
			n = n.prev
		}
	}
	return n
}
// Get returns value at the given index
func (list *LinkedList) Get(index int) (val interface{}) {
	if list == nil {
		panic("list is nil")
	}
	if index < 0 || index >= list.size {
		panic("index out of bound")
	}
	return list.find(index).val
}

lset->修改指定index的value

func (list *LinkedList) Set(index int, val interface{}) {
	if list == nil {
		panic("list is nil")
	}
	if index < 0 || index > list.size {
		panic("index out of bound")
	}
	n := list.find(index)
	n.val = val
}

linsert/lpush/rpush->指定index插入节点

func (list *LinkedList) Insert(index int, val interface{}) {
	if list == nil {
		panic("list is nil")
	}
	if index < 0 || index > list.size {
		panic("index out of bound")
	}
//如果插入索引是列表长度,直接调用rpush 插入末尾即可
	if index == list.size {
		list.Add(val)
		return
	}
	// 找到指定索引的val
	pivot := list.find(index)
    //新建节点 prev是前一个节点 next是原索引节点
	n := &node{
		val:  val,
		prev: pivot.prev,
		next: pivot,
	}
    //如果前一个节点为空 那么作为头节点插入
	if pivot.prev == nil {
		list.first = n
	} else {
		pivot.prev.next = n
	}
    //原节点的前置节点是插入节点
	pivot.prev = n
	list.size++
}

lpop/rpop/lrem ->删除指定节点

func (list *LinkedList) removeNode(n *node) {
	if n.prev == nil {
		list.first = n.next
	} else {
		n.prev.next = n.next
	}
	if n.next == nil {
		list.last = n.prev
	} else {
		n.next.prev = n.prev
	}

	// for gc
	n.prev = nil
	n.next = nil

	list.size--
}

以下是由抽象方法removeNode延伸出的方法

rpop

// RemoveLast removes the last element and returns its value 删除尾节点
//通过获取尾节点 然后删除
func (list *LinkedList) RemoveLast() (val interface{}) {
	if list == nil {
		panic("list is nil")
	}
	if list.last == nil {
		// empty list
		return nil
	}
	n := list.last
	list.removeNode(n)
	return n.val
}

lrem

// Remove removes value at the given index
// 根据索引获得节点后 删除
func (list *LinkedList) Remove(index int) (val interface{}) {
	if list == nil {
		panic("list is nil")
	}
	if index < 0 || index >= list.size {
		panic("index out of bound")
	}

	n := list.find(index)
	list.removeNode(n)
	return n.val
}

ltrim listkey count value

找到符合Expected的进行删除,删除count个

// 从左到右寻找删除
func (list *LinkedList) RemoveByVal(expected Expected, count int) int {
	if list == nil {
		panic("list is nil")
	}
	n := list.first
	removed := 0
	var nextNode *node
	for n != nil {
		nextNode = n.next
		if expected(n.val) {
			list.removeNode(n)
			removed++
		}
		if removed == count {
			break
		}
		n = nextNode
	}
	return removed
}
// 从右到左寻找删除
func (list *LinkedList) ReverseRemoveByVal(expected Expected, count int) int {
	if list == nil {
		panic("list is nil")
	}
	n := list.last
	removed := 0
	var prevNode *node
	for n != nil {
		prevNode = n.prev
		if expected(n.val) {
			list.removeNode(n)
			removed++
		}
		if removed == count {
			break
		}
		n = prevNode
	}
	return removed
}
// Expected check whether given item is equals to expected value
type Expected func(a interface{}) bool
// 删除所有符合条件的
func (list *LinkedList) RemoveAllByVal(expected Expected) int {
	if list == nil {
		panic("list is nil")
	}
	n := list.first
	removed := 0
	var nextNode *node
	for n != nil {
		nextNode = n.next
		if expected(n.val) {
			list.removeNode(n)
			removed++
		}
		n = nextNode
	}
	return removed
}

遍历

遍历规则可以自定义

func (list *LinkedList) ForEach(consumer Consumer) {
	if list == nil {
		panic("list is nil")
	}
	n := list.first
	i := 0
	for n != nil {
		goNext := consumer(i, n.val)
		if !goNext {
			break
		}
		i++
		n = n.next
	}
}

lrange

根据自定义规则获取列表是否存在节点

// Contains returns whether the given value exist in the list
func (list *LinkedList) Contains(expected Expected) bool {
	contains := false
	list.ForEach(func(i int, actual interface{}) bool {
		if expected(actual) {
			contains = true
			return false
		}
		return true
	})
	return contains
}

lrange key start end

func (list *LinkedList) Range(start int, stop int) []interface{} {
	if list == nil {
		panic("list is nil")
	}
	if start < 0 || start >= list.size {
		panic("`start` out of range")
	}
	if stop < start || stop > list.size {
		panic("`stop` out of range")
	}

	sliceSize := stop - start
	slice := make([]interface{}, sliceSize)
	n := list.first
	i := 0
	sliceIndex := 0
	for n != nil {
		if i >= start && i < stop {
			slice[sliceIndex] = n.val
			sliceIndex++
		} else if i >= stop {
			break
		}
		i++
		n = n.next
	}
	return slice
}