golang 实现 key有序map

简介:

Golang  map实现原理是hash  map(核心元素是桶,key通过哈希算法被归入不同的bucket中),key是无序的,很多应用场景可能需要map  key有序(例如交易所订单撮合),C++  的stl  map  实现了key有序,实际上是TreeMap是基于树(红黑树)的实现方式,即添加到一个有序列表,在O(log  n)的复杂度内通过key值找到value,优点是空间要求低,但在时间上不如HashMap。    

闲来用go  map  +  slice切片,实现了一套key有序map数据结构,就是空间换时间的玩法,  实质是map  负责存k  v,  slice负责维护k的有序索引位置(查找key采用的是2分法),实现后赠改删时间负责度是  O(log2n),  。  
优化的一点思考:实际上主要就是在slice上维护k位置时的增改删费操作,这时候我们可根据具体应用在2分查找上下点文章。  例如可能所存的数据结构频繁操作的节点只有前面一部分,这时候我们可以加个逻辑,操作时slice时先2查找  slice子集(例如头部热点),这样可能很多增改删操作在第一时间就解决了,整体性能会有很大提升,  最好根据应用场景来具体分析解决。下面给出代码。

Order_Map :

package Order_Map

func findIndexByBinarySearch(s []int, k int) (intbool) {
    lo, hi := 0len(s)
    var m int
    max := len(s)
    if max == 0 {
        return 0false
    }
    res := false
    for lo <= hi {
        m = (lo + hi) >> 1
        if m == 0 && s[0] > k {
            return 0, res
        }
        if m == max-1 && s[max-1] < k {
            return m + 1, res
        }
        if s[m] < k && s[m+1] > k {
            return m + 1, res
        }
        if s[m] > k && s[m-1] < k {
            return m, res
        }
        if s[m] < k {
            lo = m + 1
        } else if s[m] > k {
            hi = m - 1
        } else {
            return m, true
        }
    }
    return -1false
}

type Int_Map struct {
    dataMap  map[int]interface{}
    keyArray []int
}

func NewIntMap(cap int) *Int_Map {
    return &Int_Map{
        dataMap:  make(map[int]interface{}),
        keyArray: make([]int0cap),
    }
}

func (m *Int_Map) Exists(key int) bool {
    _, exists := m.dataMap[key]
    return exists
}

func (m *Int_Map) Insert(key int, data interface{}) bool {
    m.dataMap[key] = data
    index, res := findIndexByBinarySearch(m.keyArray, key)
    if index == -1 {
        return false
    }
    if res == true { //存在则直接返回
        return true
    }
    if len(m.keyArray) == 0 {
        m.keyArray = append(m.keyArray, key)
        return true
    }
    //追加末尾
    if index >= len(m.keyArray) {
        m.keyArray = append(m.keyArray[0:], []int{key}...)
    } else if index == 0 { //追加头部
        m.keyArray = append([]int{key}, m.keyArray[:len(m.keyArray)]...)
    } else { //插入
        rear := append([]int{}, m.keyArray[index:]...)
        m.keyArray = append(m.keyArray[0:index], key)
        m.keyArray = append(m.keyArray, rear...)
    }
    return true
}

func (m *Int_Map) Erase(key int) {
    if !m.Exists(key) {
        return
    }
    index, res := findIndexByBinarySearch(m.keyArray, key)
    if res == false {
        return
    }
    delete(m.dataMap, key)
    if index == 0 {
        m.keyArray = m.keyArray[1:]
    } else if index == len(m.keyArray) {
        m.keyArray = m.keyArray[:len(m.keyArray)-2]
    } else {
        m.keyArray = append(m.keyArray[:index], m.keyArray[index+1:]...)
    }

}

func (m *Int_Map) Size() int {
    return len(m.keyArray)
}

func (m *Int_Map) GetByOrderIndex(index int) (intinterface{}, bool) {
    if index < 0 || index >= len(m.keyArray) {
        return -1nilfalse
    }
    key := m.keyArray[index]
    return key, m.dataMap[key], true
}

测试:

package main

import (
    "Order_Map"
    "fmt"

    "math/rand"

    "reflect"
    "time"
)

