vlambda博客
学习文章列表

跳表查找和数组二分查找 对比

动机:

对比效率的想法源于公司的一次压测, 经过一轮排除下来 发现是由于排行榜引起 占了大部分CPU

 

对比代码

平台windows 10 x64

IDE  IntelliJ IDEA Community Edition 2020.3

Java  jdk 1.8.0_201

Kotlin  1.3.21

 

首先贴一份kotlin实现的链表

interface SkipList<E : RankElement> { val SKIPLIST_MAXLEVEL: Int get() = 32 val SKIPLIST_P: Double get() = 0.25 fun insert(element: E) fun delete(element: E) fun getRank(element: E): Long fun between(from: E, to: E): List<E> fun range(element: E, leftCount: Int, rightCount: Int): List<E> fun getElementByRank(rank: Long): E? fun createNode(level: Int, element: E?): SkipListNode<E> = SkipListNode(Array(level) { SkipListLevel<E>() }, element) fun firstRange(size: Int): List<E> /**根据排位来查找*/ fun findWithRange(from: Long, to: Long, filter: (element: E) -> Boolean = { true }): List<E> fun toList(): ArrayList<E>;}interface RankElement : Comparable<RankElement> { fun ownerId(): Long}class SkipListImpl<E : RankElement> : SkipList<E> { var level: Int = 1 private set var length: Int = 0 private set var header: SkipListNode<E>? = null private set var tail: SkipListNode<E>? = null private set init { this.header = createNode(SKIPLIST_MAXLEVEL, null) } //随机一个level fun randomLevel(): Int { var level = 1 while (random() and 0xFFFF < SKIPLIST_P * 0xFFFF) level += 1 return if (level < SKIPLIST_MAXLEVEL) level else SKIPLIST_MAXLEVEL } private fun random(): Int { return ThreadLocalRandom.current().nextInt() } override fun insert(element: E) { val update = arrayOfNulls<SkipListNode<E>>(SKIPLIST_MAXLEVEL) val rank = IntArray(SKIPLIST_MAXLEVEL) var x = this.header for (i in this.level - 1 downTo 0) { rank[i] = if (i == this.level - 1) 0 else rank[i + 1] var tempNode = x?.let { it.level[i].forward } while (tempNode != null) { val cmp = cmp(tempNode.element, element) if (cmp == 0) {//同一个元素 throw IllegalStateException("insert same element ${tempNode.element} $element") } if (cmp < 0) { rank[i] += requireNotNull(x?.let { it.level[i].span }) x = tempNode tempNode = x.let { it.level[i].forward } }else{ break } } update[i] = x } val level = randomLevel() if (level > this.level) { for (i in this.level until level) { rank[i] = 0; update[i] = this.header update[i]?.let { it.level[i].span = this.length } } this.level = level } val newNode = createNode(level, element) for (i in 0 until level) { update[i]?.let { newNode.level[i].forward = it.level[i].forward it.level[i].forward = newNode newNode.level[i].span = it.level[i].span - (rank[0] - rank[i]) it.level[i].span = (rank[0] - rank[i]) + 1 } } for (i in level until this.level) { update[i]?.let { it.level[i].span++ } } newNode.backward = if (update[0] == this.header) null else update[0] if (newNode.level[0].forward != null) { newNode.level[0].forward?.backward = newNode } else { this.tail = newNode } this.length++ } private fun cmp(element1: E?, element2: E): Int { if (element1 == null) { throw IllegalAccessException("元素不该为空") } return element1.compareTo(element2) } override fun delete(element: E) { val update = arrayOfNulls<SkipListNode<E>>(SKIPLIST_MAXLEVEL) var x: SkipListNode<E>? = this.header for (i in level - 1 downTo 0) { var forward = x?.let { it.level[i].forward } while (forward != null && cmp(forward.element, element) < 0) { x = forward forward = x.level[i].forward } update[i] = x } x = x?.let { it.level[0].forward } //找到哦了 x?.let { if (cmp(x.element, element) == 0) { for (i in 0 until this.level) { update[i]?.let { if (it.level[i].forward == x) { it.level[i].span += x.level[i].span - 1 it.level[i].forward = x.level[i].forward } else { it.level[i].span -= 1 } } } if (x.level[0].forward != null) { x.level[0].forward?.backward = x.backward } else { this.tail = x.backward } while (this.level > 1 && this.header?.let { it.level[this.level - 1].forward } == null) { this.level-- } this.length-- } } } override fun getRank(element: E): Long { var x = this.header var rank: Long = 0 for (i in (this.level - 1) downTo 0) { var forward = x?.let { it.level[i].forward } while (forward != null && cmp(forward.element, element) <= 0) { rank += (x?.let { it.level[i].span } ?: 0) x = forward forward = x.level[i].forward } } if (x?.element?.let { cmp(it, element) == 0 } == true) { return rank } return 0 } override fun between(from: E, to: E): List<E> { var x = this.header var rank: Long = 0 for (i in this.level - 1 downTo 0) { var forward = x?.let { it.level[i].forward } while (forward != null && cmp(forward.element, from) <= 0) { rank += (x?.let { it.level[i].span } ?: 0) x = forward forward = x.level[i].forward } } val elements = arrayListOf<E>() x?.let { var temp = it.level[0].forward while (temp != null) { val cmp = cmp(temp.element, to) if (cmp >= 0) { return@let } elements.add(temp.element!!) temp = temp.level[0].forward } } return elements } override fun range(element: E, leftCount: Int, rightCount: Int): List<E> { var x = this.header var rank: Long = 0 for (i in this.level - 1 downTo 0) { var forward = x?.let { it.level[i].forward } while (forward != null && cmp(forward.element, element) <= 0) { rank += (x?.let { it.level[i].span } ?: 0) x = forward forward = x.level[i].forward } } //找到自己的位置,朝前朝后找 val list = LinkedList<E>() x?.let { target -> var temp = target.backward repeat(leftCount) { temp?.let { list.addFirst(it.element) temp = it.backward } ?: return@repeat } temp = target.level[0].forward repeat(rightCount) { temp?.let { list.addLast(it.element) temp = it.level[0].forward } ?: return@repeat } } return list } override fun getElementByRank(rank: Long): E? { if (this.header == null || this.length < rank) { throw IllegalAccessException("排名不存在 $rank") } var x: SkipListNode<E>? = this.header var traversed: Long = 0 for (i in this.level - 1 downTo 0) { while (x?.let { it.level[i].forward != null && traversed + it.level[i].span <= rank } == true) { traversed += x.level[i].span x = x.level[i].forward } if (traversed == rank) { return x?.element } } return null } override fun firstRange(size: Int): List<E> { if (this.length == 0) { throw java.lang.IllegalStateException("元素为空") } val element = header?.level?.get(0)?.forward?.element return element?.let { range(it, 0, size) }!! } override fun findWithRange(from: Long, to: Long, filter: (element: E) -> Boolean): List<E> { if (this.header == null || this.length < from) { throw IllegalAccessException("排名不存在 $from") } val list = arrayListOf<E>() var x: SkipListNode<E>? = this.header var traversed: Long = 0 for (i in this.level - 1 downTo 0) { while (x?.let { it.level[i].forward != null && traversed + it.level[i].span <= from } == true) { traversed += x.level[i].span x = x.level[i].forward } if (traversed == from.toLong()) { repeat((to - from + 1).toInt()) { x?.let { list.add(requireNotNull(it.element)) x = it.level[0].forward } } } } return list } override fun toList(): ArrayList<E> { val arrayList = arrayListOf<E>() var x: SkipListNode<E>? = this.header repeat(this.length) { x?.let { arrayList.add(requireNotNull(it.level[0].forward?.element)) x = it.level[0].forward } } return arrayList }}class SkipListLevel<E> { //同level下一个元素 var forward: SkipListNode<E>? = null //同level跨越节点的数量 var span = 0}class SkipListNode<E>( //不同level指向的下一个元素和span var level: Array<SkipListLevel<E>>, var element: E? = null) { //元素 或集成 compareble //上一个元素 var backward: SkipListNode<E>? = null}class Rank<E : RankElement>( val skipList: SkipList<E> = SkipListImpl(), val elementMap: HashMap<Long, E> = hashMapOf()) { fun insert(element: E) { val remove = elementMap.remove(element.ownerId()) remove?.let { skipList.delete(it) } elementMap[element.ownerId()] = element skipList.insert(element) } fun remove(element: E) { elementMap.remove(element.ownerId())?.let { skipList.delete(it) } } fun toList(): ArrayList<E> { return skipList.toList() }}


对比测试的代码一

fun testSkipList(){ //初始化跳表排行榜 val skipList: SkipList<testElement> = SkipListImpl() val elementMap: HashMap<Long, testElement> = hashMapOf() val rank = Rank(skipList, elementMap) //准备跳表数据 repeat(10000){ rank.insert(testElement(it.toLong())) } //准备二维数组数据 val arrayList = arrayListOf<Int>() repeat(10000){ arrayList.add(it) } //测试跳表耗时 val t1 = System.nanoTime() repeat(1000) { //测试找单个 rank.skipList.getElementByRank((300+it).toLong()) //测试找范围 //rank.skipList.range(testElement(5000),10, 10) } val t2 = System.nanoTime() //测试二维数组耗时 val t3 = System.nanoTime() repeat(1000){ //测试找单个 arrayList.binarySearch(300+it) //测试找范围// repeat(20){// arrayList.binarySearch(4990+it)// } } val t4 = System.nanoTime() println("skiplistSearch time ${(t2-t1)/1000000.0} ms") println("binarySearch time ${(t4-t3)/1000000.0} ms")}


结果:

skiplistSearch time  1.2985 ms

binarySearch time  15.4235 ms

明显 跳表查找比二分查找还是有很大优势

 

对比测试的代码二

fun testSkipList(){ //初始化跳表排行榜 val skipList: SkipList<testElement> = SkipListImpl() val elementMap: HashMap<Long, testElement> = hashMapOf() val rank = Rank(skipList, elementMap) //准备跳表数据 repeat(10000){ rank.insert(testElement(it.toLong())) } //准备二维数组数据 val arrayList = arrayListOf<Int>() repeat(10000){ arrayList.add(it) } //测试跳表耗时 val t1 = System.nanoTime() repeat(1000) { //测试找单个 //rank.skipList.getElementByRank((300+it).toLong()) //测试找范围 rank.skipList.range(testElement(300),10, 10) } val t2 = System.nanoTime() //测试二维数组耗时 val t3 = System.nanoTime() repeat(1000){ //测试找单个 //arrayList.binarySearch(300+it) //测试找范围 repeat(20){ arrayList.binarySearch(300+it) } } val t4 = System.nanoTime() println("skiplistSearch time ${(t2-t1)/1000000.0} ms") println("binarySearch time ${(t4-t3)/1000000.0} ms")}


结果:

skiplistSearch time  3.2909 ms

binarySearch time  16.2339 ms

跳表依然比 二叉查找快

 

截图得时间 都是多次运行之后 取得平均出现次数比较高的

结论: 不管在单次查询 或者连续多次查询中  跳表查询 都比 二维数组的二分查询 有优势

 

写到这里肯定有有细心的读者 注意到为什么之前

arrayList.binarySearch(300+it)


repeat(20){ arrayList.binarySearch(300+it)}


几乎没有时间差距 一个是 15.4235 ms 一个是16.2339 ms

这是因为CPU发生了 缓存命中   

什么是缓存命中 下一篇文章讲