func main() {

    t := time.Now()
    r := rand.New(rand.NewSource(time.Now().UnixNano()))
    testmap := Order_Map.NewIntMap(10000)
    t1 := t.Second()
    for i := 0; i < 10000; i++ {
        testmap.Insert(r.Intn(10000), r.Intn(10000))
    }
    t = time.Now()
    t2 := t.Second()
    fmt.Println("insert  time  span", t2-t1)
    testmap.Erase(88)
    for i := 0; i < testmap.Size(); i++ {
        k, v, _ := testmap.GetByOrderIndex(i)
        tmp_v := reflect.ValueOf(v)
        fmt.Println("k:", k, "---""value:", tmp_v)
    }

    t = time.Now()
    t3 := t.Second()
    fmt.Println("range  time  span:", t3-t2)

}


原文发布时间为:2018-10-7

本文作者:走路带风的龙歪歪

本文来自云栖社区合作伙伴“Golang语言社区”,了解相关信息可以关注“Golang语言社区”。

相关文章
|
1天前
|
存储 缓存 安全
只会“有序无序”?面试官嫌弃的List、Set、Map回答!
小米,一位热衷于技术分享的程序员,通过与朋友小林的对话,详细解析了Java面试中常见的List、Set、Map三者之间的区别,不仅涵盖了它们的基本特性,还深入探讨了各自的实现原理及应用场景,帮助面试者更好地准备相关问题。
34 20
|
1月前
|
存储 Java API
Java交换map的key和value值
通过本文介绍的几种方法,可以在Java中实现Map键值对的交换。每种方法都有其优缺点,具体选择哪种方法应根据实际需求和场景决定。对于简单的键值对交换,可以使用简单遍历法或Java 8的Stream API;对于需要处理值不唯一的情况,可以使用集合存储或Guava的Multimap。希望本文对您理解和实现Java中的Map键值对交换有所帮助。
33 1
|
3月前
|
Go
Golang语言之映射(map)快速入门篇
这篇文章是关于Go语言中映射(map)的快速入门教程,涵盖了map的定义、创建方式、基本操作如增删改查、遍历、嵌套map的使用以及相关练习题。
42 5
|
4月前
|
Java Serverless Go
Golang 开发函数计算问题之在 Golang 中避免 "concurrent map writes" 异常如何解决
Golang 开发函数计算问题之在 Golang 中避免 "concurrent map writes" 异常如何解决
|
6月前
|
存储 Java API
探讨Java中交换Map的Key和Value值的技术
探讨Java中交换Map的Key和Value值的技术
51 2
|
6月前
|
Go
GOLANG MAP 查找
GOLANG MAP 查找
104 3
|
6月前
|
存储 Go 索引
GOLANG MAP 底层实现
GOLANG MAP 底层实现
|
6月前
|
存储 缓存 Java
Java交换map的key和value值
在Java中,直接交换`Map`的key和value是不允许的,因为key是唯一的且不可变。不过,可以通过创建新`Map`实现交换:将原`Map`的value作为新key,key作为新value。注意,如果原`Map`有重复value或null,需额外处理。以下是一个代码示例,展示了如何在value唯一且非null的情况下交换`Map`的key和value。对于重复value或null值的情况,可以使用`List`存储多个key或忽略null值。在实际应用中,`Map`常用于缓存、配置管理、数据库结果映射等多种场景。
74 1
Map集合的有序遍历,解决方法多看一下别人的资料
Map集合的有序遍历,解决方法多看一下别人的资料
|
7月前
|
存储 缓存 安全
Golang深入浅出之-Go语言中的并发安全容器:sync.Map与sync.Pool
Go语言中的`sync.Map`和`sync.Pool`是并发安全的容器。`sync.Map`提供并发安全的键值对存储,适合快速读取和少写入的情况。注意不要直接遍历Map,应使用`Range`方法。`sync.Pool`是对象池,用于缓存可重用对象,减少内存分配。使用时需注意对象生命周期管理和容量控制。在多goroutine环境下,这两个容器能提高性能和稳定性,但需根据场景谨慎使用,避免不当操作导致的问题。
206 7