mirror of
https://gitee.com/johng/gf
synced 2026-06-09 02:57:43 +08:00
Compare commits
514 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| da2ec21571 | |||
| ebb8d8a2f7 | |||
| 9706a9c768 | |||
| cee67a8d4e | |||
| 87650557fd | |||
| d3bf52f12f | |||
| 6b13a4849b | |||
| 8e380c0d9d | |||
| 0caf4bfcec | |||
| 9c3b978b50 | |||
| ab689a7792 | |||
| 846646d92d | |||
| 2eb2b89432 | |||
| 43441a8218 | |||
| 561a541fa1 | |||
| ffe9ecc141 | |||
| 77f7884604 | |||
| 0a203d1e22 | |||
| f4f4550483 | |||
| e87226a092 | |||
| 391a3ec9bd | |||
| dd5cd31ef5 | |||
| de92e804fe | |||
| 7725d9aaaf | |||
| bd3e25adea | |||
| 0b1d49af4b | |||
| 7efa9e351e | |||
| 0b0141954b | |||
| 82b531fbfb | |||
| 67fb626339 | |||
| 9044d5f05d | |||
| 47663aa1f1 | |||
| 63c0aab19c | |||
| 261216f5e4 | |||
| f88b799d67 | |||
| 9c48dd172c | |||
| 09ce105eee | |||
| 651aa895f8 | |||
| 0509e41198 | |||
| 1b40d6a53a | |||
| f9189d48d1 | |||
| 849874a247 | |||
| 3f6510bae7 | |||
| a585a26c39 | |||
| 9943966a86 | |||
| 3617e51c01 | |||
| c931032f08 | |||
| 37b286eaa4 | |||
| 619287c273 | |||
| aeb9b68298 | |||
| bae8f6315b | |||
| a6f70f8935 | |||
| acca6f4009 | |||
| 727fdd2391 | |||
| 1d174e00c0 | |||
| a5e3e2f5ba | |||
| da43c2d52f | |||
| 262f27748c | |||
| a29aef7e1c | |||
| 1671391195 | |||
| 28b0d59c61 | |||
| 86433cef25 | |||
| e96ccd5f71 | |||
| 50a087bb3d | |||
| 1ec049c52f | |||
| ec38805542 | |||
| 4c8517d075 | |||
| edf06da6ea | |||
| eb43a2040e | |||
| 9d0ecc7d3e | |||
| ad943c5e02 | |||
| bdb4fd0d25 | |||
| 2440e05457 | |||
| 1337c6c0d1 | |||
| 957689e07c | |||
| 3952d74f87 | |||
| 6dc4b81693 | |||
| 9cd953b7be | |||
| 631810dda2 | |||
| 8c12bc5506 | |||
| d4091a4826 | |||
| a7c269886b | |||
| f54593037b | |||
| 0415cf6a08 | |||
| 87c22d32b0 | |||
| 27dd15b403 | |||
| dd452c19ce | |||
| acc0846cf3 | |||
| 5a6738841f | |||
| bea451c9d6 | |||
| 676e904ec6 | |||
| 49aa5c61bc | |||
| a2272b852c | |||
| 1fa77630f9 | |||
| a841c4cc05 | |||
| 1874808e3b | |||
| 149b67916b | |||
| 7fb6f58162 | |||
| a309114a18 | |||
| 35cbde9530 | |||
| b9211b182a | |||
| 81ec499ae9 | |||
| f2e276eabd | |||
| f6dbaba1f8 | |||
| 65dcff052a | |||
| e3861567c7 | |||
| e89a20c725 | |||
| a8acc6bd28 | |||
| eb5efc735e | |||
| 737af527cd | |||
| 875d2b7e63 | |||
| bf1cb0e1bd | |||
| 2bfeb1b06c | |||
| 820e4302b7 | |||
| 84d761b418 | |||
| e5e27f2ac4 | |||
| efca9b18a8 | |||
| 607821ecbc | |||
| 7cc1b239d4 | |||
| efa8de34da | |||
| bcf45e3c5a | |||
| 0a3cd1d2ab | |||
| 3e621856c8 | |||
| 32e4d64ddb | |||
| 46e45ca84b | |||
| fcb13bd8ee | |||
| eacad9b453 | |||
| ed479e2a13 | |||
| 19937cb75d | |||
| a95093222c | |||
| 7b599d1882 | |||
| 4d501fd2f4 | |||
| c3d3053ded | |||
| 14d5fd3e11 | |||
| 9f79453334 | |||
| c7f1c881c0 | |||
| c39c032b4e | |||
| fa96c18308 | |||
| d90145a47f | |||
| 4871f86346 | |||
| 919eaf1e9a | |||
| 8a84ca16d1 | |||
| 12b4fdd692 | |||
| 3d8451d5d0 | |||
| cf88f28519 | |||
| 15d99eee46 | |||
| 6d68277db8 | |||
| 3e3b5557f7 | |||
| ba1a9d9f8e | |||
| 3e1a7953ec | |||
| 073fb2d717 | |||
| 7f33021184 | |||
| b396096721 | |||
| 0a5c6d832f | |||
| d279566114 | |||
| 2693cbb136 | |||
| f7a9be4292 | |||
| a926033b66 | |||
| dbcdd06b19 | |||
| ff5dab5c70 | |||
| 84b576418f | |||
| 253b124903 | |||
| 33dc5ddf79 | |||
| a456fa537c | |||
| 9e1fb93e08 | |||
| 5d24f702be | |||
| ca8e2c2986 | |||
| 2f0e5a45c5 | |||
| c4b28b0bc4 | |||
| 6cc4747965 | |||
| 91cd34b26a | |||
| e35a2f028c | |||
| 9cff2bd10f | |||
| 6d406498db | |||
| 939ae37baf | |||
| 4d6c8744e5 | |||
| e252d8b740 | |||
| 9b8d63e21b | |||
| 437fc04620 | |||
| 04dee090a3 | |||
| 245c6d24a1 | |||
| e92fd05f9f | |||
| 937f8e6919 | |||
| f489e6273e | |||
| 4f99bdbc87 | |||
| 1250b33220 | |||
| b57aee4595 | |||
| d9da51933d | |||
| 9fca93e7d8 | |||
| 854b2ed185 | |||
| cfac03bc40 | |||
| a3cb4a6ae8 | |||
| 2798fa4444 | |||
| 8bac0614f5 | |||
| 646280a6a9 | |||
| 208bdffdf7 | |||
| 9e7291903f | |||
| d56835fc00 | |||
| 1d5e717a80 | |||
| 2f44d9ae18 | |||
| 0627ab81d6 | |||
| 8167a398fc | |||
| 6291751014 | |||
| ee5ddaab52 | |||
| 207476be1f | |||
| b9b470c2ae | |||
| 52b6e8ef9d | |||
| 48c84bf74a | |||
| 5be30b3684 | |||
| 54a2b13825 | |||
| d44ddae3dc | |||
| 7bbc2459ba | |||
| 0cb77caa2a | |||
| 3f5f76458d | |||
| 835e07e8de | |||
| 3fc5e43abe | |||
| 9c8cb26bd6 | |||
| 540f4d2d0c | |||
| 51be255821 | |||
| 7d278fea25 | |||
| ca72d3b23a | |||
| 534cd3be1c | |||
| 78536de1b5 | |||
| edc67d9ec3 | |||
| 4dd12434b7 | |||
| f654bb2eda | |||
| 205f98cfeb | |||
| 69fa5bf464 | |||
| e7dc58ac6c | |||
| 2033299632 | |||
| 639d34d5d9 | |||
| 605181da32 | |||
| 183f631190 | |||
| 64c99b9871 | |||
| d29b0a27ff | |||
| aaa726e6dc | |||
| 05cc0c4644 | |||
| e1c0a92e60 | |||
| c770e4779a | |||
| c135122ca1 | |||
| 1d87df2afe | |||
| 8efc0ca0ea | |||
| 001d524ff7 | |||
| 5c5dce9dc3 | |||
| 293256c2ca | |||
| 4e027c1de3 | |||
| 6712a33164 | |||
| 98531143a6 | |||
| 71127ba308 | |||
| 0f6f571ccb | |||
| 9b6936a4fb | |||
| 8b78609412 | |||
| 4a31082f8c | |||
| 3643a69d8d | |||
| adbaa4aa89 | |||
| 0e025eda1b | |||
| 7c44bf8e94 | |||
| e6718f1113 | |||
| f3f6adb03a | |||
| 977827e453 | |||
| bcc9153991 | |||
| c04be14cd9 | |||
| de8f29751d | |||
| 12d58e4d08 | |||
| 3ae44185f4 | |||
| 1290f42f75 | |||
| 17b91dcad7 | |||
| 72da1642ee | |||
| 76d93b3a61 | |||
| 7be0ee9566 | |||
| 326e1f8da5 | |||
| e4e44ddd19 | |||
| 46bdde9265 | |||
| 09eba58927 | |||
| 0e884c78f5 | |||
| 2f44721086 | |||
| 6e08eebcbe | |||
| d9422d00ac | |||
| efc0501548 | |||
| bb07c60838 | |||
| 330ea05ca3 | |||
| 1f534b48a3 | |||
| aceae5eee3 | |||
| d4d66fd529 | |||
| b7686d6d37 | |||
| b97e397354 | |||
| ab8fbf171e | |||
| 2c0cfa24b0 | |||
| f28a76d470 | |||
| f3525c84a3 | |||
| abe9b54112 | |||
| b2aa59d893 | |||
| 9e9865afa7 | |||
| ecc439d4e8 | |||
| 452f0fc99e | |||
| 54f47845f6 | |||
| cb238cd6d9 | |||
| 897a9f4584 | |||
| 55f9b121de | |||
| 386f38af5e | |||
| 65cea430c2 | |||
| 4d38b508a3 | |||
| 5c774fd391 | |||
| f6d760e90f | |||
| c3ffa40bad | |||
| 5ce5d0e593 | |||
| b83f1efde8 | |||
| ca5f14c366 | |||
| 95a8b51fb4 | |||
| 2b64979730 | |||
| 508cb7db88 | |||
| e8d6d96883 | |||
| 9378a6e7bf | |||
| 1d609fc5c7 | |||
| 49d1e3dbaf | |||
| b0e9334852 | |||
| 95bd369959 | |||
| a433890097 | |||
| c9c3be517c | |||
| 89e122cd31 | |||
| 27c2a03ea8 | |||
| b56eb3a948 | |||
| fb1b0bfd88 | |||
| dd10167ec2 | |||
| 3138ad10ec | |||
| 080ca82605 | |||
| 0290de2360 | |||
| 9b330adc1d | |||
| f3ff1ae08b | |||
| 4f699af051 | |||
| 7ebc7259cb | |||
| 9d22edbdb8 | |||
| 7e95058cb5 | |||
| f33753e0cd | |||
| ac71e1d753 | |||
| 8151b6efd6 | |||
| 94de306c93 | |||
| 51f8ea26de | |||
| 29d9bb17cd | |||
| 30501a882d | |||
| f2f98e1d16 | |||
| cbf465eeea | |||
| 79c400e912 | |||
| cc4c49b5b0 | |||
| 96ea2c911d | |||
| 131b11680a | |||
| 3c6ab96283 | |||
| d2c4fa921a | |||
| dbb51a9328 | |||
| 55bfc3fa73 | |||
| d9c5182d1a | |||
| 899fcbf2da | |||
| 6e2c0d8706 | |||
| c4e599ce63 | |||
| 9cad9e4467 | |||
| 48019444ea | |||
| 76d31a7fbb | |||
| facb9d93c0 | |||
| 1d6bd46c5e | |||
| 8646dc1825 | |||
| 2992bdeed7 | |||
| c5601e6c88 | |||
| ef1d9a561c | |||
| 2d3b32c94a | |||
| 269378aa0d | |||
| 6889542801 | |||
| 8e6f1f1740 | |||
| b6ab1a992c | |||
| a5a267567c | |||
| f05f855c07 | |||
| bc5f773ba6 | |||
| 55308cc37c | |||
| 4d9db6edf0 | |||
| 38111a64e3 | |||
| bd8d3fca08 | |||
| 89ccaa3915 | |||
| 7c2cff7d99 | |||
| 788e15dbb6 | |||
| a0172d9d7e | |||
| bc1a7a1644 | |||
| c98234d3e6 | |||
| 45a94d23d5 | |||
| 351de5ee6a | |||
| 3559436d1e | |||
| 189f4c1637 | |||
| caead810e2 | |||
| ebdc5d9c9d | |||
| 4164059211 | |||
| bd27258c46 | |||
| ddf0605bc4 | |||
| 991dbe50e0 | |||
| 8050efb835 | |||
| 9355bc73a2 | |||
| acc2a6a353 | |||
| 09e83e7b8d | |||
| c0df8a3d80 | |||
| 33e890d225 | |||
| 750e5df962 | |||
| 74c65439fc | |||
| f290bd7170 | |||
| d398b749d4 | |||
| 2fd5a1574a | |||
| a7504f0629 | |||
| 3c35bb85ee | |||
| 6621edb04c | |||
| b0c722e297 | |||
| 47a6284274 | |||
| 150b8edb70 | |||
| aa5d3285eb | |||
| 993d6e3076 | |||
| 78ad9d8c2d | |||
| feff3ddce3 | |||
| 80fddad64d | |||
| 1e6dd0be02 | |||
| 22ecf4f1b6 | |||
| 5d21148657 | |||
| edb745ed92 | |||
| 61f4e0da6f | |||
| 767afa3106 | |||
| 16779359ee | |||
| 770653fafa | |||
| 0c021b6da7 | |||
| ec92b08f25 | |||
| 13e2353729 | |||
| 90d19a84ce | |||
| b4c0b95d8a | |||
| b7e8255066 | |||
| 5f8e6ad9ed | |||
| c8d253eb56 | |||
| 4814624cff | |||
| cc67f3d388 | |||
| f7c2a51c9f | |||
| 3db83e1159 | |||
| 3bb002796c | |||
| 45170bc53e | |||
| b79ff84c6f | |||
| 938c46fec9 | |||
| 8a13d94526 | |||
| 1e844d505a | |||
| a123a2c086 | |||
| 1eeeeb853e | |||
| 6e7224e306 | |||
| 9e064e2651 | |||
| 8d9dd17eac | |||
| cf1d3d3d2b | |||
| 9480ffcdc0 | |||
| 5db10add4a | |||
| fa66bf5d9d | |||
| f69da3ace1 | |||
| e01bfa05c3 | |||
| 231238c157 | |||
| 7edec099ab | |||
| 83eb8be064 | |||
| 7af30df494 | |||
| f026686fda | |||
| 35a50b9c6c | |||
| 010e2f951a | |||
| f7f86ad65a | |||
| 1e19f447d1 | |||
| 8cc378331d | |||
| 4721f68fd8 | |||
| 0d11c0a1f8 | |||
| 5076613a8f | |||
| c5a44daa65 | |||
| 520970b71f | |||
| ebfb08ee3f | |||
| 9e38b2cb90 | |||
| 71b1f00dc5 | |||
| 59d6830ab1 | |||
| 8779a3f211 | |||
| c10149baa0 | |||
| 4f87668780 | |||
| 63f33d1d8c | |||
| 734aa5a6fe | |||
| 371aef224d | |||
| 362e380ada | |||
| e995bd8c9a | |||
| 81b211dd1a | |||
| 0515fc94cb | |||
| 46ee070f0a | |||
| b17e3a6804 | |||
| 9160bee1af | |||
| 8e6018cfff | |||
| c08739b5a3 | |||
| 385a503d31 | |||
| ef286b0c15 | |||
| 53aba2d4b8 | |||
| f22da4ba3a | |||
| 23c2f12672 | |||
| 7fd53673ce | |||
| e64fd088b9 | |||
| 15672e7a09 | |||
| 2ea1d2c7b2 | |||
| 90d751f98d | |||
| 25a91c732c | |||
| 01995f5501 | |||
| 68abb3cf3d | |||
| 77cc323d0e | |||
| c7a9c03495 | |||
| f7850e3ed3 | |||
| 82125416a2 | |||
| 2c1e2155e3 | |||
| 2d30a53c3a | |||
| ccceeae29c | |||
| f22b98456f | |||
| e7f1bd692b | |||
| f82f7ab199 | |||
| 4d33b527b6 | |||
| 7bcc596308 | |||
| be2d4b080e | |||
| c96e5f5a9e | |||
| 3c23766674 | |||
| 718089fc11 | |||
| 8ab44dcb44 |
@ -512,16 +512,6 @@ func mapToStruct() {
|
||||
}
|
||||
}
|
||||
|
||||
// getQueriedSqls
|
||||
func getQueriedSqls() {
|
||||
for k, v := range db.GetQueriedSqls() {
|
||||
fmt.Println(k, ":")
|
||||
fmt.Println("Sql :", v.Sql)
|
||||
fmt.Println("Args :", v.Args)
|
||||
fmt.Println("Error:", v.Error)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
db.PingMaster()
|
||||
|
||||
@ -1,8 +1,14 @@
|
||||
|
||||
# MySQL数据库配置
|
||||
# MySQL.
|
||||
[database]
|
||||
# debug = true
|
||||
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test?parseTime=true&loc=Local"
|
||||
debug = true
|
||||
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test?parseTime=true&loc=Local"
|
||||
MaxOpen = 100
|
||||
|
||||
# Redis.
|
||||
[redis]
|
||||
default = "127.0.0.1:6379,0"
|
||||
cache = "127.0.0.1:6379,1"
|
||||
|
||||
#[database]
|
||||
# [[database.default]]
|
||||
|
||||
@ -2,7 +2,6 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
@ -11,11 +10,19 @@ func main() {
|
||||
// 开启调试模式,以便于记录所有执行的SQL
|
||||
db.SetDebug(true)
|
||||
|
||||
r, e := db.Table("test").Order("id asc").All()
|
||||
r, e := db.GetAll("SELECT * from `user` where id in(?)", g.Slice{})
|
||||
if e != nil {
|
||||
fmt.Println(e)
|
||||
}
|
||||
if r != nil {
|
||||
fmt.Println(r.List())
|
||||
fmt.Println(r)
|
||||
}
|
||||
return
|
||||
//r, e := db.Table("user").Where("id in(?)", g.Slice{}).All()
|
||||
//if e != nil {
|
||||
// fmt.Println(e)
|
||||
//}
|
||||
//if r != nil {
|
||||
// fmt.Println(r.List())
|
||||
//}
|
||||
}
|
||||
|
||||
@ -3,6 +3,7 @@ package main
|
||||
import (
|
||||
"github.com/gogf/gf/database/gdb"
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
"time"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -10,7 +11,7 @@ func main() {
|
||||
Host: "127.0.0.1",
|
||||
Port: "3306",
|
||||
User: "root",
|
||||
Pass: "123456",
|
||||
Pass: "12345678",
|
||||
Name: "test",
|
||||
Type: "mysql",
|
||||
Role: "master",
|
||||
@ -20,19 +21,20 @@ func main() {
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
//db.GetCache().SetAdapter(adapter.NewRedis(g.Redis()))
|
||||
// 开启调试模式,以便于记录所有执行的SQL
|
||||
db.SetDebug(true)
|
||||
|
||||
// 执行2次查询并将查询结果缓存3秒,并可执行缓存名称(可选)
|
||||
for i := 0; i < 2; i++ {
|
||||
r, _ := db.Table("user").Cache(3, "vip-user").Where("uid=?", 1).One()
|
||||
gutil.Dump(r.ToMap())
|
||||
for i := 0; i < 3; i++ {
|
||||
r, _ := db.Table("user").Cache(3000*time.Second).Where("id=?", 1).One()
|
||||
gutil.Dump(r.Map())
|
||||
}
|
||||
|
||||
// 执行更新操作,并清理指定名称的查询缓存
|
||||
db.Table("user").Cache(-1, "vip-user").Data(gdb.Map{"name": "smith"}).Where("uid=?", 1).Update()
|
||||
//db.Table("user").Cache(-1, "vip-user").Data(gdb.Map{"name": "smith"}).Where("id=?", 1).Update()
|
||||
|
||||
// 再次执行查询,启用查询缓存特性
|
||||
r, _ := db.Table("user").Cache(3, "vip-user").Where("uid=?", 1).One()
|
||||
gutil.Dump(r.ToMap())
|
||||
//r, _ := db.Table("user").Cache(300000*time.Second, "vip-user").Where("id=?", 1).One()
|
||||
//gutil.Dump(r.Map())
|
||||
}
|
||||
|
||||
@ -8,9 +8,6 @@ import (
|
||||
|
||||
func main() {
|
||||
db := g.DB()
|
||||
db.SetMaxIdleConnCount(10)
|
||||
db.SetMaxOpenConnCount(10)
|
||||
db.SetMaxConnLifetime(time.Minute)
|
||||
|
||||
// 开启调试模式,以便于记录所有执行的SQL
|
||||
db.SetDebug(true)
|
||||
@ -19,7 +16,7 @@ func main() {
|
||||
for i := 0; i < 10; i++ {
|
||||
go db.Table("user").All()
|
||||
}
|
||||
time.Sleep(time.Second)
|
||||
time.Sleep(time.Millisecond * 100)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@ -8,5 +8,5 @@ func main() {
|
||||
db := g.DB()
|
||||
db.SetDebug(true)
|
||||
|
||||
db.Table("user").Fields("DISTINCT id,nickname").Filter().All()
|
||||
db.Table("user").Data("num=num+1").Where("id", 8).Update()
|
||||
}
|
||||
|
||||
@ -513,16 +513,6 @@ func mapToStruct() {
|
||||
}
|
||||
}
|
||||
|
||||
// getQueriedSqls
|
||||
func getQueriedSqls() {
|
||||
for k, v := range db.GetQueriedSqls() {
|
||||
fmt.Println(k, ":")
|
||||
fmt.Println("Sql :", v.Sql)
|
||||
fmt.Println("Args :", v.Args)
|
||||
fmt.Println("Error:", v.Error)
|
||||
}
|
||||
}
|
||||
|
||||
func main() {
|
||||
|
||||
db.PingMaster()
|
||||
|
||||
@ -1,16 +1,68 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"archive/zip"
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/encoding/gcompress"
|
||||
"io"
|
||||
"os"
|
||||
"path/filepath"
|
||||
"strings"
|
||||
)
|
||||
|
||||
func main() {
|
||||
err := gcompress.ZipPath(
|
||||
`D:\Workspace\Go\GOPATH\src\github.com\gogf\gf\geg`,
|
||||
`D:\Workspace\Go\GOPATH\src\github.com\gogf\gf\geg\encoding\gcompress\data.zip`,
|
||||
"my-dir",
|
||||
)
|
||||
fmt.Println(err)
|
||||
// srcFile could be a single file or a directory
|
||||
func Zip(srcFile string, destZip string) error {
|
||||
zipfile, err := os.Create(destZip)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer zipfile.Close()
|
||||
|
||||
archive := zip.NewWriter(zipfile)
|
||||
defer archive.Close()
|
||||
|
||||
filepath.Walk(srcFile, func(path string, info os.FileInfo, err error) error {
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
header, err := zip.FileInfoHeader(info)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
header.Name = strings.TrimPrefix(path, filepath.Dir(srcFile)+"/")
|
||||
// header.Name = path
|
||||
if info.IsDir() {
|
||||
header.Name += "/"
|
||||
} else {
|
||||
header.Method = zip.Deflate
|
||||
}
|
||||
|
||||
writer, err := archive.CreateHeader(header)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
if !info.IsDir() {
|
||||
file, err := os.Open(path)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
defer file.Close()
|
||||
_, err = io.Copy(writer, file)
|
||||
}
|
||||
return err
|
||||
})
|
||||
|
||||
return err
|
||||
}
|
||||
|
||||
func main() {
|
||||
src := `/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/test`
|
||||
dst := `/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/test.zip`
|
||||
//src := `/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/README.MD`
|
||||
//dst := `/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/README.MD.zip`
|
||||
fmt.Println(gcompress.ZipPath(src, dst))
|
||||
//fmt.Println(Zip(src, dst))
|
||||
}
|
||||
|
||||
@ -2,25 +2,14 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/i18n/gi18n"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func main() {
|
||||
t := gi18n.New()
|
||||
t.SetPath("/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/i18n/gi18n/i18n")
|
||||
t.SetLanguage("en")
|
||||
fmt.Println(t.Translate(`hello`))
|
||||
fmt.Println(t.Translate(`{#hello}{#world}!`))
|
||||
|
||||
t.SetLanguage("ja")
|
||||
fmt.Println(t.Translate(`hello`))
|
||||
fmt.Println(t.Translate(`{#hello}{#world}!`))
|
||||
|
||||
t.SetLanguage("ru")
|
||||
fmt.Println(t.Translate(`hello`))
|
||||
fmt.Println(t.Translate(`{#hello}{#world}!`))
|
||||
|
||||
fmt.Println(t.Translate(`hello`, "zh-CN"))
|
||||
fmt.Println(t.Translate(`{#hello}{#world}!`, "zh-CN"))
|
||||
var (
|
||||
orderId = 865271654
|
||||
orderAmount = 99.8
|
||||
)
|
||||
fmt.Println(g.I18n().Tfl(`en`, `{#OrderPaid}`, orderId, orderAmount))
|
||||
fmt.Println(g.I18n().Tfl(`zh-CN`, `{#OrderPaid}`, orderId, orderAmount))
|
||||
}
|
||||
|
||||
@ -1,3 +1 @@
|
||||
|
||||
hello = "Hello"
|
||||
world = "World"
|
||||
OrderPaid = "You have successfully complete order #%d payment, paid amount: ¥%0.2f."
|
||||
@ -1,2 +1 @@
|
||||
hello = "你好"
|
||||
world = "世界"
|
||||
OrderPaid = "您已成功完成订单号 #%d 支付,支付金额¥%.2f。"
|
||||
@ -9,7 +9,7 @@ import (
|
||||
func Upload(r *ghttp.Request) {
|
||||
saveDirPath := "/tmp/"
|
||||
files := r.GetUploadFiles("upload-file")
|
||||
if err := files.Save(saveDirPath); err != nil {
|
||||
if _, err := files.Save(saveDirPath); err != nil {
|
||||
r.Response.WriteExit(err)
|
||||
}
|
||||
r.Response.WriteExit("upload successfully")
|
||||
|
||||
@ -2,12 +2,34 @@ package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"path/filepath"
|
||||
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/glog"
|
||||
)
|
||||
|
||||
func SendXmlFile(gameId int, areaName string, filePath string) error {
|
||||
path := filepath.FromSlash(filePath)
|
||||
fmt.Println(path)
|
||||
data := g.Map{
|
||||
"gameName": gameId,
|
||||
"area": areaName,
|
||||
"file": "@file:" + path,
|
||||
"contentType": "json",
|
||||
}
|
||||
if r, err := ghttp.Post("http://127.0.0.1:8199/upload", data); err != nil {
|
||||
panic(err)
|
||||
} else {
|
||||
defer r.Close()
|
||||
fmt.Println("ok")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func main() {
|
||||
SendXmlFile(1, "xxx", "/Users/john/Workspace/Go/GOPATH/src/github.com/gogf/gf/.example/net/ghttp/server/session.go")
|
||||
return
|
||||
path := "/home/john/Workspace/Go/github.com/gogf/gf/version.go"
|
||||
r, e := ghttp.Post("http://127.0.0.1:8199/upload", "upload-file=@file:"+path)
|
||||
if e != nil {
|
||||
|
||||
@ -8,8 +8,8 @@ import (
|
||||
// Upload uploads files to /tmp .
|
||||
func Upload(r *ghttp.Request) {
|
||||
saveDirPath := "/tmp/"
|
||||
files := r.GetUploadFiles("upload-file")
|
||||
if err := files.Save(saveDirPath); err != nil {
|
||||
files := r.GetUploadFiles("file")
|
||||
if _, err := files.Save(saveDirPath); err != nil {
|
||||
r.Response.WriteExit(err)
|
||||
}
|
||||
r.Response.WriteExit("upload successfully")
|
||||
|
||||
@ -3,15 +3,21 @@ package main
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
"github.com/gogf/gf/os/glog"
|
||||
)
|
||||
|
||||
type GetById struct {
|
||||
Id *g.Var `p:"id" v:"required|integer#id不能为空|id必须为整数"`
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.SetIndexFolder(true)
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
glog.Println(r.Header)
|
||||
r.Response.Write("hello world")
|
||||
var idInfo *GetById
|
||||
if err := r.Parse(&idInfo); err != nil {
|
||||
r.Response.Write(err)
|
||||
}
|
||||
r.Response.Write("ok")
|
||||
})
|
||||
s.SetPort(8999)
|
||||
s.Run()
|
||||
|
||||
@ -5,21 +5,18 @@ import (
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
type User struct {
|
||||
}
|
||||
type User struct{}
|
||||
|
||||
func (c *User) Index(r *ghttp.Request) {
|
||||
r.Response.Write("Index")
|
||||
}
|
||||
|
||||
// 不符合规范,不会被注册
|
||||
func (c *User) Test(r *ghttp.Request, value interface{}) {
|
||||
func (c *User) Test(r *ghttp.Request) {
|
||||
r.Response.Write("Test")
|
||||
}
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindObject("/user", new(User))
|
||||
u := new(User)
|
||||
s.Group("/", func(group *ghttp.RouterGroup) {
|
||||
group.GET("/db-{table}/{id}", u, "Test")
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
|
||||
@ -1,12 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := ghttp.GetServer()
|
||||
s.EnablePProf()
|
||||
s := g.Server()
|
||||
s.Domain("localhost").EnablePProf()
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
r.Response.Writeln("哈喽世界!")
|
||||
})
|
||||
|
||||
@ -6,12 +6,8 @@ import (
|
||||
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.SetConfigWithMap(g.Map{"Graceful": true})
|
||||
s.EnableAdmin()
|
||||
//s.BindHookHandler("/admin/*any", ghttp.HOOK_BEFORE_SERVE, func(r *ghttp.Request) {
|
||||
// if !r.BasicAuth("admin", "123", "") {
|
||||
// r.Exit()
|
||||
// }
|
||||
//})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
|
||||
@ -1,29 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/os/gfcache"
|
||||
"github.com/gogf/gf/os/gfile"
|
||||
)
|
||||
|
||||
func main() {
|
||||
s := 0
|
||||
r := ""
|
||||
path := gfile.TempDir() + gfile.Separator + "temp"
|
||||
gfile.PutContents(path, "hello")
|
||||
|
||||
s = gfcache.GetSize()
|
||||
r = gfcache.GetContents(path)
|
||||
fmt.Println(s, r)
|
||||
|
||||
gfile.PutContentsAppend(path, " john")
|
||||
|
||||
// 等待1秒以便gfsnotify回调能处理完成
|
||||
time.Sleep(time.Second)
|
||||
|
||||
s = gfcache.GetSize()
|
||||
r = gfcache.GetContents(path)
|
||||
fmt.Println(s, r)
|
||||
}
|
||||
14
.example/os/glog/glog_ctx.go
Normal file
14
.example/os/glog/glog_ctx.go
Normal file
@ -0,0 +1,14 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"context"
|
||||
"github.com/gogf/gf/os/glog"
|
||||
)
|
||||
|
||||
func main() {
|
||||
glog.SetCtxKeys("Trace-Id", "Span-Id", "Test")
|
||||
ctx := context.WithValue(context.Background(), "Trace-Id", "1234567890")
|
||||
ctx = context.WithValue(ctx, "Span-Id", "abcdefg")
|
||||
|
||||
glog.Ctx(ctx).Print(1, 2, 3)
|
||||
}
|
||||
@ -2,19 +2,18 @@ package main
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/frame/gmvc"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
type Controller struct {
|
||||
gmvc.Controller
|
||||
}
|
||||
|
||||
func (c *Controller) Test() {
|
||||
c.View.Display("layout.html")
|
||||
}
|
||||
func main() {
|
||||
s := g.Server()
|
||||
s.BindControllerMethod("/", new(Controller), "Test")
|
||||
s.BindHandler("/", func(r *ghttp.Request) {
|
||||
r.Response.WriteTpl("layout.html", g.Map{
|
||||
"header": "This is header",
|
||||
"container": "This is container",
|
||||
"footer": "This is footer",
|
||||
})
|
||||
})
|
||||
s.SetPort(8199)
|
||||
s.Run()
|
||||
}
|
||||
|
||||
@ -1,3 +1,3 @@
|
||||
{{define "container"}}
|
||||
<h1>CONTAINER</h1>
|
||||
<h1>{{.container}}</h1>
|
||||
{{end}}
|
||||
@ -1,3 +1,3 @@
|
||||
{{define "footer"}}
|
||||
<h1>FOOTER</h1>
|
||||
<h1>{{.footer}}</h1>
|
||||
{{end}}
|
||||
@ -1,3 +1,3 @@
|
||||
{{define "header"}}
|
||||
<h1>HEADER</h1>
|
||||
<h1>{{.header}}</h1>
|
||||
{{end}}
|
||||
@ -2,14 +2,14 @@
|
||||
<html>
|
||||
<head>
|
||||
<title>GoFrame Layout</title>
|
||||
{{template "header"}}
|
||||
{{template "header" .}}
|
||||
</head>
|
||||
<body>
|
||||
<div class="container">
|
||||
{{template "container"}}
|
||||
{{template "container" .}}
|
||||
</div>
|
||||
<div class="footer">
|
||||
{{template "footer"}}
|
||||
{{template "footer" .}}
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
@ -9,11 +9,13 @@ func main() {
|
||||
s := g.Server()
|
||||
s.BindHandler("/main1", func(r *ghttp.Request) {
|
||||
r.Response.WriteTpl("layout.html", g.Map{
|
||||
"name": "smith",
|
||||
"mainTpl": "main/main1.html",
|
||||
})
|
||||
})
|
||||
s.BindHandler("/main2", func(r *ghttp.Request) {
|
||||
r.Response.WriteTpl("layout.html", g.Map{
|
||||
"name": "john",
|
||||
"mainTpl": "main/main2.html",
|
||||
})
|
||||
})
|
||||
|
||||
@ -1,18 +0,0 @@
|
||||
|
||||
[database]
|
||||
debug = true
|
||||
link = "mysql:root:12345678@tcp(127.0.0.1:3306)/test"
|
||||
|
||||
[redis]
|
||||
default = "127.0.0.1:6379,0"
|
||||
cache = "127.0.0.1:6379,1"
|
||||
|
||||
# Logger.
|
||||
[logger]
|
||||
Path = "/tmp/log/gf-app"
|
||||
Level = "all"
|
||||
Stdout = true
|
||||
|
||||
[viewer]
|
||||
delimiters = ["${", "}"]
|
||||
autoencode = true
|
||||
@ -1,21 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
)
|
||||
|
||||
func Test() (int, int) {
|
||||
return 1, 1
|
||||
}
|
||||
|
||||
func Assert(v1, v2, v3 interface{}) {
|
||||
fmt.Println(v1)
|
||||
}
|
||||
|
||||
func F(v ...interface{}) []interface{} {
|
||||
return v
|
||||
}
|
||||
|
||||
func main() {
|
||||
Assert(F(Test()), 2, 3)
|
||||
}
|
||||
@ -1,11 +0,0 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
r := ghttp.PostContent("http://127.0.0.1:8199/test", `<doc><id>1</id><name>john</name><password1>123Abc!@#</password1><password2>123Abc!@#</password2></doc>`)
|
||||
fmt.Println(r)
|
||||
}
|
||||
@ -8,6 +8,9 @@ import (
|
||||
|
||||
func main() {
|
||||
for i := 0; i < 100; i++ {
|
||||
fmt.Println(grand.Rand(0, 99999))
|
||||
fmt.Println(grand.S(16))
|
||||
}
|
||||
for i := 0; i < 100; i++ {
|
||||
fmt.Println(grand.N(0, 99999999))
|
||||
}
|
||||
}
|
||||
|
||||
13
.example/util/guid/guid.go
Normal file
13
.example/util/guid/guid.go
Normal file
@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/util/guid"
|
||||
)
|
||||
|
||||
func main() {
|
||||
for i := 0; i < 100; i++ {
|
||||
s := guid.S()
|
||||
fmt.Println(s, len(s))
|
||||
}
|
||||
}
|
||||
20
.example/util/guid/guid_length.go
Normal file
20
.example/util/guid/guid_length.go
Normal file
@ -0,0 +1,20 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"math"
|
||||
"strconv"
|
||||
)
|
||||
|
||||
func main() {
|
||||
// 36*36^2+36*36+36
|
||||
var s string
|
||||
fmt.Println(strconv.ParseUint("zzz", 36, 3))
|
||||
fmt.Println(1 << 1)
|
||||
// MaxInt64
|
||||
s = strconv.FormatUint(math.MaxUint64, 16)
|
||||
fmt.Println(s, len(s))
|
||||
// PID
|
||||
s = strconv.FormatInt(1000000, 36)
|
||||
fmt.Println(s, len(s))
|
||||
}
|
||||
23
.example/util/guid/guid_unique.go
Normal file
23
.example/util/guid/guid_unique.go
Normal file
@ -0,0 +1,23 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/util/guid"
|
||||
)
|
||||
|
||||
func main() {
|
||||
for i := 0; i < 100; i++ {
|
||||
s := guid.S([]byte("123"))
|
||||
fmt.Println(s, len(s))
|
||||
}
|
||||
fmt.Println()
|
||||
for i := 0; i < 100; i++ {
|
||||
s := guid.S([]byte("123"), []byte("456"))
|
||||
fmt.Println(s, len(s))
|
||||
}
|
||||
fmt.Println()
|
||||
for i := 0; i < 100; i++ {
|
||||
s := guid.S([]byte("123"), []byte("456"), []byte("789"))
|
||||
fmt.Println(s, len(s))
|
||||
}
|
||||
}
|
||||
@ -11,7 +11,7 @@ func main() {
|
||||
fmt.Println(1)
|
||||
gutil.Throw("error")
|
||||
fmt.Println(2)
|
||||
}, func(err interface{}) {
|
||||
}, func(err error) {
|
||||
fmt.Println(err)
|
||||
})
|
||||
}
|
||||
|
||||
13
.example/util/gvalid/gvalid_custom_message.go
Normal file
13
.example/util/gvalid/gvalid_custom_message.go
Normal file
@ -0,0 +1,13 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/util/gvalid"
|
||||
)
|
||||
|
||||
func main() {
|
||||
g.I18n().SetLanguage("cn")
|
||||
err := gvalid.Check("", "required", nil)
|
||||
fmt.Println(err.String())
|
||||
}
|
||||
14
.example/util/gvalid/i18n/cn.toml
Normal file
14
.example/util/gvalid/i18n/cn.toml
Normal file
@ -0,0 +1,14 @@
|
||||
|
||||
|
||||
"gf.gvalid.required" = "字段不能为空"
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
3
.gitignore
vendored
3
.gitignore
vendored
@ -6,7 +6,7 @@
|
||||
.idea/
|
||||
.settings/
|
||||
.vscode/
|
||||
vender/
|
||||
vendor/
|
||||
composer.lock
|
||||
gitpush.sh
|
||||
pkg/
|
||||
@ -15,3 +15,4 @@ cbuild
|
||||
**/.DS_Store
|
||||
.vscode/
|
||||
go.sum
|
||||
.example/other/
|
||||
|
||||
@ -1,8 +1,11 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- "1.11.x"
|
||||
- "1.12.x"
|
||||
- "1.13.x"
|
||||
- "1.14.x"
|
||||
- "1.15.x"
|
||||
|
||||
branches:
|
||||
only:
|
||||
|
||||
66
DONATOR.MD
66
DONATOR.MD
@ -1,13 +1,17 @@
|
||||
# Donators
|
||||
|
||||
We currently accept donation by Alipay/WechatPay, please note your github/gitee account in your payment bill.
|
||||
We currently accept donation by [Wechat](https://goframe.org/images/donate.png) / [Alipay](https://goframe.org/images/donate.png) / [Gitee](https://gitee.com/johng/gf),
|
||||
please note your github/gitee account in your payment bill. All the donations will be used only for `GoFrame` project development and its community construction.
|
||||
|
||||
> If you cannot see the donation image, please click [here](https://goframe.org/images/donate.png).
|
||||
|
||||
<img src="https://goframe.org/images/donate.png?20200718"/>
|
||||
|
||||
> If you cannot view the donation image, please click [here](https://goframe.org/images/donate.png).
|
||||
|
||||
| Name | Channel | Amount | Comment
|
||||
|---|---|--- | ---
|
||||
|[hailaz](https://gitee.com/hailaz)|gitee|¥20.00 |
|
||||
|[ireadx](https://github.com/ireadx)|alipay|¥301.00 |
|
||||
|[ireadx](https://github.com/ireadx)|alipay|¥501.00 |
|
||||
|[mg91](https://gitee.com/mg91)|gitee|¥10.00 |
|
||||
|[pibigstar](https://github.com/pibigstar)|alipay|¥10.00 |
|
||||
|[tiangenglan](https://gitee.com/tiangenglan)|gitee|¥30.00 |
|
||||
@ -46,7 +50,7 @@ We currently accept donation by Alipay/WechatPay, please note your github/gitee
|
||||
|struggler|alipay|¥18.80|
|
||||
|*铁|wechat|¥0.01|
|
||||
|C*e|wechat|¥66.66| GF越来越好,棒👍!
|
||||
|(佚名)|wechat|¥6.66| (名字打不出来,也没备注,捐赠时间2020-02-21 14:24:34)
|
||||
|---P คิดถึง|wechat|¥6.66|
|
||||
|[王飞](https://gitee.com/wang_2018)|gitee|¥20.00| 感谢您的开源项目!
|
||||
|[Zeroing-ZY](https://gitee.com/yunjieg)|gitee|¥20.00| 感谢您的开源项目!
|
||||
|[katydid酱](https://gitee.com/katydid2005)|gitee|¥50.00| 感谢您的开源项目!框架给予了很大的帮助!谢谢大佬!
|
||||
@ -56,7 +60,7 @@ We currently accept donation by Alipay/WechatPay, please note your github/gitee
|
||||
|🚶|wechat|¥6.88| 喝杯冰阔落
|
||||
|a*l|wechat|¥10.00| gf
|
||||
|[wxkj](https://gitee.com/wxkj)|wechat|¥10.00|
|
||||
|*包|wechat|¥9.99|
|
||||
|[米司特包](https://github.com/misitebao)|wechat|¥9.99|
|
||||
|重庆宝尔威科技|wechat|¥6.66|
|
||||
|琦玉-QPT|wechat|¥6.66|
|
||||
|sailsea|wechat|¥11.00|
|
||||
@ -64,11 +68,59 @@ We currently accept donation by Alipay/WechatPay, please note your github/gitee
|
||||
|*华|wechat|¥6.66| 感谢郭强的热心
|
||||
|[Playhi](https://github.com/Playhi)|alipay|¥10.00|
|
||||
|北京京纬互动科技|alipay|¥200.00|
|
||||
|米司特包|wechat|¥99.99|
|
||||
|[米司特包](https://github.com/misitebao)|wechat|¥99.99|
|
||||
|金毛|alipay|¥100.00|
|
||||
|1*1x|wechat|¥100.00|
|
||||
|[ywanbing](https://github.com/ywanbing)|wechat|¥66.66|
|
||||
|[侯哥](http://www.macnie.com)|wechat|¥10.00|
|
||||
|如果🍋|alipay|¥100.00| 错过的奶茶^_^
|
||||
|蔡蔡|wechat|¥666.00| gf真强大,让项目省心
|
||||
|jack|wechat|¥100.00|
|
||||
|sbilly|wechat|¥100.00| 祝好!
|
||||
|[米司特包](https://github.com/misitebao)|wechat|¥166.66| 大佬加油!
|
||||
|*秦|wechat|¥20.00| 给群主献上一杯咖啡...
|
||||
|[zhuhuan12](https://gitee.com/zhuhuan12)|wechat|¥50.00|
|
||||
|faddei|qq|¥9.99|
|
||||
|*天|wechat|¥10.00|
|
||||
|*.|wechat|¥20.00| 学生一个微薄之力,冲
|
||||
|李勇|wechat|¥50.00|
|
||||
|[米司特包](https://github.com/misitebao)|wechat|¥66.66|
|
||||
|千年|wechat|¥1.08|
|
||||
|[szzxing](https://github.com/szzxing)|wechat|¥50.00|
|
||||
|伟客互联|wechat|¥66.66|
|
||||
|[sbilly](https://github.com/sbilly)|wechat|¥99.00|
|
||||
|**阳|alipay|¥100.01|
|
||||
|**亮|alipay|¥10.00|
|
||||
|[mingzaily](https://github.com/mingzaily)|alipay|¥30.00|
|
||||
|[seny0929](https://gitee.com/seny0929)|alipay|¥9.90|
|
||||
|Fly的狐狸|alipay|¥100.00|
|
||||
|六七 ·|wechat|¥88.88| gf越来越好
|
||||
|Tom|wechat|¥500.00| 一点心意 希望GF越来越好
|
||||
|Chao|wechat|¥166.00|
|
||||
|汤sir|wechat|¥10.00| 强哥,喝杯咖啡☕️
|
||||
|秋叶、|wechat|¥66.66| GF带我一起玩
|
||||
||wechat|¥20.00|
|
||||
|程凤明|wechat|¥18.80|
|
||||
|Glowworm|wechat|¥8.88| 感谢大佬
|
||||
|徒行|wechat|¥10.00| 目前项目在转用这个框架,鼓励一下大佬吧👍👍👍
|
||||
|产品设计.软件开发|wechat|¥10.00| 祝愿走向更成功!
|
||||
|[charlieccGuo](https://github.com/charlieccGuo)|wechat|¥10.00|
|
||||
|yiran|wechat|¥20.00| 赏给大佬喝茶🍵
|
||||
|长夏朔酒|wechat|¥20.00| 请大佬喝茶
|
||||
|💥聪จุ๊บ 🇨🇳|wechat|¥66.66| 请大佬喝茶
|
||||
|Even_|wechat|¥10.00| 日照市民发来贺电!
|
||||
|智慧人生|wechat|¥50.00| 智慧人生
|
||||
|一滴水|wechat|¥10.00| goframe 越来越强大
|
||||
|[wenzi1](https://github.com/wenzi1)|alipay|¥100.00|
|
||||
|[hyuant](https://github.com/hyuant)|alipay|¥66.66|
|
||||
|*庆|alipay|¥9.99| 支持一下,gf越来越好
|
||||
|**君|alipay|¥10.00| 加油
|
||||
|向回走的闹钟|wechat|¥20.00| 越来越好
|
||||
|金毛|wechat|¥100.00|
|
||||
|莫失莫忘|wechat|¥100.00|
|
||||
|**航|alipay|¥20.00|
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
<img src="https://goframe.org/images/donate.png"/>
|
||||
|
||||
112
README.MD
112
README.MD
@ -9,13 +9,13 @@
|
||||
|
||||
English | [简体中文](README_ZH.MD)
|
||||
|
||||
`GF(GoFrame)` is a modular, loose-coupled, full-featured and production-ready application development framework of golang,
|
||||
providing a series of core components and dozens of practical modules,
|
||||
such as: memcache, configure, validator, logging, array/queue/set/map containers,
|
||||
timer/timing tasks, file/memory lock, object pool, database ORM, etc,
|
||||
supporting web server integrated with router, cookie, session, middleware, logger,
|
||||
template, https, hooks, rewrites and many more features.
|
||||
`GF(GoFrame)` is a modular, powerful, high-performance and production-ready application development framework
|
||||
of golang. Providing a series of core components and dozens of practical modules, such as:
|
||||
cache, logging, containers, timer, resource, validator, database orm, etc.
|
||||
Supporting web server integrated with router, cookie, session, middleware, logger, configure,
|
||||
template, https, hooks, rewrites and many more features.
|
||||
|
||||
> If you're a newbie to `Go`, you may consider `GoFrame` easy and great as `Laravel` in `PHP`, `SpringBoot` in `Java` or `Django` in `Python`.
|
||||
|
||||
# Installation
|
||||
```
|
||||
@ -28,13 +28,81 @@ require github.com/gogf/gf latest
|
||||
|
||||
# Limitation
|
||||
```
|
||||
golang version >= 1.13
|
||||
golang version >= 1.11
|
||||
```
|
||||
|
||||
# Architecture
|
||||
<div align=center>
|
||||
<img src="https://goframe.org/images/arch.png?v=12"/>
|
||||
</div>
|
||||
|
||||
# Packages
|
||||
1. **Primary Package**
|
||||
|
||||
The `gf` repository maintains some basic and most commonly used packages, keeping it as lightweight and simple as possible.
|
||||
|
||||
1. **Community Package**
|
||||
|
||||
The community packages are contributed and maintained by community members, which are hosted in `gogf` organization. Some of the community packages are separated from the `gf` repository, which are not of common usage or are with heavy dependencies.
|
||||
|
||||
# Performance
|
||||
|
||||
Here's the most popular Golang frameworks and libraries performance testing result in `WEB Server`. Performance testing cases source codes are hosted at: https://github.com/gogf/gf-performance
|
||||
|
||||
## Environment
|
||||
|
||||
OS : Ubuntu 18.04 amd64
|
||||
CPU : AMD A8-6600K x 4
|
||||
MEM : 32GB
|
||||
GO : v1.13.4
|
||||
|
||||
## Testing Tool
|
||||
|
||||
`ab`: Apache HTTP server benchmarking tool.
|
||||
|
||||
Command:
|
||||
```
|
||||
ab -t 10 -c 100 http://127.0.0.1:3000/hello
|
||||
ab -t 10 -c 100 http://127.0.0.1:3000/query?id=10000
|
||||
ab -t 10 -c 100 http://127.0.0.1:3000/json
|
||||
```
|
||||
The concurrency starts from `100` to `10000`.
|
||||
|
||||
> Run `5` times for each case of each project and pick up the best testing result.
|
||||
|
||||
## 1. Hello World
|
||||
<table>
|
||||
<tr>
|
||||
<th>Throughputs</th>
|
||||
<th>Mean Latency</th>
|
||||
<th>P99 Latency</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/throughputs1.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/meanlatency1.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/p99latency1.jpeg"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## 2. Json Response
|
||||
<table>
|
||||
<tr>
|
||||
<th>Throughputs</th>
|
||||
<th>Mean Latency</th>
|
||||
<th>P99 Latency</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/throughputs3.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/meanlatency3.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/p99latency3.jpeg"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
# Documentation
|
||||
|
||||
* [APIDoc](https://godoc.org/github.com/gogf/gf)
|
||||
* [中文文档](https://goframe.org)
|
||||
* 中文官网: https://goframe.org
|
||||
* GoDoc API: https://godoc.org/github.com/gogf/gf
|
||||
|
||||
|
||||
# Discussion
|
||||
- QQ Group:[116707870](//shang.qq.com/wpa/qunwpa?idkey=195f91eceeb5d7fa76009b7cd5a4641f70bf4897b7f5a520635eb26ff17adfe7)
|
||||
@ -43,20 +111,29 @@ golang version >= 1.13
|
||||
|
||||
> It's recommended learning `GoFrame` through its awesome source codes and API reference.
|
||||
|
||||
# Architecture
|
||||
<div align=center>
|
||||
<img src="https://goframe.org/images/arch.png?v=11"/>
|
||||
</div>
|
||||
|
||||
|
||||
# License
|
||||
|
||||
`GF` is licensed under the [MIT License](LICENSE), 100% free and open-source, forever.
|
||||
|
||||
# Part Of Users
|
||||
|
||||
- [Tencent](https://www.tencent.com/)
|
||||
- [ZTE](https://www.zte.com.cn/china/)
|
||||
- [Ant Financial Services](https://www.antfin.com/)
|
||||
- [MedLinker](https://www.medlinker.com/)
|
||||
- [KuCoin](https://www.kucoin.io/)
|
||||
- [LeYouJia](https://www.leyoujia.com/)
|
||||
- [IGG](https://igg.com)
|
||||
- [XiMaLaYa](https://www.ximalaya.com)
|
||||
|
||||
> We list part of the users here, if your company or products are using `GoFrame`, please let us know [here](https://github.com/gogf/gf/issues/168).
|
||||
|
||||
|
||||
# Contributors
|
||||
This project exists thanks to all the people who contribute. [[Contributors](https://github.com/gogf/gf/graphs/contributors)].
|
||||
<a href="https://github.com/gogf/gf/graphs/contributors"><img src="https://opencollective.com/goframe/contributors.svg?width=890&button=false" /></a>
|
||||
|
||||
|
||||
# Donators
|
||||
|
||||
We currently accept donation by Alipay/WechatPay, please note your github/gitee account in your payment bill. If you like `GF`, why not [buy developer a cup of coffee](DONATOR.MD)?
|
||||
@ -65,6 +142,7 @@ We currently accept donation by Alipay/WechatPay, please note your github/gitee
|
||||
We appreciate any kind of sponsorship for `GF` development. If you've got some interesting, please contact WeChat `389961817` / Email `john@goframe.org`.
|
||||
|
||||
|
||||
|
||||
# Thanks
|
||||
<a href="https://www.jetbrains.com/?from=GoFrame"><img src="https://goframe.org/images/jetbrains.png" width="100" alt="JetBrains"/></a>
|
||||
|
||||
@ -75,3 +153,7 @@ We appreciate any kind of sponsorship for `GF` development. If you've got some i
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
101
README_ZH.MD
101
README_ZH.MD
@ -15,16 +15,17 @@
|
||||
并提供了Web服务开发的系列核心组件,如:Router、Cookie、Session、Middleware、服务注册、模板引擎等等,
|
||||
支持热重启、热更新、域名绑定、TLS/HTTPS、Rewrite等特性。
|
||||
|
||||
> 如果您初识`Go`语言,您可以将`GoFrame`类似于`PHP`中的`Laravel`, `Java`中的`SpringBoot`或者`Python`中的`Django`。
|
||||
|
||||
# 特点
|
||||
* 模块化、松耦合设计;
|
||||
* 模块丰富,开箱即用;
|
||||
* 简便易用,易于维护;
|
||||
* 社区活跃,大牛谦逊低调脾气好;
|
||||
* 模块丰富、开箱即用;
|
||||
* 简便易用、易于维护;
|
||||
* 高代码质量、高单元测试覆盖率;
|
||||
* 社区活跃,大牛谦逊低调脾气好;
|
||||
* 详尽的开发文档及示例;
|
||||
* 完善的本地中文化支持;
|
||||
* 更适合企业及团队使用;
|
||||
* 设计为团队及企业使用;
|
||||
|
||||
# 地址
|
||||
- **主库**:https://github.com/gogf/gf
|
||||
@ -41,33 +42,112 @@ require github.com/gogf/gf latest
|
||||
|
||||
# 限制
|
||||
```shell
|
||||
golang版本 >= 1.13
|
||||
golang版本 >= 1.11
|
||||
```
|
||||
|
||||
|
||||
|
||||
# 架构
|
||||
<div align=center>
|
||||
<img src="https://goframe.org/images/arch.png?v=11"/>
|
||||
<img src="https://goframe.org/images/arch.png?v=12"/>
|
||||
</div>
|
||||
|
||||
# 模块
|
||||
|
||||
1. **核心模块**
|
||||
|
||||
`GoFrame`提供了一些基础的、常用的模块,简单、易用和轻量级,并保持极少的外部依赖,这些模块由`gf`主仓库细致维护。
|
||||
|
||||
1. **社区模块**
|
||||
|
||||
社区模块主要由社区贡献并维护,大部分也是由`gf`主仓库的贡献者提供及维护,存放于`gogf`空间下,与`gf`主仓库处于同一级别。有的社区模块是从`gf`主仓库中剥离出来单独维护的模块,这些模块并不是特别常用,或者对外部依赖较重。
|
||||
|
||||
|
||||
# 性能
|
||||
|
||||
以下是目前最流行的`WEB Server` Golang框架/类库性能测试结果。
|
||||
性能测试用例源代码仓库: https://github.com/gogf/gf-performance
|
||||
|
||||
## 环境:
|
||||
|
||||
OS : Ubuntu 18.04 amd64
|
||||
CPU : AMD A8-6600K x 4
|
||||
MEM : 32GB
|
||||
GO : v1.13.4
|
||||
|
||||
## 工具
|
||||
|
||||
`ab`: Apache HTTP server benchmarking tool.
|
||||
|
||||
测试命令:
|
||||
```
|
||||
ab -t 10 -c 100 http://127.0.0.1:3000/hello
|
||||
ab -t 10 -c 100 http://127.0.0.1:3000/query?id=10000
|
||||
ab -t 10 -c 100 http://127.0.0.1:3000/json
|
||||
```
|
||||
并发客户端数量从 `100` 递增到 `10000`。
|
||||
|
||||
> 每个项目的每个用例均运行`5`次,取最优的结果展示。
|
||||
|
||||
## 1. Hello World
|
||||
<table>
|
||||
<tr>
|
||||
<th>Throughputs</th>
|
||||
<th>Mean Latency</th>
|
||||
<th>P99 Latency</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/throughputs1.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/meanlatency1.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/p99latency1.jpeg"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
## 2. Json Response
|
||||
<table>
|
||||
<tr>
|
||||
<th>Throughputs</th>
|
||||
<th>Mean Latency</th>
|
||||
<th>P99 Latency</th>
|
||||
</tr>
|
||||
<tr>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/throughputs3.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/meanlatency3.jpeg"></td>
|
||||
<td width="30%"><img src="http://gfcdn.johng.cn/images/performance/p99latency3.jpeg"></td>
|
||||
</tr>
|
||||
</table>
|
||||
|
||||
|
||||
# 文档
|
||||
|
||||
开发文档:[https://goframe.org](https://goframe.org)
|
||||
开发文档:https://goframe.org
|
||||
|
||||
接口文档:[https://godoc.org/github.com/gogf/gf](https://godoc.org/github.com/gogf/gf)
|
||||
接口文档:https://godoc.org/github.com/gogf/gf
|
||||
|
||||
# 帮助
|
||||
- QQ交流群:[116707870](//shang.qq.com/wpa/qunwpa?idkey=195f91eceeb5d7fa76009b7cd5a4641f70bf4897b7f5a520635eb26ff17adfe7)
|
||||
- WX交流群:微信添加`389961817`备注`GF`
|
||||
- 主库ISSUE:https://github.com/gogf/gf/issues
|
||||
|
||||
> 建议通过阅读`Gorame`的源码以及API文档深度学习`GoFrame`,了解更多的精妙设计。
|
||||
> 建议通过阅读`GoFrame`的源码以及API文档深度学习`GoFrame`,了解更多的精妙设计。
|
||||
|
||||
# 协议
|
||||
|
||||
`GF` 使用非常友好的 [MIT](LICENSE) 开源协议进行发布,永久`100%`开源免费。
|
||||
|
||||
# 用户
|
||||
|
||||
- [腾讯科技](https://www.tencent.com/)
|
||||
- [中兴科技](https://www.zte.com.cn/china/)
|
||||
- [蚂蚁金服](https://www.antfin.com/)
|
||||
- [医联科技](https://www.medlinker.com/)
|
||||
- [库币科技](https://www.kucoin.io/)
|
||||
- [乐有家](https://www.leyoujia.com/)
|
||||
- [IGG](https://igg.com)
|
||||
- [喜马拉雅](https://www.ximalaya.com)
|
||||
|
||||
> 在这里只列举了部分知名的用户,如果您的企业或者产品正在使用`GoFrame`,欢迎到 [这里](https://github.com/gogf/gf/issues/168) 留言。
|
||||
|
||||
# 贡献
|
||||
|
||||
感谢所有参与`GoFrame`开发的贡献者。 [[贡献者列表](https://github.com/gogf/gf/graphs/contributors)].
|
||||
@ -84,4 +164,5 @@ golang版本 >= 1.13
|
||||
赞助支持`GF`框架的快速研发,如果您感兴趣,请联系 微信 `389961817` / 邮件 `john@goframe.org`。
|
||||
|
||||
# 感谢
|
||||
<a href="https://www.jetbrains.com/?from=GoFrame"><img src="https://goframe.org/images/jetbrains.png" width="100" alt="JetBrains"/></a>
|
||||
<a href="https://www.jetbrains.com/?from=GoFrame"><img src="https://goframe.org/images/jetbrains.png" width="100" alt="JetBrains"/></a>
|
||||
|
||||
|
||||
210
RELEASE.2.MD
210
RELEASE.2.MD
@ -1,3 +1,213 @@
|
||||
# `v1.13.1` (2020-06-10)
|
||||
|
||||
# GoFrame
|
||||
|
||||
`GF(Go Frame)`是一款模块化、高性能、生产级的Go基础开发框架。实现了比较完善的基础设施建设以及开发工具链,提供了常用的基础开发模块,如:缓存、日志、队列、数组、集合、容器、定时器、命令行、内存锁、对象池、配置管理、资源管理、数据校验、数据编码、定时任务、数据库ORM、TCP/UDP组件、进程管理/通信等等。并提供了Web服务开发的系列核心组件,如:Router、Cookie、Session、Middleware、服务注册、模板引擎等等,支持热重启、热更新、域名绑定、TLS/HTTPS、Rewrite等特性。
|
||||
|
||||
## 特点
|
||||
|
||||
* 模块化、松耦合设计;
|
||||
* 模块丰富、开箱即用;
|
||||
* 简便易用、易于维护;
|
||||
* 高代码质量、高单元测试覆盖率;
|
||||
* 社区活跃,大牛谦逊低调脾气好;
|
||||
* 详尽的开发文档及示例;
|
||||
* 完善的本地中文化支持;
|
||||
* 设计为团队及企业使用;
|
||||
|
||||
## 发展
|
||||
|
||||
`GoFrame`开始得比较早,`2011`年始于北京一个智能物联网平台项目,那时还没有这么多物联网的现行标准,`Go`的标准库以及生态也未如此丰富。`2017`年的时候`GF`才开始发布测试版,`2018`年`1024`程序员节日的时候才发布`v1.0`正式版,为`Go`生态发展添砖加瓦。开源以来快速迭代、发展成长,广受开发者和企业的青睐,也有许多的开发者加入了贡献行列。`GF`原本是为开发团队设计的,因此她的开发效率和可维护性做得非常好,有着很高的代码质量以及丰富的单元测试和示例,并且`GF`是目前中文化文档做的最好的`Golang`开发框架。
|
||||
|
||||
# Change Log
|
||||
|
||||
1. 应多数开发者的要求,框架要求的最低`Golang`运行版本降级为了`v1.11`。
|
||||
1. 新增`GoFrame`视频教程地址:
|
||||
- bilibili:https://www.bilibili.com/video/av94410029
|
||||
- 西瓜视频: https://www.ixigua.com/pseries/6809291194665796100/
|
||||
1. 将不常用的`guuid`模块迁移到 github.com/gogf/guuid 作为社区模块维护,保持`gf`主仓库的轻量级。
|
||||
1. 新增`guid`模块,用于高效轻量级的唯一字符串生成:https://goframe.org/util/guid/index
|
||||
|
||||
|
||||
## `tool chain`
|
||||
|
||||
1. 工具链更新:https://goframe.org/toolchain/cli
|
||||
1. 新增`gf env`命令,更优雅地查看当前`Golang`环境变量信息。
|
||||
1. 新增`gf mod path`命令,用于将当前`go modules`包拷贝到`GOPATH`中,以便使用原始的`GOPATH`方式开发项目。
|
||||
1. 对现有`cli`命令进行了一些改进,提高使用体验;预编译二进制版本在部分平台下提供了`upx`压缩,使得下载的文件更小。
|
||||
|
||||
## `container`
|
||||
1. `garray`
|
||||
- https://goframe.org/container/garray/index
|
||||
- 简化数组使用方式,支持类似于`var garray.Array`的变量定义使用方式;
|
||||
- 增加`Walk`方法,用于自定义的数组元素处理方法;
|
||||
- 增加`ContainsI`方法,用于大小写忽略匹配的数组元素项存在性查找;
|
||||
- 完善单元测试,代码覆盖率`94%`;
|
||||
- 代码改进,提高性能;
|
||||
- 修复一些问题;
|
||||
1. `gchan`
|
||||
- 由于该封装包实际意义不是很大,因此从主框架中删除;
|
||||
1. `glist`
|
||||
- https://goframe.org/container/glist/index
|
||||
- 简化链表使用方式,支持类似于`var glist.List`的变量定义使用方式;
|
||||
- 完善单元测试,代码覆盖率`99%`;
|
||||
1. `gmap`
|
||||
- https://goframe.org/container/gmap/index
|
||||
- 简化`Map`使用方式,支持类似于`var gmap.Map`的变量定义使用方式;
|
||||
- 完善单元测试,代码覆盖率`81%`;
|
||||
- 代码改进,提高性能;
|
||||
1. `gset`
|
||||
- https://goframe.org/container/gset/index
|
||||
- 简化集合使用方式,支持类似于`var gset.Set`的变量定义使用方式;
|
||||
- 增加`Walk`方法,用于自定义的集合元素处理方法;
|
||||
- 完善单元测试,代码覆盖率`90%`;
|
||||
- 代码改进,提高性能;
|
||||
1. `gtree`
|
||||
- https://goframe.org/container/gtree/index
|
||||
- 简化树型使用方式,支持类似于`var gtree.BTree`的变量定义使用方式;
|
||||
- 完善单元测试,代码覆盖率`90%`;
|
||||
1. `gvar`
|
||||
- https://goframe.org/container/gvar/index
|
||||
- 完善单元测试,代码覆盖率`69%`;
|
||||
- 代码组织结构调整,提高维护性;
|
||||
- 代码改进,提高性能;
|
||||
|
||||
## `database`
|
||||
|
||||
1. `gdb`
|
||||
- 增加`Transaction(f func(tx *TX) error) (err error)`接口方法,用于通过闭包实现事务封装处理:https://goframe.org/database/gdb/transaction
|
||||
- 去掉不常用的`From`接口方法,改进`Table`及`Model`方法的参数为不定参数,并支持通过不定参数传递表别名:https://goframe.org/database/gdb/chaining/select
|
||||
- 增加`DryRun`特性,支持空跑时只执行查询不执行写入/更新/删除操作:https://goframe.org/database/gdb/senior
|
||||
- 增加`create_at`, `update_at`写入时间、更新时间字段自动填充特性:https://goframe.org/database/gdb/chaining/auto-time
|
||||
- 增加`delete_at`软删除特性:https://goframe.org/database/gdb/chaining/auto-time
|
||||
- 增加`Having`链式操作方法,用于`having`条件查询:https://goframe.org/database/gdb/chaining/select
|
||||
- `Result`结果对象增加`Chunk`方法,用于自定义的数据分批处理:https://goframe.org/database/gdb/result
|
||||
- 改进`Schema`数据库运行时切换特性;
|
||||
- 改进对`pgsql`, `mssql`, `sqlite`, `oracle`数据库字段类型的支持;
|
||||
- 进一步完善单元测试;
|
||||
- 代码组织结构调整,提高维护性;
|
||||
- 代码改进,提高性能;
|
||||
1. `gredis`
|
||||
- 增加`MaxActive`连接池参数默认配置为`100`,限制默认的连接数量;
|
||||
- 改进`Conn`连接对象的`Do`方法,支持对`map/slice/struct`类型进行自动的`json.Marshal`处理,注意获取数据时使用`DoVar`方法获取:https://goframe.org/database/gredis/usage
|
||||
- 完善单元测试,代码覆盖率`72%`;
|
||||
|
||||
## `net`
|
||||
1. `ghttp`
|
||||
- 增加`Prefix`及`Retry`客户端链式操作方法;
|
||||
- 增加客户端原始请求打印特性:https://goframe.org/net/ghttp/client/demo/dump
|
||||
- 增加`ClientMaxBodySize`的服务端配置,用于限制客户端提交的`Body`大小,默认为`8MB`;在涉及到上传的Server中需要增加该配置的大小,在配置文件中指定对应的大小即可,如`ClientMaxBodySize="100MB"`:https://goframe.org/net/ghttp/config
|
||||
- 改进`SessionId`生成的随机性,提高`Session`安全性:https://goframe.org/os/gsession/index
|
||||
- 改进`ghttp.Server`实现了标准库的`http.Handler`接口,便于与其他第三方的服务如`Prometheus`进行代码集成;
|
||||
- 其他大量的代码细节改进工作,提高性能及持久维护性;
|
||||
- 完善单元测试,代码覆盖率`61%`;
|
||||
|
||||
1. `gipv4`
|
||||
- 增加`GetIpArray`方法,用于获取当前主机的所有IPv4地址;
|
||||
- 增加`GetMacArray`及`GetMac`方法,用于获取当前主机的`MAC`地址信息;
|
||||
- 修改`IntranetIP`方法名称为`GetIntranetIp`,修改`IntranetIPArray`方法名称为`GetIntranetIpArray`;
|
||||
|
||||
## `encoding`
|
||||
1. `gjson`
|
||||
- 新增`GetMaps`获取`JSON`内部节点变量方法;
|
||||
- 改进`NewWithTag`方法对`map/struct`的处理;
|
||||
- 完善单元测试,代码覆盖率`77%`;
|
||||
1. `gyaml`
|
||||
- 升级依赖的第三方`yaml`解析包,解决了`map[interface{}]interface{}`转换问题;
|
||||
|
||||
## `error`
|
||||
1. `gerror`
|
||||
- 新增`NewfSkip`方法,用于创建`skip`指定堆栈的错误对象;
|
||||
- 放开框架所有的堆栈链路打印,展示错误时真实的链路调用详情;
|
||||
|
||||
## `os`
|
||||
1. `gcache`
|
||||
- 增加`GetVar`方法,用于获得可以便捷转换为其他数据类型的"泛型"变量;
|
||||
- 标记`Removes`方法废弃,改进`Remove`方法参数为不定参数,统一使用`Remove`方法删除单个/多个键值对;
|
||||
- 完善单元测试,代码覆盖率`96%`;
|
||||
|
||||
1. `genv`
|
||||
- 增加`GetVar`方法,用于获得可以便捷转换为其他数据类型的"泛型"变量;
|
||||
|
||||
1. `gfile`
|
||||
- 改进`CopyDir/CopyFile`复制目录/文件方法;
|
||||
- 新增`ScanDirFunc`方法,用于支持自定义处理回调的目录检索;
|
||||
- 完善单元测试,代码覆盖率`64%`;
|
||||
|
||||
1. `glog`
|
||||
- 增加支持`Context`上下文变量的日志打印特性:https://goframe.org/os/glog/context
|
||||
|
||||
1. `gres`
|
||||
- 改进打包特性,增强生成二进制文件及Go文件的压缩比,比旧版本增加`20%`压缩率,使得编译生成的二进制文件体积更小;
|
||||
- 代码结构改进,提高执行效率及可持久维护性;
|
||||
|
||||
1. `gsession`
|
||||
- 改进`SessionId`默认生成方法,采用`guid.S`方法生成;
|
||||
- 增加`SetId`及`SetIdFunc`方法,用于自定义`SessionId`及自定义的`SessionId`生成方法;
|
||||
|
||||
## `frame`
|
||||
1. `g`
|
||||
- 新增`g.Table`方法,用于快速创建数据库模型操作对象;
|
||||
|
||||
## `i18n`
|
||||
1. `gi18n`
|
||||
- 新增`GetContent`方法,用于获取指定`i18n`关键字为转译内容;
|
||||
- 改进代码细节,提高性能和持久可维护性;
|
||||
- 完善单元测试,代码覆盖率`74%`;
|
||||
|
||||
## `test`
|
||||
1. `gtest`
|
||||
- 增加`AssertNQ`断言方法,用于强类型的不相等判断;
|
||||
|
||||
## `text`
|
||||
1. `gstr`
|
||||
- 增加`SubStrRune`方法,用于支持`unicode`的字符串截取;
|
||||
- 增加`StrLimitRune`方法,用于支持`unicode`的字符串截断隐藏;
|
||||
- 增加`LenRune`方法,用于替换`RuneLen`方法,统一方法命名风格;
|
||||
- 增加`PosRune/PosIRune/PosRRune/PosRIRune`方法,用于支持`unicode`的字符串左右位置查找;
|
||||
- 增加`CompareVersionGo`方法,用于`Golang`风格的版本号大小比较;
|
||||
- 完善单元测试,代码覆盖率`75%`;
|
||||
|
||||
## `util`
|
||||
1. `gconv`
|
||||
- 改进`Convert`转换方法,支持常见`map`类型的转换;
|
||||
- 改进类型转换过程中异常错误的捕获,通过`error`返回;
|
||||
- 其他一些细节改进;
|
||||
- 完善单元测试,代码覆盖率`63%`;
|
||||
|
||||
1. `grand`
|
||||
- 增加`B`方法,用于获得随机的二进制数据;
|
||||
- 改进代码底层实现,部分接口性能提高`50%`;
|
||||
- 完善单元测试,代码覆盖率`74%`;
|
||||
|
||||
1. `guid`
|
||||
- 新增`guid`模块,用于高效轻量级的唯一字符串生成:https://goframe.org/util/guid/index
|
||||
|
||||
1. `gutil`
|
||||
- 增加`MapContains`方法,用于判断map中是否包含指定键名;
|
||||
- 增加`MapDelete`方法,用于删除map中指定的键名,可以为多个键名;
|
||||
- 增加`MapMerge`方法,用于合并两个map;
|
||||
- 增加`MapMergeCopy`方法,用于拷贝多个map;
|
||||
- 增加`MapContainsPossibleKey`方法,用于查找指定键名,忽略大小写及字符`'-'/'_'/'.'/' '`;
|
||||
|
||||
1. `gvalid`
|
||||
- 所有默认的错误提示改为了英文;
|
||||
- 错误提示的配置改为了通过`i18n`来配置实现,以便支持国际化:https://goframe.org/util/gvalid/message
|
||||
- 身份证号规则名称从`id-number`改为了`resident-id `;
|
||||
- 银行卡号规则名称从`luhn`改为了`bank-card`;
|
||||
- 完善单元测试,代码覆盖率`96%`;
|
||||
|
||||
## Bug Fix
|
||||
1. 修复`gcompress`的多文件`zip`压缩问题;
|
||||
1. 修复`ghttp.Client`获取返回的过期`Cookie`的问题;
|
||||
1. 修复`gres.File`对于`http.File`接口的实现细节;
|
||||
1. 修复`garray.Pop*`方法的边界问题;
|
||||
1. 修复`gres`中`Readdir`方法参数为`0`时报错的问题;
|
||||
1. 其他一些修复:https://github.com/gogf/gf/issues?q=is%3Aissue+label%3Abug
|
||||
|
||||
|
||||
|
||||
|
||||
# `v1.12.1` (2020-03-31)
|
||||
|
||||
大家好啊!久等啦!
|
||||
|
||||
@ -4,5 +4,5 @@
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package garray provides concurrent-safe/unsafe arrays.
|
||||
// Package garray provides most commonly used array containers which also support concurrent-safe/unsafe switch feature.
|
||||
package garray
|
||||
|
||||
@ -8,6 +8,12 @@ package garray
|
||||
|
||||
import "strings"
|
||||
|
||||
// apiInterfaces is used for type assert api for Interfaces.
|
||||
type apiInterfaces interface {
|
||||
Interfaces() []interface{}
|
||||
}
|
||||
|
||||
// defaultComparatorInt for int comparison.
|
||||
func defaultComparatorInt(a, b int) int {
|
||||
if a < b {
|
||||
return -1
|
||||
@ -18,6 +24,7 @@ func defaultComparatorInt(a, b int) int {
|
||||
return 0
|
||||
}
|
||||
|
||||
// defaultComparatorStr for string comparison.
|
||||
func defaultComparatorStr(a, b string) int {
|
||||
return strings.Compare(a, b)
|
||||
}
|
||||
|
||||
@ -8,10 +8,10 @@ package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"math"
|
||||
"sort"
|
||||
@ -22,8 +22,10 @@ import (
|
||||
)
|
||||
|
||||
// Array is a golang array with rich features.
|
||||
// It contains a concurrent-safe/unsafe switch, which should be set
|
||||
// when its initialization and cannot be changed then.
|
||||
type Array struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
array []interface{}
|
||||
}
|
||||
|
||||
@ -44,7 +46,7 @@ func NewArray(safe ...bool) *Array {
|
||||
// which is false in default.
|
||||
func NewArraySize(size int, cap int, safe ...bool) *Array {
|
||||
return &Array{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: make([]interface{}, size, cap),
|
||||
}
|
||||
}
|
||||
@ -79,7 +81,7 @@ func NewFromCopy(array []interface{}, safe ...bool) *Array {
|
||||
// which is false in default.
|
||||
func NewArrayFrom(array []interface{}, safe ...bool) *Array {
|
||||
return &Array{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: array,
|
||||
}
|
||||
}
|
||||
@ -91,7 +93,7 @@ func NewArrayFromCopy(array []interface{}, safe ...bool) *Array {
|
||||
newArray := make([]interface{}, len(array))
|
||||
copy(newArray, array)
|
||||
return &Array{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: newArray,
|
||||
}
|
||||
}
|
||||
@ -288,7 +290,7 @@ func (a *Array) PopRight() (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - 1
|
||||
if index <= 0 {
|
||||
if index < 0 {
|
||||
return nil, false
|
||||
}
|
||||
value = a.array[index]
|
||||
@ -473,18 +475,7 @@ func (a *Array) Contains(value interface{}) bool {
|
||||
// or returns -1 if not exists.
|
||||
func (a *Array) Search(value interface{}) int {
|
||||
a.mu.RLock()
|
||||
result := -1
|
||||
for index, v := range a.array {
|
||||
if v == value {
|
||||
result = index
|
||||
break
|
||||
}
|
||||
}
|
||||
a.mu.RUnlock()
|
||||
return result
|
||||
}
|
||||
|
||||
func (a *Array) doSearch(value interface{}) int {
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return -1
|
||||
}
|
||||
@ -499,12 +490,15 @@ func (a *Array) doSearch(value interface{}) int {
|
||||
}
|
||||
|
||||
// Unique uniques the array, clear repeated items.
|
||||
// Example: [1,1,2,3,2] -> [1,2,3]
|
||||
func (a *Array) Unique() *Array {
|
||||
a.mu.Lock()
|
||||
for i := 0; i < len(a.array)-1; i++ {
|
||||
for j := i + 1; j < len(a.array); j++ {
|
||||
for j := i + 1; j < len(a.array); {
|
||||
if a.array[i] == a.array[j] {
|
||||
a.array = append(a.array[:j], a.array[j+1:]...)
|
||||
} else {
|
||||
j++
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -533,23 +527,7 @@ func (a *Array) RLockFunc(f func(array []interface{})) *Array {
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more parameter types.
|
||||
func (a *Array) Merge(array interface{}) *Array {
|
||||
switch v := array.(type) {
|
||||
case *Array:
|
||||
a.Append(gconv.Interfaces(v.Slice())...)
|
||||
case *IntArray:
|
||||
a.Append(gconv.Interfaces(v.Slice())...)
|
||||
case *StrArray:
|
||||
a.Append(gconv.Interfaces(v.Slice())...)
|
||||
case *SortedArray:
|
||||
a.Append(gconv.Interfaces(v.Slice())...)
|
||||
case *SortedIntArray:
|
||||
a.Append(gconv.Interfaces(v.Slice())...)
|
||||
case *SortedStrArray:
|
||||
a.Append(gconv.Interfaces(v.Slice())...)
|
||||
default:
|
||||
a.Append(gconv.Interfaces(array)...)
|
||||
}
|
||||
return a
|
||||
return a.Append(gconv.Interfaces(array)...)
|
||||
}
|
||||
|
||||
// Fill fills an array with num entries of the value <value>,
|
||||
@ -668,6 +646,9 @@ func (a *Array) Reverse() *Array {
|
||||
func (a *Array) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return ""
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(gconv.String(v))
|
||||
@ -694,7 +675,7 @@ func (a *Array) Iterator(f func(k int, v interface{}) bool) {
|
||||
a.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the array in ascending order with given callback function <f>.
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *Array) IteratorAsc(f func(k int, v interface{}) bool) {
|
||||
a.mu.RLock()
|
||||
@ -706,7 +687,7 @@ func (a *Array) IteratorAsc(f func(k int, v interface{}) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the array in descending order with given callback function <f>.
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *Array) IteratorDesc(f func(k int, v interface{}) bool) {
|
||||
a.mu.RLock()
|
||||
@ -741,7 +722,8 @@ func (a *Array) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (a *Array) MarshalJSON() ([]byte, error) {
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (a Array) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
@ -749,8 +731,7 @@ func (a *Array) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (a *Array) UnmarshalJSON(b []byte) error {
|
||||
if a.mu == nil {
|
||||
a.mu = rwmutex.New()
|
||||
if a.array == nil {
|
||||
a.array = make([]interface{}, 0)
|
||||
}
|
||||
a.mu.Lock()
|
||||
@ -763,9 +744,6 @@ func (a *Array) UnmarshalJSON(b []byte) error {
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for array.
|
||||
func (a *Array) UnmarshalValue(value interface{}) error {
|
||||
if a.mu == nil {
|
||||
a.mu = rwmutex.New()
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
switch value.(type) {
|
||||
@ -806,6 +784,16 @@ func (a *Array) FilterEmpty() *Array {
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of array.
|
||||
func (a *Array) Walk(f func(value interface{}) interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i, v := range a.array {
|
||||
a.array[i] = f(v)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the array is empty.
|
||||
func (a *Array) IsEmpty() bool {
|
||||
return a.Len() == 0
|
||||
|
||||
@ -8,9 +8,9 @@ package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
@ -20,8 +20,10 @@ import (
|
||||
)
|
||||
|
||||
// IntArray is a golang int array with rich features.
|
||||
// It contains a concurrent-safe/unsafe switch, which should be set
|
||||
// when its initialization and cannot be changed then.
|
||||
type IntArray struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
array []int
|
||||
}
|
||||
|
||||
@ -37,7 +39,7 @@ func NewIntArray(safe ...bool) *IntArray {
|
||||
// which is false in default.
|
||||
func NewIntArraySize(size int, cap int, safe ...bool) *IntArray {
|
||||
return &IntArray{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: make([]int, size, cap),
|
||||
}
|
||||
}
|
||||
@ -62,7 +64,7 @@ func NewIntArrayRange(start, end, step int, safe ...bool) *IntArray {
|
||||
// which is false in default.
|
||||
func NewIntArrayFrom(array []int, safe ...bool) *IntArray {
|
||||
return &IntArray{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: array,
|
||||
}
|
||||
}
|
||||
@ -74,7 +76,7 @@ func NewIntArrayFromCopy(array []int, safe ...bool) *IntArray {
|
||||
newArray := make([]int, len(array))
|
||||
copy(newArray, array)
|
||||
return &IntArray{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: newArray,
|
||||
}
|
||||
}
|
||||
@ -264,7 +266,7 @@ func (a *IntArray) PopRight() (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - 1
|
||||
if index <= 0 {
|
||||
if index < 0 {
|
||||
return 0, false
|
||||
}
|
||||
value = a.array[index]
|
||||
@ -489,6 +491,10 @@ func (a *IntArray) Contains(value int) bool {
|
||||
// or returns -1 if not exists.
|
||||
func (a *IntArray) Search(value int) int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return -1
|
||||
}
|
||||
result := -1
|
||||
for index, v := range a.array {
|
||||
if v == value {
|
||||
@ -496,17 +502,19 @@ func (a *IntArray) Search(value int) int {
|
||||
break
|
||||
}
|
||||
}
|
||||
a.mu.RUnlock()
|
||||
return result
|
||||
}
|
||||
|
||||
// Unique uniques the array, clear repeated items.
|
||||
// Example: [1,1,2,3,2] -> [1,2,3]
|
||||
func (a *IntArray) Unique() *IntArray {
|
||||
a.mu.Lock()
|
||||
for i := 0; i < len(a.array)-1; i++ {
|
||||
for j := i + 1; j < len(a.array); j++ {
|
||||
for j := i + 1; j < len(a.array); {
|
||||
if a.array[i] == a.array[j] {
|
||||
a.array = append(a.array[:j], a.array[j+1:]...)
|
||||
} else {
|
||||
j++
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -535,23 +543,7 @@ func (a *IntArray) RLockFunc(f func(array []int)) *IntArray {
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more parameter types.
|
||||
func (a *IntArray) Merge(array interface{}) *IntArray {
|
||||
switch v := array.(type) {
|
||||
case *Array:
|
||||
a.Append(gconv.Ints(v.Slice())...)
|
||||
case *IntArray:
|
||||
a.Append(gconv.Ints(v.Slice())...)
|
||||
case *StrArray:
|
||||
a.Append(gconv.Ints(v.Slice())...)
|
||||
case *SortedArray:
|
||||
a.Append(gconv.Ints(v.Slice())...)
|
||||
case *SortedIntArray:
|
||||
a.Append(gconv.Ints(v.Slice())...)
|
||||
case *SortedStrArray:
|
||||
a.Append(gconv.Ints(v.Slice())...)
|
||||
default:
|
||||
a.Append(gconv.Ints(array)...)
|
||||
}
|
||||
return a
|
||||
return a.Append(gconv.Ints(array)...)
|
||||
}
|
||||
|
||||
// Fill fills an array with num entries of the value <value>,
|
||||
@ -670,6 +662,9 @@ func (a *IntArray) Reverse() *IntArray {
|
||||
func (a *IntArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return ""
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(gconv.String(v))
|
||||
@ -696,7 +691,7 @@ func (a *IntArray) Iterator(f func(k int, v int) bool) {
|
||||
a.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the array in ascending order with given callback function <f>.
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *IntArray) IteratorAsc(f func(k int, v int) bool) {
|
||||
a.mu.RLock()
|
||||
@ -708,7 +703,7 @@ func (a *IntArray) IteratorAsc(f func(k int, v int) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the array in descending order with given callback function <f>.
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *IntArray) IteratorDesc(f func(k int, v int) bool) {
|
||||
a.mu.RLock()
|
||||
@ -726,7 +721,8 @@ func (a *IntArray) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (a *IntArray) MarshalJSON() ([]byte, error) {
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (a IntArray) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
@ -734,8 +730,7 @@ func (a *IntArray) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (a *IntArray) UnmarshalJSON(b []byte) error {
|
||||
if a.mu == nil {
|
||||
a.mu = rwmutex.New()
|
||||
if a.array == nil {
|
||||
a.array = make([]int, 0)
|
||||
}
|
||||
a.mu.Lock()
|
||||
@ -748,9 +743,6 @@ func (a *IntArray) UnmarshalJSON(b []byte) error {
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for array.
|
||||
func (a *IntArray) UnmarshalValue(value interface{}) error {
|
||||
if a.mu == nil {
|
||||
a.mu = rwmutex.New()
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
switch value.(type) {
|
||||
@ -776,6 +768,16 @@ func (a *IntArray) FilterEmpty() *IntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of array.
|
||||
func (a *IntArray) Walk(f func(value int) int) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i, v := range a.array {
|
||||
a.array[i] = f(v)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the array is empty.
|
||||
func (a *IntArray) IsEmpty() bool {
|
||||
return a.Len() == 0
|
||||
|
||||
@ -8,9 +8,9 @@ package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"errors"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"math"
|
||||
"sort"
|
||||
@ -22,8 +22,10 @@ import (
|
||||
)
|
||||
|
||||
// StrArray is a golang string array with rich features.
|
||||
// It contains a concurrent-safe/unsafe switch, which should be set
|
||||
// when its initialization and cannot be changed then.
|
||||
type StrArray struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
array []string
|
||||
}
|
||||
|
||||
@ -39,7 +41,7 @@ func NewStrArray(safe ...bool) *StrArray {
|
||||
// which is false in default.
|
||||
func NewStrArraySize(size int, cap int, safe ...bool) *StrArray {
|
||||
return &StrArray{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: make([]string, size, cap),
|
||||
}
|
||||
}
|
||||
@ -49,7 +51,7 @@ func NewStrArraySize(size int, cap int, safe ...bool) *StrArray {
|
||||
// which is false in default.
|
||||
func NewStrArrayFrom(array []string, safe ...bool) *StrArray {
|
||||
return &StrArray{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: array,
|
||||
}
|
||||
}
|
||||
@ -61,7 +63,7 @@ func NewStrArrayFromCopy(array []string, safe ...bool) *StrArray {
|
||||
newArray := make([]string, len(array))
|
||||
copy(newArray, array)
|
||||
return &StrArray{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: newArray,
|
||||
}
|
||||
}
|
||||
@ -252,7 +254,7 @@ func (a *StrArray) PopRight() (value string, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - 1
|
||||
if index <= 0 {
|
||||
if index < 0 {
|
||||
return "", false
|
||||
}
|
||||
value = a.array[index]
|
||||
@ -473,13 +475,30 @@ func (a *StrArray) Contains(value string) bool {
|
||||
return a.Search(value) != -1
|
||||
}
|
||||
|
||||
// ContainsI checks whether a value exists in the array with case-insensitively.
|
||||
// Note that it internally iterates the whole array to do the comparison with case-insensitively.
|
||||
func (a *StrArray) ContainsI(value string) bool {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, v := range a.array {
|
||||
if strings.EqualFold(v, value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Search searches array by <value>, returns the index of <value>,
|
||||
// or returns -1 if not exists.
|
||||
func (a *StrArray) Search(value string) int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return -1
|
||||
}
|
||||
a.mu.RLock()
|
||||
result := -1
|
||||
for index, v := range a.array {
|
||||
if strings.Compare(v, value) == 0 {
|
||||
@ -487,17 +506,19 @@ func (a *StrArray) Search(value string) int {
|
||||
break
|
||||
}
|
||||
}
|
||||
a.mu.RUnlock()
|
||||
return result
|
||||
}
|
||||
|
||||
// Unique uniques the array, clear repeated items.
|
||||
// Example: [1,1,2,3,2] -> [1,2,3]
|
||||
func (a *StrArray) Unique() *StrArray {
|
||||
a.mu.Lock()
|
||||
for i := 0; i < len(a.array)-1; i++ {
|
||||
for j := i + 1; j < len(a.array); j++ {
|
||||
for j := i + 1; j < len(a.array); {
|
||||
if a.array[i] == a.array[j] {
|
||||
a.array = append(a.array[:j], a.array[j+1:]...)
|
||||
} else {
|
||||
j++
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -526,23 +547,7 @@ func (a *StrArray) RLockFunc(f func(array []string)) *StrArray {
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more parameter types.
|
||||
func (a *StrArray) Merge(array interface{}) *StrArray {
|
||||
switch v := array.(type) {
|
||||
case *Array:
|
||||
a.Append(gconv.Strings(v.Slice())...)
|
||||
case *IntArray:
|
||||
a.Append(gconv.Strings(v.Slice())...)
|
||||
case *StrArray:
|
||||
a.Append(gconv.Strings(v.Slice())...)
|
||||
case *SortedArray:
|
||||
a.Append(gconv.Strings(v.Slice())...)
|
||||
case *SortedIntArray:
|
||||
a.Append(gconv.Strings(v.Slice())...)
|
||||
case *SortedStrArray:
|
||||
a.Append(gconv.Strings(v.Slice())...)
|
||||
default:
|
||||
a.Append(gconv.Strings(array)...)
|
||||
}
|
||||
return a
|
||||
return a.Append(gconv.Strings(array)...)
|
||||
}
|
||||
|
||||
// Fill fills an array with num entries of the value <value>,
|
||||
@ -661,6 +666,9 @@ func (a *StrArray) Reverse() *StrArray {
|
||||
func (a *StrArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return ""
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(v)
|
||||
@ -687,7 +695,7 @@ func (a *StrArray) Iterator(f func(k int, v string) bool) {
|
||||
a.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the array in ascending order with given callback function <f>.
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *StrArray) IteratorAsc(f func(k int, v string) bool) {
|
||||
a.mu.RLock()
|
||||
@ -699,7 +707,7 @@ func (a *StrArray) IteratorAsc(f func(k int, v string) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the array in descending order with given callback function <f>.
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *StrArray) IteratorDesc(f func(k int, v string) bool) {
|
||||
a.mu.RLock()
|
||||
@ -728,7 +736,8 @@ func (a *StrArray) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (a *StrArray) MarshalJSON() ([]byte, error) {
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (a StrArray) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
@ -736,8 +745,7 @@ func (a *StrArray) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (a *StrArray) UnmarshalJSON(b []byte) error {
|
||||
if a.mu == nil {
|
||||
a.mu = rwmutex.New()
|
||||
if a.array == nil {
|
||||
a.array = make([]string, 0)
|
||||
}
|
||||
a.mu.Lock()
|
||||
@ -750,9 +758,6 @@ func (a *StrArray) UnmarshalJSON(b []byte) error {
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for array.
|
||||
func (a *StrArray) UnmarshalValue(value interface{}) error {
|
||||
if a.mu == nil {
|
||||
a.mu = rwmutex.New()
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
switch value.(type) {
|
||||
@ -778,6 +783,16 @@ func (a *StrArray) FilterEmpty() *StrArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of array.
|
||||
func (a *StrArray) Walk(f func(value string) string) *StrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i, v := range a.array {
|
||||
a.array[i] = f(v)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the array is empty.
|
||||
func (a *StrArray) IsEmpty() bool {
|
||||
return a.Len() == 0
|
||||
|
||||
@ -8,26 +8,28 @@ package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/gogf/gf/container/gtype"
|
||||
"github.com/gogf/gf/internal/rwmutex"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"github.com/gogf/gf/util/grand"
|
||||
)
|
||||
|
||||
// SortedArray is a golang sorted array with rich features.
|
||||
// It's using increasing order in default.
|
||||
// It is using increasing order in default, which can be changed by
|
||||
// setting it a custom comparator.
|
||||
// It contains a concurrent-safe/unsafe switch, which should be set
|
||||
// when its initialization and cannot be changed then.
|
||||
type SortedArray struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
array []interface{}
|
||||
unique *gtype.Bool // Whether enable unique feature(false)
|
||||
unique bool // Whether enable unique feature(false)
|
||||
comparator func(a, b interface{}) int // Comparison function(it returns -1: a < b; 0: a == b; 1: a > b)
|
||||
}
|
||||
|
||||
@ -46,8 +48,7 @@ func NewSortedArray(comparator func(a, b interface{}) int, safe ...bool) *Sorted
|
||||
// which is false in default.
|
||||
func NewSortedArraySize(cap int, comparator func(a, b interface{}) int, safe ...bool) *SortedArray {
|
||||
return &SortedArray{
|
||||
mu: rwmutex.New(safe...),
|
||||
unique: gtype.NewBool(),
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: make([]interface{}, 0, cap),
|
||||
comparator: comparator,
|
||||
}
|
||||
@ -75,7 +76,7 @@ func NewSortedArrayFrom(array []interface{}, comparator func(a, b interface{}) i
|
||||
a := NewSortedArraySize(0, comparator, safe...)
|
||||
a.array = array
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.comparator(a.array[i], a.array[j]) < 0
|
||||
return a.getComparator()(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
return a
|
||||
}
|
||||
@ -95,19 +96,19 @@ func (a *SortedArray) SetArray(array []interface{}) *SortedArray {
|
||||
defer a.mu.Unlock()
|
||||
a.array = array
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.comparator(a.array[i], a.array[j]) < 0
|
||||
return a.getComparator()(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
return a
|
||||
}
|
||||
|
||||
// SetComparator sets/changes the comparator for sorting.
|
||||
// It resorts the array as the comparator is changed.
|
||||
func (a *SortedArray) SetComparator(comparator func(a, b interface{}) int) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
a.comparator = comparator
|
||||
// Resort the array if comparator is changed.
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.comparator(a.array[i], a.array[j]) < 0
|
||||
return a.getComparator()(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
}
|
||||
|
||||
@ -118,13 +119,19 @@ func (a *SortedArray) Sort() *SortedArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.comparator(a.array[i], a.array[j]) < 0
|
||||
return a.getComparator()(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
return a
|
||||
}
|
||||
|
||||
// Add adds one or multiple values to sorted array, the array always keeps sorted.
|
||||
// It's alias of function Append, see Append.
|
||||
func (a *SortedArray) Add(values ...interface{}) *SortedArray {
|
||||
return a.Append(values...)
|
||||
}
|
||||
|
||||
// Append adds one or multiple values to sorted array, the array always keeps sorted.
|
||||
func (a *SortedArray) Append(values ...interface{}) *SortedArray {
|
||||
if len(values) == 0 {
|
||||
return a
|
||||
}
|
||||
@ -132,7 +139,7 @@ func (a *SortedArray) Add(values ...interface{}) *SortedArray {
|
||||
defer a.mu.Unlock()
|
||||
for _, value := range values {
|
||||
index, cmp := a.binSearch(value, false)
|
||||
if a.unique.Val() && cmp == 0 {
|
||||
if a.unique && cmp == 0 {
|
||||
continue
|
||||
}
|
||||
if index < 0 {
|
||||
@ -220,7 +227,7 @@ func (a *SortedArray) PopRight() (value interface{}, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - 1
|
||||
if index <= 0 {
|
||||
if index < 0 {
|
||||
return nil, false
|
||||
}
|
||||
value = a.array[index]
|
||||
@ -390,7 +397,7 @@ func (a *SortedArray) Len() int {
|
||||
// Note that, if it's in concurrent-safe usage, it returns a copy of underlying data,
|
||||
// or else a pointer to the underlying data.
|
||||
func (a *SortedArray) Slice() []interface{} {
|
||||
array := ([]interface{})(nil)
|
||||
var array []interface{}
|
||||
if a.mu.IsSafe() {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -427,20 +434,20 @@ func (a *SortedArray) Search(value interface{}) (index int) {
|
||||
// If <result> lesser than 0, it means the value at <index> is lesser than <value>.
|
||||
// If <result> greater than 0, it means the value at <index> is greater than <value>.
|
||||
func (a *SortedArray) binSearch(value interface{}, lock bool) (index int, result int) {
|
||||
if len(a.array) == 0 {
|
||||
return -1, -2
|
||||
}
|
||||
if lock {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
}
|
||||
if len(a.array) == 0 {
|
||||
return -1, -2
|
||||
}
|
||||
min := 0
|
||||
max := len(a.array) - 1
|
||||
mid := 0
|
||||
cmp := -2
|
||||
for min <= max {
|
||||
mid = int((min + max) / 2)
|
||||
cmp = a.comparator(value, a.array[mid])
|
||||
mid = (min + max) / 2
|
||||
cmp = a.getComparator()(value, a.array[mid])
|
||||
switch {
|
||||
case cmp < 0:
|
||||
max = mid - 1
|
||||
@ -457,8 +464,8 @@ func (a *SortedArray) binSearch(value interface{}, lock bool) (index int, result
|
||||
// which means it does not contain any repeated items.
|
||||
// It also do unique check, remove all repeated items.
|
||||
func (a *SortedArray) SetUnique(unique bool) *SortedArray {
|
||||
oldUnique := a.unique.Val()
|
||||
a.unique.Set(unique)
|
||||
oldUnique := a.unique
|
||||
a.unique = unique
|
||||
if unique && oldUnique != unique {
|
||||
a.Unique()
|
||||
}
|
||||
@ -477,7 +484,7 @@ func (a *SortedArray) Unique() *SortedArray {
|
||||
if i == len(a.array)-1 {
|
||||
break
|
||||
}
|
||||
if a.comparator(a.array[i], a.array[i+1]) == 0 {
|
||||
if a.getComparator()(a.array[i], a.array[i+1]) == 0 {
|
||||
a.array = append(a.array[:i+1], a.array[i+1+1:]...)
|
||||
} else {
|
||||
i++
|
||||
@ -509,6 +516,12 @@ func (a *SortedArray) Clear() *SortedArray {
|
||||
func (a *SortedArray) LockFunc(f func(array []interface{})) *SortedArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
// Keep the array always sorted.
|
||||
defer sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.getComparator()(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
@ -526,23 +539,7 @@ func (a *SortedArray) RLockFunc(f func(array []interface{})) *SortedArray {
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more parameter types.
|
||||
func (a *SortedArray) Merge(array interface{}) *SortedArray {
|
||||
switch v := array.(type) {
|
||||
case *Array:
|
||||
a.Add(gconv.Interfaces(v.Slice())...)
|
||||
case *IntArray:
|
||||
a.Add(gconv.Interfaces(v.Slice())...)
|
||||
case *StrArray:
|
||||
a.Add(gconv.Interfaces(v.Slice())...)
|
||||
case *SortedArray:
|
||||
a.Add(gconv.Interfaces(v.Slice())...)
|
||||
case *SortedIntArray:
|
||||
a.Add(gconv.Interfaces(v.Slice())...)
|
||||
case *SortedStrArray:
|
||||
a.Add(gconv.Interfaces(v.Slice())...)
|
||||
default:
|
||||
a.Add(gconv.Interfaces(array)...)
|
||||
}
|
||||
return a
|
||||
return a.Add(gconv.Interfaces(array)...)
|
||||
}
|
||||
|
||||
// Chunk splits an array into multiple arrays,
|
||||
@ -596,6 +593,9 @@ func (a *SortedArray) Rands(size int) []interface{} {
|
||||
func (a *SortedArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return ""
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(gconv.String(v))
|
||||
@ -622,7 +622,7 @@ func (a *SortedArray) Iterator(f func(k int, v interface{}) bool) {
|
||||
a.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the array in ascending order with given callback function <f>.
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *SortedArray) IteratorAsc(f func(k int, v interface{}) bool) {
|
||||
a.mu.RLock()
|
||||
@ -634,7 +634,7 @@ func (a *SortedArray) IteratorAsc(f func(k int, v interface{}) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the array in descending order with given callback function <f>.
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *SortedArray) IteratorDesc(f func(k int, v interface{}) bool) {
|
||||
a.mu.RLock()
|
||||
@ -669,19 +669,18 @@ func (a *SortedArray) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (a *SortedArray) MarshalJSON() ([]byte, error) {
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (a SortedArray) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
}
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
// Note that the comparator is set as string comparator in default.
|
||||
func (a *SortedArray) UnmarshalJSON(b []byte) error {
|
||||
if a.mu == nil {
|
||||
a.mu = rwmutex.New()
|
||||
if a.comparator == nil {
|
||||
a.array = make([]interface{}, 0)
|
||||
a.unique = gtype.NewBool()
|
||||
// Note that the comparator is string comparator in default.
|
||||
a.comparator = gutil.ComparatorString
|
||||
}
|
||||
a.mu.Lock()
|
||||
@ -698,11 +697,9 @@ func (a *SortedArray) UnmarshalJSON(b []byte) error {
|
||||
}
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for array.
|
||||
// Note that the comparator is set as string comparator in default.
|
||||
func (a *SortedArray) UnmarshalValue(value interface{}) (err error) {
|
||||
if a.mu == nil {
|
||||
a.mu = rwmutex.New()
|
||||
a.unique = gtype.NewBool()
|
||||
// Note that the comparator is string comparator in default.
|
||||
if a.comparator == nil {
|
||||
a.comparator = gutil.ComparatorString
|
||||
}
|
||||
a.mu.Lock()
|
||||
@ -764,7 +761,30 @@ func (a *SortedArray) FilterEmpty() *SortedArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of array.
|
||||
func (a *SortedArray) Walk(f func(value interface{}) interface{}) *SortedArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
// Keep the array always sorted.
|
||||
defer sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.getComparator()(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
for i, v := range a.array {
|
||||
a.array[i] = f(v)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the array is empty.
|
||||
func (a *SortedArray) IsEmpty() bool {
|
||||
return a.Len() == 0
|
||||
}
|
||||
|
||||
// getComparator returns the comparator if it's previously set,
|
||||
// or else it panics.
|
||||
func (a *SortedArray) getComparator() func(a, b interface{}) int {
|
||||
if a.comparator == nil {
|
||||
panic("comparator is missing for sorted array")
|
||||
}
|
||||
return a.comparator
|
||||
}
|
||||
|
||||
@ -8,23 +8,25 @@ package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"math"
|
||||
"sort"
|
||||
|
||||
"github.com/gogf/gf/container/gtype"
|
||||
"github.com/gogf/gf/internal/rwmutex"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"github.com/gogf/gf/util/grand"
|
||||
)
|
||||
|
||||
// SortedIntArray is a golang sorted int array with rich features.
|
||||
// It's using increasing order in default.
|
||||
// It is using increasing order in default, which can be changed by
|
||||
// setting it a custom comparator.
|
||||
// It contains a concurrent-safe/unsafe switch, which should be set
|
||||
// when its initialization and cannot be changed then.
|
||||
type SortedIntArray struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
array []int
|
||||
unique *gtype.Bool // Whether enable unique feature(false)
|
||||
unique bool // Whether enable unique feature(false)
|
||||
comparator func(a, b int) int // Comparison function(it returns -1: a < b; 0: a == b; 1: a > b)
|
||||
}
|
||||
|
||||
@ -48,9 +50,8 @@ func NewSortedIntArrayComparator(comparator func(a, b int) int, safe ...bool) *S
|
||||
// which is false in default.
|
||||
func NewSortedIntArraySize(cap int, safe ...bool) *SortedIntArray {
|
||||
return &SortedIntArray{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: make([]int, 0, cap),
|
||||
unique: gtype.NewBool(),
|
||||
comparator: defaultComparatorInt,
|
||||
}
|
||||
}
|
||||
@ -94,7 +95,7 @@ func (a *SortedIntArray) SetArray(array []int) *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
a.array = array
|
||||
sort.Ints(a.array)
|
||||
quickSortInt(a.array, a.getComparator())
|
||||
return a
|
||||
}
|
||||
|
||||
@ -104,12 +105,18 @@ func (a *SortedIntArray) SetArray(array []int) *SortedIntArray {
|
||||
func (a *SortedIntArray) Sort() *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
sort.Ints(a.array)
|
||||
quickSortInt(a.array, a.getComparator())
|
||||
return a
|
||||
}
|
||||
|
||||
// Add adds one or multiple values to sorted array, the array always keeps sorted.
|
||||
// It's alias of function Append, see Append.
|
||||
func (a *SortedIntArray) Add(values ...int) *SortedIntArray {
|
||||
return a.Append(values...)
|
||||
}
|
||||
|
||||
// Append adds one or multiple values to sorted array, the array always keeps sorted.
|
||||
func (a *SortedIntArray) Append(values ...int) *SortedIntArray {
|
||||
if len(values) == 0 {
|
||||
return a
|
||||
}
|
||||
@ -117,7 +124,7 @@ func (a *SortedIntArray) Add(values ...int) *SortedIntArray {
|
||||
defer a.mu.Unlock()
|
||||
for _, value := range values {
|
||||
index, cmp := a.binSearch(value, false)
|
||||
if a.unique.Val() && cmp == 0 {
|
||||
if a.unique && cmp == 0 {
|
||||
continue
|
||||
}
|
||||
if index < 0 {
|
||||
@ -205,7 +212,7 @@ func (a *SortedIntArray) PopRight() (value int, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - 1
|
||||
if index <= 0 {
|
||||
if index < 0 {
|
||||
return 0, false
|
||||
}
|
||||
value = a.array[index]
|
||||
@ -424,20 +431,20 @@ func (a *SortedIntArray) Search(value int) (index int) {
|
||||
// If <result> lesser than 0, it means the value at <index> is lesser than <value>.
|
||||
// If <result> greater than 0, it means the value at <index> is greater than <value>.
|
||||
func (a *SortedIntArray) binSearch(value int, lock bool) (index int, result int) {
|
||||
if len(a.array) == 0 {
|
||||
return -1, -2
|
||||
}
|
||||
if lock {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
}
|
||||
if len(a.array) == 0 {
|
||||
return -1, -2
|
||||
}
|
||||
min := 0
|
||||
max := len(a.array) - 1
|
||||
mid := 0
|
||||
cmp := -2
|
||||
for min <= max {
|
||||
mid = int((min + max) / 2)
|
||||
cmp = a.comparator(value, a.array[mid])
|
||||
mid = (min + max) / 2
|
||||
cmp = a.getComparator()(value, a.array[mid])
|
||||
switch {
|
||||
case cmp < 0:
|
||||
max = mid - 1
|
||||
@ -454,8 +461,8 @@ func (a *SortedIntArray) binSearch(value int, lock bool) (index int, result int)
|
||||
// which means it does not contain any repeated items.
|
||||
// It also do unique check, remove all repeated items.
|
||||
func (a *SortedIntArray) SetUnique(unique bool) *SortedIntArray {
|
||||
oldUnique := a.unique.Val()
|
||||
a.unique.Set(unique)
|
||||
oldUnique := a.unique
|
||||
a.unique = unique
|
||||
if unique && oldUnique != unique {
|
||||
a.Unique()
|
||||
}
|
||||
@ -474,7 +481,7 @@ func (a *SortedIntArray) Unique() *SortedIntArray {
|
||||
if i == len(a.array)-1 {
|
||||
break
|
||||
}
|
||||
if a.comparator(a.array[i], a.array[i+1]) == 0 {
|
||||
if a.getComparator()(a.array[i], a.array[i+1]) == 0 {
|
||||
a.array = append(a.array[:i+1], a.array[i+1+1:]...)
|
||||
} else {
|
||||
i++
|
||||
@ -523,23 +530,7 @@ func (a *SortedIntArray) RLockFunc(f func(array []int)) *SortedIntArray {
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more parameter types.
|
||||
func (a *SortedIntArray) Merge(array interface{}) *SortedIntArray {
|
||||
switch v := array.(type) {
|
||||
case *Array:
|
||||
a.Add(gconv.Ints(v.Slice())...)
|
||||
case *IntArray:
|
||||
a.Add(gconv.Ints(v.Slice())...)
|
||||
case *StrArray:
|
||||
a.Add(gconv.Ints(v.Slice())...)
|
||||
case *SortedArray:
|
||||
a.Add(gconv.Ints(v.Slice())...)
|
||||
case *SortedIntArray:
|
||||
a.Add(gconv.Ints(v.Slice())...)
|
||||
case *SortedStrArray:
|
||||
a.Add(gconv.Ints(v.Slice())...)
|
||||
default:
|
||||
a.Add(gconv.Ints(array)...)
|
||||
}
|
||||
return a
|
||||
return a.Add(gconv.Ints(array)...)
|
||||
}
|
||||
|
||||
// Chunk splits an array into multiple arrays,
|
||||
@ -593,6 +584,9 @@ func (a *SortedIntArray) Rands(size int) []int {
|
||||
func (a *SortedIntArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return ""
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(gconv.String(v))
|
||||
@ -619,7 +613,7 @@ func (a *SortedIntArray) Iterator(f func(k int, v int) bool) {
|
||||
a.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the array in ascending order with given callback function <f>.
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *SortedIntArray) IteratorAsc(f func(k int, v int) bool) {
|
||||
a.mu.RLock()
|
||||
@ -631,7 +625,7 @@ func (a *SortedIntArray) IteratorAsc(f func(k int, v int) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the array in descending order with given callback function <f>.
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *SortedIntArray) IteratorDesc(f func(k int, v int) bool) {
|
||||
a.mu.RLock()
|
||||
@ -649,7 +643,8 @@ func (a *SortedIntArray) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (a *SortedIntArray) MarshalJSON() ([]byte, error) {
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (a SortedIntArray) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
@ -657,10 +652,8 @@ func (a *SortedIntArray) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (a *SortedIntArray) UnmarshalJSON(b []byte) error {
|
||||
if a.mu == nil {
|
||||
a.mu = rwmutex.New()
|
||||
if a.comparator == nil {
|
||||
a.array = make([]int, 0)
|
||||
a.unique = gtype.NewBool()
|
||||
a.comparator = defaultComparatorInt
|
||||
}
|
||||
a.mu.Lock()
|
||||
@ -676,10 +669,7 @@ func (a *SortedIntArray) UnmarshalJSON(b []byte) error {
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for array.
|
||||
func (a *SortedIntArray) UnmarshalValue(value interface{}) (err error) {
|
||||
if a.mu == nil {
|
||||
a.mu = rwmutex.New()
|
||||
a.unique = gtype.NewBool()
|
||||
// Note that the comparator is string comparator in default.
|
||||
if a.comparator == nil {
|
||||
a.comparator = defaultComparatorInt
|
||||
}
|
||||
a.mu.Lock()
|
||||
@ -717,7 +707,30 @@ func (a *SortedIntArray) FilterEmpty() *SortedIntArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of array.
|
||||
func (a *SortedIntArray) Walk(f func(value int) int) *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
// Keep the array always sorted.
|
||||
defer quickSortInt(a.array, a.getComparator())
|
||||
|
||||
for i, v := range a.array {
|
||||
a.array[i] = f(v)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the array is empty.
|
||||
func (a *SortedIntArray) IsEmpty() bool {
|
||||
return a.Len() == 0
|
||||
}
|
||||
|
||||
// getComparator returns the comparator if it's previously set,
|
||||
// or else it returns a default comparator.
|
||||
func (a *SortedIntArray) getComparator() func(a, b int) int {
|
||||
if a.comparator == nil {
|
||||
return defaultComparatorInt
|
||||
}
|
||||
return a.comparator
|
||||
}
|
||||
|
||||
@ -8,23 +8,26 @@ package garray
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"github.com/gogf/gf/container/gtype"
|
||||
"github.com/gogf/gf/internal/rwmutex"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"github.com/gogf/gf/util/grand"
|
||||
)
|
||||
|
||||
// SortedStrArray is a golang sorted string array with rich features.
|
||||
// It's using increasing order in default.
|
||||
// It is using increasing order in default, which can be changed by
|
||||
// setting it a custom comparator.
|
||||
// It contains a concurrent-safe/unsafe switch, which should be set
|
||||
// when its initialization and cannot be changed then.
|
||||
type SortedStrArray struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
array []string
|
||||
unique *gtype.Bool // Whether enable unique feature(false)
|
||||
unique bool // Whether enable unique feature(false)
|
||||
comparator func(a, b string) int // Comparison function(it returns -1: a < b; 0: a == b; 1: a > b)
|
||||
}
|
||||
|
||||
@ -48,9 +51,8 @@ func NewSortedStrArrayComparator(comparator func(a, b string) int, safe ...bool)
|
||||
// which is false in default.
|
||||
func NewSortedStrArraySize(cap int, safe ...bool) *SortedStrArray {
|
||||
return &SortedStrArray{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
array: make([]string, 0, cap),
|
||||
unique: gtype.NewBool(),
|
||||
comparator: defaultComparatorStr,
|
||||
}
|
||||
}
|
||||
@ -61,7 +63,7 @@ func NewSortedStrArraySize(cap int, safe ...bool) *SortedStrArray {
|
||||
func NewSortedStrArrayFrom(array []string, safe ...bool) *SortedStrArray {
|
||||
a := NewSortedStrArraySize(0, safe...)
|
||||
a.array = array
|
||||
quickSortStr(a.array, a.comparator)
|
||||
quickSortStr(a.array, a.getComparator())
|
||||
return a
|
||||
}
|
||||
|
||||
@ -79,7 +81,7 @@ func (a *SortedStrArray) SetArray(array []string) *SortedStrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
a.array = array
|
||||
quickSortStr(a.array, a.comparator)
|
||||
quickSortStr(a.array, a.getComparator())
|
||||
return a
|
||||
}
|
||||
|
||||
@ -89,12 +91,18 @@ func (a *SortedStrArray) SetArray(array []string) *SortedStrArray {
|
||||
func (a *SortedStrArray) Sort() *SortedStrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
quickSortStr(a.array, a.comparator)
|
||||
quickSortStr(a.array, a.getComparator())
|
||||
return a
|
||||
}
|
||||
|
||||
// Add adds one or multiple values to sorted array, the array always keeps sorted.
|
||||
// It's alias of function Append, see Append.
|
||||
func (a *SortedStrArray) Add(values ...string) *SortedStrArray {
|
||||
return a.Append(values...)
|
||||
}
|
||||
|
||||
// Append adds one or multiple values to sorted array, the array always keeps sorted.
|
||||
func (a *SortedStrArray) Append(values ...string) *SortedStrArray {
|
||||
if len(values) == 0 {
|
||||
return a
|
||||
}
|
||||
@ -102,7 +110,7 @@ func (a *SortedStrArray) Add(values ...string) *SortedStrArray {
|
||||
defer a.mu.Unlock()
|
||||
for _, value := range values {
|
||||
index, cmp := a.binSearch(value, false)
|
||||
if a.unique.Val() && cmp == 0 {
|
||||
if a.unique && cmp == 0 {
|
||||
continue
|
||||
}
|
||||
if index < 0 {
|
||||
@ -190,7 +198,7 @@ func (a *SortedStrArray) PopRight() (value string, found bool) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - 1
|
||||
if index <= 0 {
|
||||
if index < 0 {
|
||||
return "", false
|
||||
}
|
||||
value = a.array[index]
|
||||
@ -394,6 +402,22 @@ func (a *SortedStrArray) Contains(value string) bool {
|
||||
return a.Search(value) != -1
|
||||
}
|
||||
|
||||
// ContainsI checks whether a value exists in the array with case-insensitively.
|
||||
// Note that it internally iterates the whole array to do the comparison with case-insensitively.
|
||||
func (a *SortedStrArray) ContainsI(value string) bool {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return false
|
||||
}
|
||||
for _, v := range a.array {
|
||||
if strings.EqualFold(v, value) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Search searches array by <value>, returns the index of <value>,
|
||||
// or returns -1 if not exists.
|
||||
func (a *SortedStrArray) Search(value string) (index int) {
|
||||
@ -409,20 +433,20 @@ func (a *SortedStrArray) Search(value string) (index int) {
|
||||
// If <result> lesser than 0, it means the value at <index> is lesser than <value>.
|
||||
// If <result> greater than 0, it means the value at <index> is greater than <value>.
|
||||
func (a *SortedStrArray) binSearch(value string, lock bool) (index int, result int) {
|
||||
if len(a.array) == 0 {
|
||||
return -1, -2
|
||||
}
|
||||
if lock {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
}
|
||||
if len(a.array) == 0 {
|
||||
return -1, -2
|
||||
}
|
||||
min := 0
|
||||
max := len(a.array) - 1
|
||||
mid := 0
|
||||
cmp := -2
|
||||
for min <= max {
|
||||
mid = int((min + max) / 2)
|
||||
cmp = a.comparator(value, a.array[mid])
|
||||
mid = (min + max) / 2
|
||||
cmp = a.getComparator()(value, a.array[mid])
|
||||
switch {
|
||||
case cmp < 0:
|
||||
max = mid - 1
|
||||
@ -439,8 +463,8 @@ func (a *SortedStrArray) binSearch(value string, lock bool) (index int, result i
|
||||
// which means it does not contain any repeated items.
|
||||
// It also do unique check, remove all repeated items.
|
||||
func (a *SortedStrArray) SetUnique(unique bool) *SortedStrArray {
|
||||
oldUnique := a.unique.Val()
|
||||
a.unique.Set(unique)
|
||||
oldUnique := a.unique
|
||||
a.unique = unique
|
||||
if unique && oldUnique != unique {
|
||||
a.Unique()
|
||||
}
|
||||
@ -459,7 +483,7 @@ func (a *SortedStrArray) Unique() *SortedStrArray {
|
||||
if i == len(a.array)-1 {
|
||||
break
|
||||
}
|
||||
if a.comparator(a.array[i], a.array[i+1]) == 0 {
|
||||
if a.getComparator()(a.array[i], a.array[i+1]) == 0 {
|
||||
a.array = append(a.array[:i+1], a.array[i+1+1:]...)
|
||||
} else {
|
||||
i++
|
||||
@ -508,23 +532,7 @@ func (a *SortedStrArray) RLockFunc(f func(array []string)) *SortedStrArray {
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more parameter types.
|
||||
func (a *SortedStrArray) Merge(array interface{}) *SortedStrArray {
|
||||
switch v := array.(type) {
|
||||
case *Array:
|
||||
a.Add(gconv.Strings(v.Slice())...)
|
||||
case *IntArray:
|
||||
a.Add(gconv.Strings(v.Slice())...)
|
||||
case *StrArray:
|
||||
a.Add(gconv.Strings(v.Slice())...)
|
||||
case *SortedArray:
|
||||
a.Add(gconv.Strings(v.Slice())...)
|
||||
case *SortedIntArray:
|
||||
a.Add(gconv.Strings(v.Slice())...)
|
||||
case *SortedStrArray:
|
||||
a.Add(gconv.Strings(v.Slice())...)
|
||||
default:
|
||||
a.Add(gconv.Strings(array)...)
|
||||
}
|
||||
return a
|
||||
return a.Add(gconv.Strings(array)...)
|
||||
}
|
||||
|
||||
// Chunk splits an array into multiple arrays,
|
||||
@ -578,6 +586,9 @@ func (a *SortedStrArray) Rands(size int) []string {
|
||||
func (a *SortedStrArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if len(a.array) == 0 {
|
||||
return ""
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(v)
|
||||
@ -604,7 +615,7 @@ func (a *SortedStrArray) Iterator(f func(k int, v string) bool) {
|
||||
a.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the array in ascending order with given callback function <f>.
|
||||
// IteratorAsc iterates the array readonly in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *SortedStrArray) IteratorAsc(f func(k int, v string) bool) {
|
||||
a.mu.RLock()
|
||||
@ -616,7 +627,7 @@ func (a *SortedStrArray) IteratorAsc(f func(k int, v string) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the array in descending order with given callback function <f>.
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (a *SortedStrArray) IteratorDesc(f func(k int, v string) bool) {
|
||||
a.mu.RLock()
|
||||
@ -645,7 +656,8 @@ func (a *SortedStrArray) String() string {
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (a *SortedStrArray) MarshalJSON() ([]byte, error) {
|
||||
// Note that do not use pointer as its receiver here.
|
||||
func (a SortedStrArray) MarshalJSON() ([]byte, error) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return json.Marshal(a.array)
|
||||
@ -653,10 +665,8 @@ func (a *SortedStrArray) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (a *SortedStrArray) UnmarshalJSON(b []byte) error {
|
||||
if a.mu == nil {
|
||||
a.mu = rwmutex.New()
|
||||
if a.comparator == nil {
|
||||
a.array = make([]string, 0)
|
||||
a.unique = gtype.NewBool()
|
||||
a.comparator = defaultComparatorStr
|
||||
}
|
||||
a.mu.Lock()
|
||||
@ -672,10 +682,7 @@ func (a *SortedStrArray) UnmarshalJSON(b []byte) error {
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for array.
|
||||
func (a *SortedStrArray) UnmarshalValue(value interface{}) (err error) {
|
||||
if a.mu == nil {
|
||||
a.mu = rwmutex.New()
|
||||
a.unique = gtype.NewBool()
|
||||
// Note that the comparator is string comparator in default.
|
||||
if a.comparator == nil {
|
||||
a.comparator = defaultComparatorStr
|
||||
}
|
||||
a.mu.Lock()
|
||||
@ -713,7 +720,30 @@ func (a *SortedStrArray) FilterEmpty() *SortedStrArray {
|
||||
return a
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of array.
|
||||
func (a *SortedStrArray) Walk(f func(value string) string) *SortedStrArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
|
||||
// Keep the array always sorted.
|
||||
defer quickSortStr(a.array, a.getComparator())
|
||||
|
||||
for i, v := range a.array {
|
||||
a.array[i] = f(v)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// IsEmpty checks whether the array is empty.
|
||||
func (a *SortedStrArray) IsEmpty() bool {
|
||||
return a.Len() == 0
|
||||
}
|
||||
|
||||
// getComparator returns the comparator if it's previously set,
|
||||
// or else it returns a default comparator.
|
||||
func (a *SortedStrArray) getComparator() func(a, b string) int {
|
||||
if a.comparator == nil {
|
||||
return defaultComparatorStr
|
||||
}
|
||||
return a.comparator
|
||||
}
|
||||
|
||||
281
container/garray/garray_z_example_any_test.go
Normal file
281
container/garray/garray_z_example_any_test.go
Normal file
@ -0,0 +1,281 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
|
||||
"github.com/gogf/gf/container/garray"
|
||||
)
|
||||
|
||||
func ExampleNew() {
|
||||
// A normal array.
|
||||
a := garray.New()
|
||||
|
||||
// Adding items.
|
||||
for i := 0; i < 10; i++ {
|
||||
a.Append(i)
|
||||
}
|
||||
|
||||
// Print the array length.
|
||||
fmt.Println(a.Len())
|
||||
|
||||
// Print the array items.
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// Retrieve item by index.
|
||||
fmt.Println(a.Get(6))
|
||||
|
||||
// Check item existence.
|
||||
fmt.Println(a.Contains(6))
|
||||
fmt.Println(a.Contains(100))
|
||||
|
||||
// Insert item before specified index.
|
||||
a.InsertAfter(9, 11)
|
||||
// Insert item after specified index.
|
||||
a.InsertBefore(10, 10)
|
||||
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// Modify item by index.
|
||||
a.Set(0, 100)
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// Search item and return its index.
|
||||
fmt.Println(a.Search(5))
|
||||
|
||||
// Remove item by index.
|
||||
a.Remove(0)
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// Empty the array, removes all items of it.
|
||||
fmt.Println(a.Slice())
|
||||
a.Clear()
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// Output:
|
||||
// 10
|
||||
// [0 1 2 3 4 5 6 7 8 9]
|
||||
// 6 true
|
||||
// true
|
||||
// false
|
||||
// [0 1 2 3 4 5 6 7 8 9 10 11]
|
||||
// [100 1 2 3 4 5 6 7 8 9 10 11]
|
||||
// 5
|
||||
// [1 2 3 4 5 6 7 8 9 10 11]
|
||||
// [1 2 3 4 5 6 7 8 9 10 11]
|
||||
// []
|
||||
}
|
||||
|
||||
func ExampleArray_Iterator() {
|
||||
array := garray.NewArrayFrom(g.Slice{"a", "b", "c"})
|
||||
// Iterator is alias of IteratorAsc, which iterates the array readonly in ascending order
|
||||
// with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
array.Iterator(func(k int, v interface{}) bool {
|
||||
fmt.Println(k, v)
|
||||
return true
|
||||
})
|
||||
// IteratorDesc iterates the array readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
array.IteratorDesc(func(k int, v interface{}) bool {
|
||||
fmt.Println(k, v)
|
||||
return true
|
||||
})
|
||||
|
||||
// Output:
|
||||
// 0 a
|
||||
// 1 b
|
||||
// 2 c
|
||||
// 2 c
|
||||
// 1 b
|
||||
// 0 a
|
||||
}
|
||||
|
||||
func ExampleArray_Reverse() {
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
// Reverse makes array with elements in reverse order.
|
||||
fmt.Println(array.Reverse().Slice())
|
||||
|
||||
// Output:
|
||||
// [9 8 7 6 5 4 3 2 1]
|
||||
}
|
||||
|
||||
func ExampleArray_Shuffle() {
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
// Shuffle randomly shuffles the array.
|
||||
fmt.Println(array.Shuffle().Slice())
|
||||
}
|
||||
|
||||
func ExampleArray_Rands() {
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
// Randomly retrieve and return 2 items from the array.
|
||||
// It does not delete the items from array.
|
||||
fmt.Println(array.Rands(2))
|
||||
|
||||
// Randomly pick and return one item from the array.
|
||||
// It deletes the picked up item from array.
|
||||
fmt.Println(array.PopRand())
|
||||
}
|
||||
|
||||
func ExampleArray_PopRand() {
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
// Randomly retrieve and return 2 items from the array.
|
||||
// It does not delete the items from array.
|
||||
fmt.Println(array.Rands(2))
|
||||
|
||||
// Randomly pick and return one item from the array.
|
||||
// It deletes the picked up item from array.
|
||||
fmt.Println(array.PopRand())
|
||||
}
|
||||
|
||||
func ExampleArray_Join() {
|
||||
array := garray.NewFrom(g.Slice{"a", "b", "c", "d"})
|
||||
fmt.Println(array.Join(","))
|
||||
|
||||
// Output:
|
||||
// a,b,c,d
|
||||
}
|
||||
|
||||
func ExampleArray_Chunk() {
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
// Chunk splits an array into multiple arrays,
|
||||
// the size of each array is determined by <size>.
|
||||
// The last chunk may contain less than size elements.
|
||||
fmt.Println(array.Chunk(2))
|
||||
|
||||
// Output:
|
||||
// [[1 2] [3 4] [5 6] [7 8] [9]]
|
||||
}
|
||||
|
||||
func ExampleArray_PopLeft() {
|
||||
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
// Any Pop* functions pick, delete and return the item from array.
|
||||
|
||||
fmt.Println(array.PopLeft())
|
||||
fmt.Println(array.PopLefts(2))
|
||||
fmt.Println(array.PopRight())
|
||||
fmt.Println(array.PopRights(2))
|
||||
|
||||
// Output:
|
||||
// 1 true
|
||||
// [2 3]
|
||||
// 9 true
|
||||
// [7 8]
|
||||
}
|
||||
|
||||
func ExampleArray_PopLefts() {
|
||||
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
// Any Pop* functions pick, delete and return the item from array.
|
||||
|
||||
fmt.Println(array.PopLeft())
|
||||
fmt.Println(array.PopLefts(2))
|
||||
fmt.Println(array.PopRight())
|
||||
fmt.Println(array.PopRights(2))
|
||||
|
||||
// Output:
|
||||
// 1 true
|
||||
// [2 3]
|
||||
// 9 true
|
||||
// [7 8]
|
||||
}
|
||||
|
||||
func ExampleArray_PopRight() {
|
||||
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
// Any Pop* functions pick, delete and return the item from array.
|
||||
|
||||
fmt.Println(array.PopLeft())
|
||||
fmt.Println(array.PopLefts(2))
|
||||
fmt.Println(array.PopRight())
|
||||
fmt.Println(array.PopRights(2))
|
||||
|
||||
// Output:
|
||||
// 1 true
|
||||
// [2 3]
|
||||
// 9 true
|
||||
// [7 8]
|
||||
}
|
||||
|
||||
func ExampleArray_PopRights() {
|
||||
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
// Any Pop* functions pick, delete and return the item from array.
|
||||
|
||||
fmt.Println(array.PopLeft())
|
||||
fmt.Println(array.PopLefts(2))
|
||||
fmt.Println(array.PopRight())
|
||||
fmt.Println(array.PopRights(2))
|
||||
|
||||
// Output:
|
||||
// 1 true
|
||||
// [2 3]
|
||||
// 9 true
|
||||
// [7 8]
|
||||
}
|
||||
|
||||
func ExampleArray_Contains() {
|
||||
var array garray.StrArray
|
||||
array.Append("a")
|
||||
fmt.Println(array.Contains("a"))
|
||||
fmt.Println(array.Contains("A"))
|
||||
fmt.Println(array.ContainsI("A"))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleArray_Merge() {
|
||||
array1 := garray.NewFrom(g.Slice{1, 2})
|
||||
array2 := garray.NewFrom(g.Slice{3, 4})
|
||||
slice1 := g.Slice{5, 6}
|
||||
slice2 := []int{7, 8}
|
||||
slice3 := []string{"9", "0"}
|
||||
fmt.Println(array1.Slice())
|
||||
array1.Merge(array1)
|
||||
array1.Merge(array2)
|
||||
array1.Merge(slice1)
|
||||
array1.Merge(slice2)
|
||||
array1.Merge(slice3)
|
||||
fmt.Println(array1.Slice())
|
||||
|
||||
// Output:
|
||||
// [1 2]
|
||||
// [1 2 1 2 3 4 5 6 7 8 9 0]
|
||||
}
|
||||
|
||||
func ExampleArray_FilterEmpty() {
|
||||
array1 := garray.NewFrom(g.Slice{0, 1, 2, nil, "", g.Slice{}, "john"})
|
||||
array2 := garray.NewFrom(g.Slice{0, 1, 2, nil, "", g.Slice{}, "john"})
|
||||
fmt.Printf("%#v\n", array1.FilterNil().Slice())
|
||||
fmt.Printf("%#v\n", array2.FilterEmpty().Slice())
|
||||
|
||||
// Output:
|
||||
// []interface {}{0, 1, 2, "", []interface {}{}, "john"}
|
||||
// []interface {}{1, 2, "john"}
|
||||
}
|
||||
|
||||
func ExampleArray_FilterNil() {
|
||||
array1 := garray.NewFrom(g.Slice{0, 1, 2, nil, "", g.Slice{}, "john"})
|
||||
array2 := garray.NewFrom(g.Slice{0, 1, 2, nil, "", g.Slice{}, "john"})
|
||||
fmt.Printf("%#v\n", array1.FilterNil().Slice())
|
||||
fmt.Printf("%#v\n", array2.FilterEmpty().Slice())
|
||||
|
||||
// Output:
|
||||
// []interface {}{0, 1, 2, "", []interface {}{}, "john"}
|
||||
// []interface {}{1, 2, "john"}
|
||||
}
|
||||
28
container/garray/garray_z_example_str_test.go
Normal file
28
container/garray/garray_z_example_str_test.go
Normal file
@ -0,0 +1,28 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func ExampleStrArray_Walk() {
|
||||
var array garray.StrArray
|
||||
tables := g.SliceStr{"user", "user_detail"}
|
||||
prefix := "gf_"
|
||||
array.Append(tables...)
|
||||
// Add prefix for given table names.
|
||||
array.Walk(func(value string) string {
|
||||
return prefix + value
|
||||
})
|
||||
fmt.Println(array.Slice())
|
||||
|
||||
// Output:
|
||||
// [gf_user gf_user_detail]
|
||||
}
|
||||
@ -1,132 +0,0 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
|
||||
"github.com/gogf/gf/container/garray"
|
||||
)
|
||||
|
||||
func Example_basic() {
|
||||
// A normal array.
|
||||
a := garray.New()
|
||||
|
||||
// Adding items.
|
||||
for i := 0; i < 10; i++ {
|
||||
a.Append(i)
|
||||
}
|
||||
|
||||
// Print the array length.
|
||||
fmt.Println(a.Len())
|
||||
|
||||
// Print the array items.
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// Retrieve item by index.
|
||||
fmt.Println(a.Get(6))
|
||||
|
||||
// Check item existence.
|
||||
fmt.Println(a.Contains(6))
|
||||
fmt.Println(a.Contains(100))
|
||||
|
||||
// Insert item before specified index.
|
||||
a.InsertAfter(9, 11)
|
||||
// Insert item after specified index.
|
||||
a.InsertBefore(10, 10)
|
||||
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// Modify item by index.
|
||||
a.Set(0, 100)
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// Search item and return its index.
|
||||
fmt.Println(a.Search(5))
|
||||
|
||||
// Remove item by index.
|
||||
a.Remove(0)
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// Empty the array, removes all items of it.
|
||||
fmt.Println(a.Slice())
|
||||
a.Clear()
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// Output:
|
||||
// 10
|
||||
// [0 1 2 3 4 5 6 7 8 9]
|
||||
// 6 true
|
||||
// true
|
||||
// false
|
||||
// [0 1 2 3 4 5 6 7 8 9 10 11]
|
||||
// [100 1 2 3 4 5 6 7 8 9 10 11]
|
||||
// 5
|
||||
// [1 2 3 4 5 6 7 8 9 10 11]
|
||||
// [1 2 3 4 5 6 7 8 9 10 11]
|
||||
// []
|
||||
}
|
||||
|
||||
func Example_rand() {
|
||||
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
// Randomly retrieve and return 2 items from the array.
|
||||
// It does not delete the items from array.
|
||||
fmt.Println(array.Rands(2))
|
||||
|
||||
// Randomly pick and return one item from the array.
|
||||
// It deletes the picked up item from array.
|
||||
fmt.Println(array.PopRand())
|
||||
}
|
||||
|
||||
func Example_popItem() {
|
||||
array := garray.NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
// Any Pop* functions pick, delete and return the item from array.
|
||||
|
||||
fmt.Println(array.PopLeft())
|
||||
fmt.Println(array.PopLefts(2))
|
||||
fmt.Println(array.PopRight())
|
||||
fmt.Println(array.PopRights(2))
|
||||
|
||||
// Output:
|
||||
// 1 true
|
||||
// [2 3]
|
||||
// 9 true
|
||||
// [7 8]
|
||||
}
|
||||
|
||||
func Example_mergeArray() {
|
||||
array1 := garray.NewFrom([]interface{}{1, 2})
|
||||
array2 := garray.NewFrom([]interface{}{3, 4})
|
||||
slice1 := []interface{}{5, 6}
|
||||
slice2 := []int{7, 8}
|
||||
slice3 := []string{"9", "0"}
|
||||
fmt.Println(array1.Slice())
|
||||
array1.Merge(array1)
|
||||
array1.Merge(array2)
|
||||
array1.Merge(slice1)
|
||||
array1.Merge(slice2)
|
||||
array1.Merge(slice3)
|
||||
fmt.Println(array1.Slice())
|
||||
|
||||
// Output:
|
||||
// [1 2]
|
||||
// [1 2 1 2 3 4 5 6 7 8 9 0]
|
||||
}
|
||||
|
||||
func Example_filter() {
|
||||
array1 := garray.NewFrom(g.Slice{0, 1, 2, nil, "", g.Slice{}, "john"})
|
||||
array2 := garray.NewFrom(g.Slice{0, 1, 2, nil, "", g.Slice{}, "john"})
|
||||
fmt.Printf("%#v\n", array1.FilterNil().Slice())
|
||||
fmt.Printf("%#v\n", array2.FilterEmpty().Slice())
|
||||
|
||||
// Output:
|
||||
// []interface {}{0, 1, 2, "", []interface {}{}, "john"}
|
||||
// []interface {}{1, 2, "john"}
|
||||
}
|
||||
@ -9,6 +9,7 @@
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
"strings"
|
||||
"testing"
|
||||
|
||||
@ -17,6 +18,55 @@ import (
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
func Test_Array_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var array garray.Array
|
||||
expect := []int{2, 3, 1}
|
||||
array.Append(2, 3, 1)
|
||||
t.Assert(array.Slice(), expect)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var array garray.IntArray
|
||||
expect := []int{2, 3, 1}
|
||||
array.Append(2, 3, 1)
|
||||
t.Assert(array.Slice(), expect)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var array garray.StrArray
|
||||
expect := []string{"b", "a"}
|
||||
array.Append("b", "a")
|
||||
t.Assert(array.Slice(), expect)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var array garray.SortedArray
|
||||
array.SetComparator(gutil.ComparatorInt)
|
||||
expect := []int{1, 2, 3}
|
||||
array.Add(2, 3, 1)
|
||||
t.Assert(array.Slice(), expect)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var array garray.SortedIntArray
|
||||
expect := []int{1, 2, 3}
|
||||
array.Add(2, 3, 1)
|
||||
t.Assert(array.Slice(), expect)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var array garray.SortedStrArray
|
||||
expect := []string{"a", "b", "c"}
|
||||
array.Add("c", "a", "b")
|
||||
t.Assert(array.Slice(), expect)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_SortedIntArray_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var array garray.SortedIntArray
|
||||
expect := []int{1, 2, 3}
|
||||
array.Add(2, 3, 1)
|
||||
t.Assert(array.Slice(), expect)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntArray_Unique(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []int{1, 2, 3, 4, 5, 6}
|
||||
|
||||
@ -9,8 +9,8 @@
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -95,9 +95,9 @@ func TestArray_Sort(t *testing.T) {
|
||||
|
||||
func TestArray_Unique(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []interface{}{1, 1, 2, 3}
|
||||
expect := []interface{}{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
|
||||
array := garray.NewArrayFrom(expect)
|
||||
t.Assert(array.Unique().Slice(), []interface{}{1, 2, 3})
|
||||
t.Assert(array.Unique().Slice(), []interface{}{1, 2, 3, 4, 5})
|
||||
})
|
||||
}
|
||||
|
||||
@ -137,6 +137,65 @@ func TestArray_PopRands(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_PopLeft(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3})
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_PopRight(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3})
|
||||
|
||||
v, ok := array.PopRight()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_PopLefts(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3})
|
||||
t.Assert(array.PopLefts(2), g.Slice{1, 2})
|
||||
t.Assert(array.Len(), 1)
|
||||
t.Assert(array.PopLefts(2), g.Slice{3})
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_PopRights(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewFrom(g.Slice{1, 2, 3})
|
||||
t.Assert(array.PopRights(2), g.Slice{2, 3})
|
||||
t.Assert(array.Len(), 1)
|
||||
t.Assert(array.PopLefts(2), g.Slice{1})
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_PopLeftsAndPopRights(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.New()
|
||||
@ -501,6 +560,7 @@ func TestArray_RLockFunc(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestArray_Json(t *testing.T) {
|
||||
// pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []interface{}{"a", "b", "d", "c"}
|
||||
a1 := garray.NewArrayFrom(s1)
|
||||
@ -519,7 +579,26 @@ func TestArray_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// value.
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []interface{}{"a", "b", "d", "c"}
|
||||
a1 := *garray.NewArrayFrom(s1)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.New()
|
||||
err2 = json.Unmarshal(b2, &a2)
|
||||
t.Assert(err2, nil)
|
||||
t.Assert(a2.Slice(), s1)
|
||||
|
||||
var a3 garray.Array
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
@ -532,6 +611,25 @@ func TestArray_Json(t *testing.T) {
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, data["Scores"])
|
||||
})
|
||||
// value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores garray.Array
|
||||
}
|
||||
data := g.Map{
|
||||
"Name": "john",
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
t.Assert(err, nil)
|
||||
@ -650,3 +748,12 @@ func TestArray_FilterEmpty(t *testing.T) {
|
||||
t.Assert(array.FilterEmpty(), g.Slice{1, 2, 3, 4})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Walk(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewArrayFrom(g.Slice{"1", "2"})
|
||||
t.Assert(array.Walk(func(value interface{}) interface{} {
|
||||
return "key-" + gconv.String(value)
|
||||
}), g.Slice{"key-1", "key-2"})
|
||||
})
|
||||
}
|
||||
|
||||
@ -9,8 +9,8 @@
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -87,9 +87,9 @@ func TestIntArray_Sort(t *testing.T) {
|
||||
|
||||
func TestIntArray_Unique(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []int{1, 1, 2, 3}
|
||||
expect := []int{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
|
||||
array := garray.NewIntArrayFrom(expect)
|
||||
t.Assert(array.Unique().Slice(), []int{1, 2, 3})
|
||||
t.Assert(array.Unique().Slice(), []int{1, 2, 3, 4, 5})
|
||||
})
|
||||
}
|
||||
|
||||
@ -230,6 +230,65 @@ func TestIntArray_Fill(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_PopLeft(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewIntArrayFrom(g.SliceInt{1, 2, 3})
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_PopRight(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewIntArrayFrom(g.SliceInt{1, 2, 3})
|
||||
|
||||
v, ok := array.PopRight()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_PopLefts(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewIntArrayFrom(g.SliceInt{1, 2, 3})
|
||||
t.Assert(array.PopLefts(2), g.Slice{1, 2})
|
||||
t.Assert(array.Len(), 1)
|
||||
t.Assert(array.PopLefts(2), g.Slice{3})
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_PopRights(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewIntArrayFrom(g.SliceInt{1, 2, 3})
|
||||
t.Assert(array.PopRights(2), g.Slice{2, 3})
|
||||
t.Assert(array.Len(), 1)
|
||||
t.Assert(array.PopLefts(2), g.Slice{1})
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Chunk(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5}
|
||||
@ -546,6 +605,7 @@ func TestIntArray_RLockFunc(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestIntArray_Json(t *testing.T) {
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []int{1, 4, 3, 2}
|
||||
a1 := garray.NewIntArrayFrom(s1)
|
||||
@ -563,7 +623,25 @@ func TestIntArray_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []int{1, 4, 3, 2}
|
||||
a1 := *garray.NewIntArrayFrom(s1)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewIntArray()
|
||||
err1 = json.Unmarshal(b2, &a2)
|
||||
t.Assert(a2.Slice(), s1)
|
||||
|
||||
var a3 garray.IntArray
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
@ -576,6 +654,25 @@ func TestIntArray_Json(t *testing.T) {
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, data["Scores"])
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores garray.IntArray
|
||||
}
|
||||
data := g.Map{
|
||||
"Name": "john",
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
t.Assert(err, nil)
|
||||
@ -683,3 +780,12 @@ func TestIntArray_FilterEmpty(t *testing.T) {
|
||||
t.Assert(array.FilterEmpty(), g.SliceInt{1, 2, 3, 4})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Walk(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewIntArrayFrom(g.SliceInt{1, 2})
|
||||
t.Assert(array.Walk(func(value int) int {
|
||||
return 10 + value
|
||||
}), g.Slice{11, 12})
|
||||
})
|
||||
}
|
||||
|
||||
@ -9,8 +9,8 @@
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"strings"
|
||||
"testing"
|
||||
"time"
|
||||
@ -64,6 +64,16 @@ func Test_StrArray_Basic(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_ContainsI(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := garray.NewStrArray()
|
||||
s.Append("a", "b", "C")
|
||||
t.Assert(s.Contains("A"), false)
|
||||
t.Assert(s.Contains("a"), true)
|
||||
t.Assert(s.ContainsI("A"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_Sort(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect1 := []string{"0", "1", "2", "3"}
|
||||
@ -81,7 +91,7 @@ func TestStrArray_Sort(t *testing.T) {
|
||||
|
||||
func TestStrArray_Unique(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
expect := []string{"1", "1", "2", "3"}
|
||||
expect := []string{"1", "1", "2", "2", "3", "3", "2", "2"}
|
||||
array := garray.NewStrArrayFrom(expect)
|
||||
t.Assert(array.Unique().Slice(), []string{"1", "2", "3"})
|
||||
})
|
||||
@ -119,6 +129,65 @@ func TestStrArray_PushAndPop(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_PopLeft(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewStrArrayFrom(g.SliceStr{"1", "2", "3"})
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_PopRight(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewStrArrayFrom(g.SliceStr{"1", "2", "3"})
|
||||
|
||||
v, ok := array.PopRight()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_PopLefts(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewStrArrayFrom(g.SliceStr{"1", "2", "3"})
|
||||
t.Assert(array.PopLefts(2), g.Slice{"1", "2"})
|
||||
t.Assert(array.Len(), 1)
|
||||
t.Assert(array.PopLefts(2), g.Slice{"3"})
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_PopRights(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewStrArrayFrom(g.SliceStr{"1", "2", "3"})
|
||||
t.Assert(array.PopRights(2), g.Slice{"2", "3"})
|
||||
t.Assert(array.Len(), 1)
|
||||
t.Assert(array.PopLefts(2), g.Slice{"1"})
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_PopLeftsAndPopRights(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewStrArray()
|
||||
@ -535,6 +604,7 @@ func TestStrArray_LockFunc(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestStrArray_Json(t *testing.T) {
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []string{"a", "b", "d", "c"}
|
||||
a1 := garray.NewStrArrayFrom(s1)
|
||||
@ -552,7 +622,25 @@ func TestStrArray_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []string{"a", "b", "d", "c"}
|
||||
a1 := *garray.NewStrArrayFrom(s1)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewStrArray()
|
||||
err1 = json.Unmarshal(b2, &a2)
|
||||
t.Assert(a2.Slice(), s1)
|
||||
|
||||
var a3 garray.StrArray
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
@ -565,6 +653,25 @@ func TestStrArray_Json(t *testing.T) {
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, data["Scores"])
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores garray.StrArray
|
||||
}
|
||||
data := g.Map{
|
||||
"Name": "john",
|
||||
"Scores": []string{"A+", "A", "A"},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
t.Assert(err, nil)
|
||||
@ -671,3 +778,12 @@ func TestStrArray_FilterEmpty(t *testing.T) {
|
||||
t.Assert(array.FilterEmpty(), g.SliceStr{"1", "2"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrArray_Walk(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewStrArrayFrom(g.SliceStr{"1", "2"})
|
||||
t.Assert(array.Walk(func(value string) string {
|
||||
return "key-" + value
|
||||
}), g.Slice{"key-1", "key-2"})
|
||||
})
|
||||
}
|
||||
|
||||
@ -9,8 +9,8 @@
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
"strings"
|
||||
"testing"
|
||||
@ -148,34 +148,62 @@ func TestSortedArray_Remove(t *testing.T) {
|
||||
|
||||
func TestSortedArray_PopLeft(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "b"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
array1 := garray.NewSortedArrayFrom(
|
||||
[]interface{}{"a", "d", "c", "b"},
|
||||
gutil.ComparatorString,
|
||||
)
|
||||
i1, ok := array1.PopLeft()
|
||||
t.Assert(ok, true)
|
||||
t.Assert(gconv.String(i1), "a")
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1, []interface{}{"b", "c", "d"})
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedArrayFrom(g.Slice{1, 2, 3}, gutil.ComparatorInt)
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_PopRight(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "b"}
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
array1 := garray.NewSortedArrayFrom(
|
||||
[]interface{}{"a", "d", "c", "b"},
|
||||
gutil.ComparatorString,
|
||||
)
|
||||
i1, ok := array1.PopRight()
|
||||
t.Assert(ok, true)
|
||||
t.Assert(gconv.String(i1), "d")
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1, []interface{}{"a", "b", "c"})
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedArrayFrom(g.Slice{1, 2, 3}, gutil.ComparatorInt)
|
||||
v, ok := array.PopRight()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_PopRand(t *testing.T) {
|
||||
@ -249,7 +277,6 @@ func TestSortedArray_PopLefts(t *testing.T) {
|
||||
t.Assert(len(i2), 4)
|
||||
t.AssertIN(i1, []interface{}{"a", "d", "c", "b", "e", "f"})
|
||||
t.Assert(array1.Len(), 0)
|
||||
|
||||
})
|
||||
}
|
||||
|
||||
@ -267,7 +294,7 @@ func TestSortedArray_PopRights(t *testing.T) {
|
||||
|
||||
i2 := array1.PopRights(10)
|
||||
t.Assert(len(i2), 4)
|
||||
|
||||
t.Assert(array1.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
@ -499,15 +526,21 @@ func TestSortedArray_CountValues(t *testing.T) {
|
||||
|
||||
func TestSortedArray_SetUnique(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{"a", "d", "c", "c"}
|
||||
|
||||
func1 := func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
}
|
||||
array1 := garray.NewSortedArrayFrom(a1, func1)
|
||||
a1 := []interface{}{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
|
||||
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorInt)
|
||||
array1.SetUnique(true)
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1, []interface{}{"a", "c", "d"})
|
||||
t.Assert(array1.Len(), 5)
|
||||
t.Assert(array1, []interface{}{1, 2, 3, 4, 5})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Unique(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []interface{}{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
|
||||
array1 := garray.NewSortedArrayFrom(a1, gutil.ComparatorInt)
|
||||
array1.Unique()
|
||||
t.Assert(array1.Len(), 5)
|
||||
t.Assert(array1, []interface{}{1, 2, 3, 4, 5})
|
||||
})
|
||||
}
|
||||
|
||||
@ -612,6 +645,7 @@ func TestSortedArray_Merge(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSortedArray_Json(t *testing.T) {
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []interface{}{"a", "b", "d", "c"}
|
||||
s2 := []interface{}{"a", "b", "c", "d"}
|
||||
@ -631,7 +665,27 @@ func TestSortedArray_Json(t *testing.T) {
|
||||
t.Assert(a3.Slice(), s1)
|
||||
t.Assert(a3.Interfaces(), s1)
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []interface{}{"a", "b", "d", "c"}
|
||||
s2 := []interface{}{"a", "b", "c", "d"}
|
||||
a1 := *garray.NewSortedArrayFrom(s1, gutil.ComparatorString)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewSortedArray(gutil.ComparatorString)
|
||||
err1 = json.Unmarshal(b2, &a2)
|
||||
t.Assert(a2.Slice(), s2)
|
||||
|
||||
var a3 garray.SortedArray
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
t.Assert(a3.Interfaces(), s1)
|
||||
})
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
@ -663,6 +717,42 @@ func TestSortedArray_Json(t *testing.T) {
|
||||
t.AssertIN(v, data["Scores"])
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = user.Scores.PopLeft()
|
||||
t.Assert(v, nil)
|
||||
t.Assert(ok, false)
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores garray.SortedArray
|
||||
}
|
||||
data := g.Map{
|
||||
"Name": "john",
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.AssertNE(user.Scores, nil)
|
||||
t.Assert(user.Scores.Len(), 3)
|
||||
|
||||
v, ok := user.Scores.PopLeft()
|
||||
t.AssertIN(v, data["Scores"])
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = user.Scores.PopLeft()
|
||||
t.AssertIN(v, data["Scores"])
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = user.Scores.PopLeft()
|
||||
t.AssertIN(v, data["Scores"])
|
||||
t.Assert(ok, true)
|
||||
|
||||
v, ok = user.Scores.PopLeft()
|
||||
t.Assert(v, nil)
|
||||
t.Assert(ok, false)
|
||||
@ -779,3 +869,12 @@ func TestSortedArray_FilterEmpty(t *testing.T) {
|
||||
t.Assert(array.FilterEmpty(), g.Slice{1, 2, 3, 4})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedArray_Walk(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedArrayFrom(g.Slice{"1", "2"}, gutil.ComparatorString)
|
||||
t.Assert(array.Walk(func(value interface{}) interface{} {
|
||||
return "key-" + gconv.String(value)
|
||||
}), g.Slice{"key-1", "key-2"})
|
||||
})
|
||||
}
|
||||
|
||||
@ -9,8 +9,8 @@
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -133,6 +133,21 @@ func TestSortedIntArray_PopLeft(t *testing.T) {
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1.Search(1), -1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedIntArrayFrom(g.SliceInt{1, 2, 3})
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_PopRight(t *testing.T) {
|
||||
@ -145,6 +160,23 @@ func TestSortedIntArray_PopRight(t *testing.T) {
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1.Search(5), -1)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedIntArrayFrom(g.SliceInt{1, 2, 3})
|
||||
v, ok := array.PopRight()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_PopRand(t *testing.T) {
|
||||
@ -404,7 +436,7 @@ func TestSortedIntArray_CountValues(t *testing.T) {
|
||||
|
||||
func TestSortedIntArray_SetUnique(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5, 3}
|
||||
a1 := []int{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
array1.SetUnique(true)
|
||||
t.Assert(array1.Len(), 5)
|
||||
@ -412,6 +444,16 @@ func TestSortedIntArray_SetUnique(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Unique(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []int{1, 2, 3, 4, 5, 3, 2, 2, 3, 5, 5}
|
||||
array1 := garray.NewSortedIntArrayFrom(a1)
|
||||
array1.Unique()
|
||||
t.Assert(array1.Len(), 5)
|
||||
t.Assert(array1, []int{1, 2, 3, 4, 5})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_LockFunc(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []int{1, 2, 3, 4}
|
||||
@ -504,6 +546,7 @@ func TestSortedIntArray_Merge(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Json(t *testing.T) {
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []int{1, 4, 3, 2}
|
||||
s2 := []int{1, 2, 3, 4}
|
||||
@ -522,7 +565,26 @@ func TestSortedIntArray_Json(t *testing.T) {
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []int{1, 4, 3, 2}
|
||||
s2 := []int{1, 2, 3, 4}
|
||||
a1 := *garray.NewSortedIntArrayFrom(s1)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewSortedIntArray()
|
||||
err1 = json.Unmarshal(b2, &a2)
|
||||
t.Assert(a2.Slice(), s2)
|
||||
|
||||
var a3 garray.SortedIntArray
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
})
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
@ -535,6 +597,25 @@ func TestSortedIntArray_Json(t *testing.T) {
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, []int{98, 99, 100})
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores garray.SortedIntArray
|
||||
}
|
||||
data := g.Map{
|
||||
"Name": "john",
|
||||
"Scores": []int{99, 100, 98},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
t.Assert(err, nil)
|
||||
@ -642,3 +723,12 @@ func TestSortedIntArray_FilterEmpty(t *testing.T) {
|
||||
t.Assert(array.FilterEmpty(), g.SliceInt{1, 2, 3, 4})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedIntArray_Walk(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedIntArrayFrom(g.SliceInt{1, 2})
|
||||
t.Assert(array.Walk(func(value int) int {
|
||||
return 10 + value
|
||||
}), g.Slice{11, 12})
|
||||
})
|
||||
}
|
||||
|
||||
@ -9,8 +9,8 @@
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
@ -51,6 +51,16 @@ func TestSortedStrArray_SetArray(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_ContainsI(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := garray.NewSortedStrArray()
|
||||
s.Append("a", "b", "C")
|
||||
t.Assert(s.Contains("A"), false)
|
||||
t.Assert(s.Contains("a"), true)
|
||||
t.Assert(s.ContainsI("A"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Sort(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"a", "d", "c", "b"}
|
||||
@ -127,6 +137,21 @@ func TestSortedStrArray_PopLeft(t *testing.T) {
|
||||
t.Assert(array1.Len(), 4)
|
||||
t.Assert(array1.Contains("a"), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedStrArrayFrom(g.SliceStr{"1", "2", "3"})
|
||||
v, ok := array.PopLeft()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
v, ok = array.PopLeft()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_PopRight(t *testing.T) {
|
||||
@ -139,6 +164,23 @@ func TestSortedStrArray_PopRight(t *testing.T) {
|
||||
t.Assert(array1.Len(), 4)
|
||||
t.Assert(array1.Contains("e"), false)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedStrArrayFrom(g.SliceStr{"1", "2", "3"})
|
||||
v, ok := array.PopRight()
|
||||
t.Assert(v, 3)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 2)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 2)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 1)
|
||||
|
||||
v, ok = array.PopRight()
|
||||
t.Assert(v, 1)
|
||||
t.Assert(ok, true)
|
||||
t.Assert(array.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_PopRand(t *testing.T) {
|
||||
@ -200,6 +242,7 @@ func TestSortedStrArray_PopLefts(t *testing.T) {
|
||||
s1 = array1.PopLefts(4)
|
||||
t.Assert(len(s1), 3)
|
||||
t.Assert(s1, []string{"c", "d", "e"})
|
||||
t.Assert(array1.Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
@ -411,11 +454,21 @@ func TestSortedStrArray_Chunk(t *testing.T) {
|
||||
|
||||
func TestSortedStrArray_SetUnique(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"e", "a", "d", "a", "c"}
|
||||
a1 := []string{"1", "1", "2", "2", "3", "3", "2", "2"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
array2 := array1.SetUnique(true)
|
||||
t.Assert(array2.Len(), 4)
|
||||
t.Assert(array2, []string{"a", "c", "d", "e"})
|
||||
t.Assert(array2.Len(), 3)
|
||||
t.Assert(array2, []string{"1", "2", "3"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Unique(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
a1 := []string{"1", "1", "2", "2", "3", "3", "2", "2"}
|
||||
array1 := garray.NewSortedStrArrayFrom(a1)
|
||||
array1.Unique()
|
||||
t.Assert(array1.Len(), 3)
|
||||
t.Assert(array1, []string{"1", "2", "3"})
|
||||
})
|
||||
}
|
||||
|
||||
@ -513,6 +566,7 @@ func TestSortedStrArray_Merge(t *testing.T) {
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Json(t *testing.T) {
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []string{"a", "b", "d", "c"}
|
||||
s2 := []string{"a", "b", "c", "d"}
|
||||
@ -533,7 +587,28 @@ func TestSortedStrArray_Json(t *testing.T) {
|
||||
t.Assert(a3.Slice(), s1)
|
||||
t.Assert(a3.Interfaces(), s1)
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []string{"a", "b", "d", "c"}
|
||||
s2 := []string{"a", "b", "c", "d"}
|
||||
a1 := *garray.NewSortedStrArrayFrom(s1)
|
||||
b1, err1 := json.Marshal(a1)
|
||||
b2, err2 := json.Marshal(s1)
|
||||
t.Assert(b1, b2)
|
||||
t.Assert(err1, err2)
|
||||
|
||||
a2 := garray.NewSortedStrArray()
|
||||
err1 = json.Unmarshal(b2, &a2)
|
||||
t.Assert(a2.Slice(), s2)
|
||||
t.Assert(a2.Interfaces(), s2)
|
||||
|
||||
var a3 garray.SortedStrArray
|
||||
err := json.Unmarshal(b2, &a3)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(a3.Slice(), s1)
|
||||
t.Assert(a3.Interfaces(), s1)
|
||||
})
|
||||
// array pointer
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
@ -546,6 +621,25 @@ func TestSortedStrArray_Json(t *testing.T) {
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
t.Assert(err, nil)
|
||||
t.Assert(user.Name, data["Name"])
|
||||
t.Assert(user.Scores, []string{"A", "A", "A+"})
|
||||
})
|
||||
// array value
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
type User struct {
|
||||
Name string
|
||||
Scores garray.SortedStrArray
|
||||
}
|
||||
data := g.Map{
|
||||
"Name": "john",
|
||||
"Scores": []string{"A+", "A", "A"},
|
||||
}
|
||||
b, err := json.Marshal(data)
|
||||
t.Assert(err, nil)
|
||||
|
||||
user := new(User)
|
||||
err = json.Unmarshal(b, user)
|
||||
t.Assert(err, nil)
|
||||
@ -652,3 +746,12 @@ func TestSortedStrArray_FilterEmpty(t *testing.T) {
|
||||
t.Assert(array.FilterEmpty(), g.SliceStr{"1", "2"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSortedStrArray_Walk(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
array := garray.NewSortedStrArrayFrom(g.SliceStr{"1", "2"})
|
||||
t.Assert(array.Walk(func(value string) string {
|
||||
return "key-" + value
|
||||
}), g.Slice{"key-1", "key-2"})
|
||||
})
|
||||
}
|
||||
|
||||
@ -1,70 +0,0 @@
|
||||
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gchan provides graceful channel for no panic operations.
|
||||
//
|
||||
// It's safe to call Chan.Push/Close functions repeatedly.
|
||||
package gchan
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/gogf/gf/container/gtype"
|
||||
)
|
||||
|
||||
// Graceful channel.
|
||||
type Chan struct {
|
||||
channel chan interface{}
|
||||
closed *gtype.Bool
|
||||
}
|
||||
|
||||
// New creates a graceful channel with given <limit>.
|
||||
func New(limit int) *Chan {
|
||||
return &Chan{
|
||||
channel: make(chan interface{}, limit),
|
||||
closed: gtype.NewBool(),
|
||||
}
|
||||
}
|
||||
|
||||
// Push pushes <value> to channel.
|
||||
// It is safe to be called repeatedly.
|
||||
func (c *Chan) Push(value interface{}) error {
|
||||
if c.closed.Val() {
|
||||
return errors.New("channel is closed")
|
||||
}
|
||||
c.channel <- value
|
||||
return nil
|
||||
}
|
||||
|
||||
// Pop pops value from channel.
|
||||
// If there's no value in channel, it would block to wait.
|
||||
// If the channel is closed, it will return a nil value immediately.
|
||||
func (c *Chan) Pop() interface{} {
|
||||
return <-c.channel
|
||||
}
|
||||
|
||||
// Close closes the channel.
|
||||
// It is safe to be called repeatedly.
|
||||
func (c *Chan) Close() {
|
||||
if !c.closed.Set(true) {
|
||||
close(c.channel)
|
||||
}
|
||||
}
|
||||
|
||||
// See Len.
|
||||
func (c *Chan) Size() int {
|
||||
return c.Len()
|
||||
}
|
||||
|
||||
// Len returns the length of the channel.
|
||||
func (c *Chan) Len() int {
|
||||
return len(c.channel)
|
||||
}
|
||||
|
||||
// Cap returns the capacity of the channel.
|
||||
func (c *Chan) Cap() int {
|
||||
return cap(c.channel)
|
||||
}
|
||||
@ -1,30 +0,0 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gchan_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/container/gchan"
|
||||
)
|
||||
|
||||
func Example_basic() {
|
||||
n := 10
|
||||
c := gchan.New(n)
|
||||
for i := 0; i < n; i++ {
|
||||
c.Push(i)
|
||||
}
|
||||
fmt.Println(c.Len(), c.Cap())
|
||||
for i := 0; i < n; i++ {
|
||||
fmt.Print(c.Pop())
|
||||
}
|
||||
c.Close()
|
||||
|
||||
// Output:
|
||||
//10 10
|
||||
//0123456789
|
||||
}
|
||||
@ -1,47 +0,0 @@
|
||||
package gchan_test
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/container/gchan"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
)
|
||||
|
||||
func Test_Gchan(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
ch := gchan.New(10)
|
||||
|
||||
t.Assert(ch.Cap(), 10)
|
||||
t.Assert(ch.Push(1), nil)
|
||||
t.Assert(ch.Len(), 1)
|
||||
t.Assert(ch.Size(), 1)
|
||||
ch.Pop()
|
||||
t.Assert(ch.Len(), 0)
|
||||
t.Assert(ch.Size(), 0)
|
||||
ch.Close()
|
||||
t.Assert(ch.Push(1), errors.New("channel is closed"))
|
||||
|
||||
ch = gchan.New(0)
|
||||
ch1 := gchan.New(0)
|
||||
go func() {
|
||||
var i = 0
|
||||
for {
|
||||
v := ch.Pop()
|
||||
if v == nil {
|
||||
ch1.Push(i)
|
||||
break
|
||||
}
|
||||
t.Assert(v, i)
|
||||
i++
|
||||
}
|
||||
}()
|
||||
|
||||
for index := 0; index < 10; index++ {
|
||||
ch.Push(index)
|
||||
}
|
||||
ch.Close()
|
||||
t.Assert(ch1.Pop(), 10)
|
||||
ch1.Close()
|
||||
})
|
||||
}
|
||||
@ -5,32 +5,33 @@
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
// Package glist provides a concurrent-safe/unsafe doubly linked list.
|
||||
// Package glist provides most commonly used doubly linked list container which also supports concurrent-safe/unsafe switch feature.
|
||||
package glist
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"container/list"
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
|
||||
"github.com/gogf/gf/internal/rwmutex"
|
||||
)
|
||||
|
||||
type (
|
||||
// List is a doubly linked list containing a concurrent-safe/unsafe switch.
|
||||
// The switch should be set when its initialization and cannot be changed then.
|
||||
List struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
list *list.List
|
||||
}
|
||||
|
||||
// Element the item type of the list.
|
||||
Element = list.Element
|
||||
)
|
||||
|
||||
// New creates and returns a new empty doubly linked list.
|
||||
func New(safe ...bool) *List {
|
||||
return &List{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
list: list.New(),
|
||||
}
|
||||
}
|
||||
@ -44,7 +45,7 @@ func NewFrom(array []interface{}, safe ...bool) *List {
|
||||
l.PushBack(v)
|
||||
}
|
||||
return &List{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
list: l,
|
||||
}
|
||||
}
|
||||
@ -52,6 +53,9 @@ func NewFrom(array []interface{}, safe ...bool) *List {
|
||||
// PushFront inserts a new element <e> with value <v> at the front of list <l> and returns <e>.
|
||||
func (l *List) PushFront(v interface{}) (e *Element) {
|
||||
l.mu.Lock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
e = l.list.PushFront(v)
|
||||
l.mu.Unlock()
|
||||
return
|
||||
@ -60,6 +64,9 @@ func (l *List) PushFront(v interface{}) (e *Element) {
|
||||
// PushBack inserts a new element <e> with value <v> at the back of list <l> and returns <e>.
|
||||
func (l *List) PushBack(v interface{}) (e *Element) {
|
||||
l.mu.Lock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
e = l.list.PushBack(v)
|
||||
l.mu.Unlock()
|
||||
return
|
||||
@ -68,6 +75,9 @@ func (l *List) PushBack(v interface{}) (e *Element) {
|
||||
// PushFronts inserts multiple new elements with values <values> at the front of list <l>.
|
||||
func (l *List) PushFronts(values []interface{}) {
|
||||
l.mu.Lock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
for _, v := range values {
|
||||
l.list.PushFront(v)
|
||||
}
|
||||
@ -77,6 +87,9 @@ func (l *List) PushFronts(values []interface{}) {
|
||||
// PushBacks inserts multiple new elements with values <values> at the back of list <l>.
|
||||
func (l *List) PushBacks(values []interface{}) {
|
||||
l.mu.Lock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
for _, v := range values {
|
||||
l.list.PushBack(v)
|
||||
}
|
||||
@ -86,20 +99,28 @@ func (l *List) PushBacks(values []interface{}) {
|
||||
// PopBack removes the element from back of <l> and returns the value of the element.
|
||||
func (l *List) PopBack() (value interface{}) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
return
|
||||
}
|
||||
if e := l.list.Back(); e != nil {
|
||||
value = l.list.Remove(e)
|
||||
}
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// PopFront removes the element from front of <l> and returns the value of the element.
|
||||
func (l *List) PopFront() (value interface{}) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
return
|
||||
}
|
||||
if e := l.list.Front(); e != nil {
|
||||
value = l.list.Remove(e)
|
||||
}
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -107,6 +128,11 @@ func (l *List) PopFront() (value interface{}) {
|
||||
// and returns values of the removed elements as slice.
|
||||
func (l *List) PopBacks(max int) (values []interface{}) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
return
|
||||
}
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
if max > 0 && max < length {
|
||||
@ -117,7 +143,6 @@ func (l *List) PopBacks(max int) (values []interface{}) {
|
||||
values[i] = l.list.Remove(l.list.Back())
|
||||
}
|
||||
}
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -125,6 +150,11 @@ func (l *List) PopBacks(max int) (values []interface{}) {
|
||||
// and returns values of the removed elements as slice.
|
||||
func (l *List) PopFronts(max int) (values []interface{}) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
return
|
||||
}
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
if max > 0 && max < length {
|
||||
@ -135,7 +165,6 @@ func (l *List) PopFronts(max int) (values []interface{}) {
|
||||
values[i] = l.list.Remove(l.list.Front())
|
||||
}
|
||||
}
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -154,6 +183,10 @@ func (l *List) PopFrontAll() []interface{} {
|
||||
// FrontAll copies and returns values of all elements from front of <l> as slice.
|
||||
func (l *List) FrontAll() (values []interface{}) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
values = make([]interface{}, length)
|
||||
@ -161,13 +194,16 @@ func (l *List) FrontAll() (values []interface{}) {
|
||||
values[i] = e.Value
|
||||
}
|
||||
}
|
||||
l.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// BackAll copies and returns values of all elements from back of <l> as slice.
|
||||
func (l *List) BackAll() (values []interface{}) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
values = make([]interface{}, length)
|
||||
@ -175,43 +211,54 @@ func (l *List) BackAll() (values []interface{}) {
|
||||
values[i] = e.Value
|
||||
}
|
||||
}
|
||||
l.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// FrontValue returns value of the first element of <l> or nil if the list is empty.
|
||||
func (l *List) FrontValue() (value interface{}) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
if e := l.list.Front(); e != nil {
|
||||
value = e.Value
|
||||
}
|
||||
l.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// BackValue returns value of the last element of <l> or nil if the list is empty.
|
||||
func (l *List) BackValue() (value interface{}) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
if e := l.list.Back(); e != nil {
|
||||
value = e.Value
|
||||
}
|
||||
l.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Front returns the first element of list <l> or nil if the list is empty.
|
||||
func (l *List) Front() (e *Element) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
e = l.list.Front()
|
||||
l.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Back returns the last element of list <l> or nil if the list is empty.
|
||||
func (l *List) Back() (e *Element) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
e = l.list.Back()
|
||||
l.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -219,8 +266,11 @@ func (l *List) Back() (e *Element) {
|
||||
// The complexity is O(1).
|
||||
func (l *List) Len() (length int) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
length = l.list.Len()
|
||||
l.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -234,8 +284,11 @@ func (l *List) Size() int {
|
||||
// The element and <p> must not be nil.
|
||||
func (l *List) MoveBefore(e, p *Element) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
l.list.MoveBefore(e, p)
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// MoveAfter moves element <e> to its new position after <p>.
|
||||
@ -243,8 +296,11 @@ func (l *List) MoveBefore(e, p *Element) {
|
||||
// The element and <p> must not be nil.
|
||||
func (l *List) MoveAfter(e, p *Element) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
l.list.MoveAfter(e, p)
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// MoveToFront moves element <e> to the front of list <l>.
|
||||
@ -252,8 +308,11 @@ func (l *List) MoveAfter(e, p *Element) {
|
||||
// The element must not be nil.
|
||||
func (l *List) MoveToFront(e *Element) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
l.list.MoveToFront(e)
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// MoveToBack moves element <e> to the back of list <l>.
|
||||
@ -261,8 +320,11 @@ func (l *List) MoveToFront(e *Element) {
|
||||
// The element must not be nil.
|
||||
func (l *List) MoveToBack(e *Element) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
l.list.MoveToBack(e)
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// PushBackList inserts a copy of an other list at the back of list <l>.
|
||||
@ -273,8 +335,11 @@ func (l *List) PushBackList(other *List) {
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
l.list.PushBackList(other.list)
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// PushFrontList inserts a copy of an other list at the front of list <l>.
|
||||
@ -285,8 +350,11 @@ func (l *List) PushFrontList(other *List) {
|
||||
defer other.mu.RUnlock()
|
||||
}
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
l.list.PushFrontList(other.list)
|
||||
l.mu.Unlock()
|
||||
}
|
||||
|
||||
// InsertAfter inserts a new element <e> with value <v> immediately after <p> and returns <e>.
|
||||
@ -294,8 +362,11 @@ func (l *List) PushFrontList(other *List) {
|
||||
// The <p> must not be nil.
|
||||
func (l *List) InsertAfter(p *Element, v interface{}) (e *Element) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
e = l.list.InsertAfter(v, p)
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -304,8 +375,11 @@ func (l *List) InsertAfter(p *Element, v interface{}) (e *Element) {
|
||||
// The <p> must not be nil.
|
||||
func (l *List) InsertBefore(p *Element, v interface{}) (e *Element) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
e = l.list.InsertBefore(v, p)
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -314,18 +388,24 @@ func (l *List) InsertBefore(p *Element, v interface{}) (e *Element) {
|
||||
// The element must not be nil.
|
||||
func (l *List) Remove(e *Element) (value interface{}) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
value = l.list.Remove(e)
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Removes removes multiple elements <es> from <l> if <es> are elements of list <l>.
|
||||
func (l *List) Removes(es []*Element) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
for _, e := range es {
|
||||
l.list.Remove(e)
|
||||
}
|
||||
l.mu.Unlock()
|
||||
return
|
||||
}
|
||||
|
||||
@ -345,13 +425,18 @@ func (l *List) Clear() {
|
||||
func (l *List) RLockFunc(f func(list *list.List)) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
f(l.list)
|
||||
if l.list != nil {
|
||||
f(l.list)
|
||||
}
|
||||
}
|
||||
|
||||
// LockFunc locks writing with given callback function <f> within RWMutex.Lock.
|
||||
func (l *List) LockFunc(f func(list *list.List)) {
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
f(l.list)
|
||||
}
|
||||
|
||||
@ -360,11 +445,14 @@ func (l *List) Iterator(f func(e *Element) bool) {
|
||||
l.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the list in ascending order with given callback function <f>.
|
||||
// IteratorAsc iterates the list readonly in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (l *List) IteratorAsc(f func(e *Element) bool) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
for i, e := 0, l.list.Front(); i < length; i, e = i+1, e.Next() {
|
||||
@ -375,11 +463,14 @@ func (l *List) IteratorAsc(f func(e *Element) bool) {
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the list in descending order with given callback function <f>.
|
||||
// IteratorDesc iterates the list readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (l *List) IteratorDesc(f func(e *Element) bool) {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return
|
||||
}
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
for i, e := 0, l.list.Back(); i < length; i, e = i+1, e.Prev() {
|
||||
@ -394,17 +485,14 @@ func (l *List) IteratorDesc(f func(e *Element) bool) {
|
||||
func (l *List) Join(glue string) string {
|
||||
l.mu.RLock()
|
||||
defer l.mu.RUnlock()
|
||||
if l.list == nil {
|
||||
return ""
|
||||
}
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
length := l.list.Len()
|
||||
if length > 0 {
|
||||
s := ""
|
||||
for i, e := 0, l.list.Front(); i < length; i, e = i+1, e.Next() {
|
||||
s = gconv.String(e.Value)
|
||||
if gstr.IsNumeric(s) {
|
||||
buffer.WriteString(s)
|
||||
} else {
|
||||
buffer.WriteString(`"` + gstr.QuoteMeta(s, `"\`) + `"`)
|
||||
}
|
||||
buffer.WriteString(gconv.String(e.Value))
|
||||
if i != length-1 {
|
||||
buffer.WriteString(glue)
|
||||
}
|
||||
@ -425,12 +513,11 @@ func (l *List) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (l *List) UnmarshalJSON(b []byte) error {
|
||||
if l.mu == nil {
|
||||
l.mu = rwmutex.New()
|
||||
l.list = list.New()
|
||||
}
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
var array []interface{}
|
||||
if err := json.Unmarshal(b, &array); err != nil {
|
||||
return err
|
||||
@ -441,12 +528,11 @@ func (l *List) UnmarshalJSON(b []byte) error {
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for list.
|
||||
func (l *List) UnmarshalValue(value interface{}) (err error) {
|
||||
if l.mu == nil {
|
||||
l.mu = rwmutex.New()
|
||||
l.list = list.New()
|
||||
}
|
||||
l.mu.Lock()
|
||||
defer l.mu.Unlock()
|
||||
if l.list == nil {
|
||||
l.list = list.New()
|
||||
}
|
||||
var array []interface{}
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
|
||||
@ -10,11 +10,12 @@ import (
|
||||
"container/list"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
|
||||
"github.com/gogf/gf/container/glist"
|
||||
)
|
||||
|
||||
func Example_basic() {
|
||||
func ExampleNew() {
|
||||
n := 10
|
||||
l := glist.New()
|
||||
for i := 0; i < n; i++ {
|
||||
@ -31,14 +32,14 @@ func Example_basic() {
|
||||
fmt.Println(l.Len())
|
||||
|
||||
// Output:
|
||||
//10
|
||||
//[0 1 2 3 4 5 6 7 8 9]
|
||||
//[9 8 7 6 5 4 3 2 1 0]
|
||||
//0123456789
|
||||
//0
|
||||
// 10
|
||||
// [0 1 2 3 4 5 6 7 8 9]
|
||||
// [9 8 7 6 5 4 3 2 1 0]
|
||||
// 0123456789
|
||||
// 0
|
||||
}
|
||||
|
||||
func Example_iterate() {
|
||||
func ExampleList_RLockFunc() {
|
||||
// concurrent-safe list.
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 10, 1).Slice(), true)
|
||||
// iterate reading from head.
|
||||
@ -62,21 +63,39 @@ func Example_iterate() {
|
||||
})
|
||||
|
||||
fmt.Println()
|
||||
// Output:
|
||||
// 12345678910
|
||||
// 10987654321
|
||||
}
|
||||
|
||||
func ExampleList_IteratorAsc() {
|
||||
// concurrent-safe list.
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 10, 1).Slice(), true)
|
||||
// iterate reading from head using IteratorAsc.
|
||||
l.IteratorAsc(func(e *glist.Element) bool {
|
||||
fmt.Print(e.Value)
|
||||
return true
|
||||
})
|
||||
fmt.Println()
|
||||
|
||||
// Output:
|
||||
// 12345678910
|
||||
}
|
||||
|
||||
func ExampleList_IteratorDesc() {
|
||||
// concurrent-safe list.
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 10, 1).Slice(), true)
|
||||
// iterate reading from tail using IteratorDesc.
|
||||
l.IteratorDesc(func(e *glist.Element) bool {
|
||||
fmt.Print(e.Value)
|
||||
return true
|
||||
})
|
||||
// Output:
|
||||
// 10987654321
|
||||
}
|
||||
|
||||
fmt.Println()
|
||||
|
||||
func ExampleList_LockFunc() {
|
||||
// concurrent-safe list.
|
||||
l := glist.NewFrom(garray.NewArrayRange(1, 10, 1).Slice(), true)
|
||||
// iterate writing from head.
|
||||
l.LockFunc(func(list *list.List) {
|
||||
length := list.Len()
|
||||
@ -91,10 +110,51 @@ func Example_iterate() {
|
||||
})
|
||||
fmt.Println(l)
|
||||
|
||||
//output:
|
||||
//12345678910
|
||||
//10987654321
|
||||
//12345678910
|
||||
//10987654321
|
||||
//[1,2,3,4,5,"M",7,8,9,10]
|
||||
// Output:
|
||||
// [1,2,3,4,5,M,7,8,9,10]
|
||||
}
|
||||
|
||||
func ExampleList_PopBack() {
|
||||
l := glist.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
fmt.Println(l.PopBack())
|
||||
|
||||
// Output:
|
||||
// 9
|
||||
}
|
||||
func ExampleList_PopBacks() {
|
||||
l := glist.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
fmt.Println(l.PopBacks(2))
|
||||
|
||||
// Output:
|
||||
// [9 8]
|
||||
}
|
||||
|
||||
func ExampleList_PopFront() {
|
||||
l := glist.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
fmt.Println(l.PopFront())
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
}
|
||||
|
||||
func ExampleList_PopFronts() {
|
||||
l := glist.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
fmt.Println(l.PopFronts(2))
|
||||
|
||||
// Output:
|
||||
// [1 2]
|
||||
}
|
||||
|
||||
func ExampleList_Join() {
|
||||
var l glist.List
|
||||
l.PushBacks(g.Slice{"a", "b", "c", "d"})
|
||||
|
||||
fmt.Println(l.Join(","))
|
||||
|
||||
// Output:
|
||||
// a,b,c,d
|
||||
}
|
||||
|
||||
@ -8,14 +8,13 @@ package glist
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
|
||||
"testing"
|
||||
)
|
||||
|
||||
// 检查链表长度
|
||||
func checkListLen(t *gtest.T, l *List, len int) bool {
|
||||
if n := l.Len(); n != len {
|
||||
t.Errorf("l.Len() = %d, want %d", n, len)
|
||||
@ -24,7 +23,6 @@ func checkListLen(t *gtest.T, l *List, len int) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
// 检查指针地址
|
||||
func checkListPointers(t *gtest.T, l *List, es []*Element) {
|
||||
if !checkListLen(t, l, len(es)) {
|
||||
return
|
||||
@ -41,6 +39,44 @@ func checkListPointers(t *gtest.T, l *List, es []*Element) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestVar(t *testing.T) {
|
||||
var l List
|
||||
l.PushFront(1)
|
||||
l.PushFront(2)
|
||||
if v := l.PopBack(); v != 1 {
|
||||
t.Errorf("EXPECT %v, GOT %v", 1, v)
|
||||
} else {
|
||||
//fmt.Println(v)
|
||||
}
|
||||
if v := l.PopBack(); v != 2 {
|
||||
t.Errorf("EXPECT %v, GOT %v", 2, v)
|
||||
} else {
|
||||
//fmt.Println(v)
|
||||
}
|
||||
if v := l.PopBack(); v != nil {
|
||||
t.Errorf("EXPECT %v, GOT %v", nil, v)
|
||||
} else {
|
||||
//fmt.Println(v)
|
||||
}
|
||||
l.PushBack(1)
|
||||
l.PushBack(2)
|
||||
if v := l.PopFront(); v != 1 {
|
||||
t.Errorf("EXPECT %v, GOT %v", 1, v)
|
||||
} else {
|
||||
//fmt.Println(v)
|
||||
}
|
||||
if v := l.PopFront(); v != 2 {
|
||||
t.Errorf("EXPECT %v, GOT %v", 2, v)
|
||||
} else {
|
||||
//fmt.Println(v)
|
||||
}
|
||||
if v := l.PopFront(); v != nil {
|
||||
t.Errorf("EXPECT %v, GOT %v", nil, v)
|
||||
} else {
|
||||
//fmt.Println(v)
|
||||
}
|
||||
}
|
||||
|
||||
func TestBasic(t *testing.T) {
|
||||
l := New()
|
||||
l.PushFront(1)
|
||||
@ -567,6 +603,17 @@ func TestList_Removes(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_Pop(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewFrom([]interface{}{1, 2, 3, 4, 5, 6, 7, 8, 9})
|
||||
|
||||
t.Assert(l.PopBack(), 9)
|
||||
t.Assert(l.PopBacks(2), []interface{}{8, 7})
|
||||
t.Assert(l.PopFront(), 1)
|
||||
t.Assert(l.PopFronts(2), []interface{}{2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_Clear(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := New()
|
||||
@ -634,15 +681,15 @@ func TestList_Iterator(t *testing.T) {
|
||||
func TestList_Join(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewFrom([]interface{}{1, 2, "a", `"b"`, `\c`})
|
||||
t.Assert(l.Join(","), `1,2,"a","\"b\"","\\c"`)
|
||||
t.Assert(l.Join("."), `1.2."a"."\"b\""."\\c"`)
|
||||
t.Assert(l.Join(","), `1,2,a,"b",\c`)
|
||||
t.Assert(l.Join("."), `1.2.a."b".\c`)
|
||||
})
|
||||
}
|
||||
|
||||
func TestList_String(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
l := NewFrom([]interface{}{1, 2, "a", `"b"`, `\c`})
|
||||
t.Assert(l.String(), `[1,2,"a","\"b\"","\\c"]`)
|
||||
t.Assert(l.String(), `[1,2,a,"b",\c]`)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -4,12 +4,13 @@
|
||||
// If a copy of the MIT was not distributed with gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gmap provides concurrent-safe/unsafe map containers.
|
||||
// Package gmap provides most commonly used map container which also support concurrent-safe/unsafe switch feature.
|
||||
package gmap
|
||||
|
||||
// Map based on hash table, alias of AnyAnyMap.
|
||||
type Map = AnyAnyMap
|
||||
type HashMap = AnyAnyMap
|
||||
type (
|
||||
Map = AnyAnyMap // Map is alias of AnyAnyMap.
|
||||
HashMap = AnyAnyMap // HashMap is alias of AnyAnyMap.
|
||||
)
|
||||
|
||||
// New creates and returns an empty hash map.
|
||||
// The parameter <safe> is used to specify whether using map in concurrent-safety,
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
|
||||
@ -18,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
type AnyAnyMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
data map[interface{}]interface{}
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ type AnyAnyMap struct {
|
||||
// which is false in default.
|
||||
func NewAnyAnyMap(safe ...bool) *AnyAnyMap {
|
||||
return &AnyAnyMap{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: make(map[interface{}]interface{}),
|
||||
}
|
||||
}
|
||||
@ -37,12 +37,12 @@ func NewAnyAnyMap(safe ...bool) *AnyAnyMap {
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewAnyAnyMapFrom(data map[interface{}]interface{}, safe ...bool) *AnyAnyMap {
|
||||
return &AnyAnyMap{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// Iterator iterates the hash map readonly with custom callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (m *AnyAnyMap) Iterator(f func(k interface{}, v interface{}) bool) {
|
||||
m.mu.RLock()
|
||||
@ -89,37 +89,56 @@ func (m *AnyAnyMap) MapCopy() map[interface{}]interface{} {
|
||||
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
|
||||
func (m *AnyAnyMap) MapStrAny() map[string]interface{} {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
data := make(map[string]interface{}, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[gconv.String(k)] = v
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return data
|
||||
}
|
||||
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (m *AnyAnyMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for k, v := range m.data {
|
||||
if empty.IsEmpty(v) {
|
||||
delete(m.data, k)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// FilterNil deletes all key-value pair of which the value is nil.
|
||||
func (m *AnyAnyMap) FilterNil() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for k, v := range m.data {
|
||||
if empty.IsNil(v) {
|
||||
delete(m.data, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *AnyAnyMap) Set(key interface{}, val interface{}) {
|
||||
func (m *AnyAnyMap) Set(key interface{}, value interface{}) {
|
||||
m.mu.Lock()
|
||||
m.data[key] = val
|
||||
if m.data == nil {
|
||||
m.data = make(map[interface{}]interface{})
|
||||
}
|
||||
m.data[key] = value
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Sets batch sets key-values to the hash map.
|
||||
func (m *AnyAnyMap) Sets(data map[interface{}]interface{}) {
|
||||
m.mu.Lock()
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
if m.data == nil {
|
||||
m.data = data
|
||||
} else {
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
@ -128,17 +147,21 @@ func (m *AnyAnyMap) Sets(data map[interface{}]interface{}) {
|
||||
// Second return parameter <found> is true if key was found, otherwise false.
|
||||
func (m *AnyAnyMap) Search(key interface{}) (value interface{}, found bool) {
|
||||
m.mu.RLock()
|
||||
value, found = m.data[key]
|
||||
if m.data != nil {
|
||||
value, found = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (m *AnyAnyMap) Get(key interface{}) interface{} {
|
||||
func (m *AnyAnyMap) Get(key interface{}) (value interface{}) {
|
||||
m.mu.RLock()
|
||||
val, _ := m.data[key]
|
||||
if m.data != nil {
|
||||
value, _ = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return val
|
||||
return
|
||||
}
|
||||
|
||||
// Pop retrieves and deletes an item from the map.
|
||||
@ -163,8 +186,10 @@ func (m *AnyAnyMap) Pops(size int) map[interface{}]interface{} {
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
index := 0
|
||||
newMap := make(map[interface{}]interface{}, size)
|
||||
var (
|
||||
index = 0
|
||||
newMap = make(map[interface{}]interface{}, size)
|
||||
)
|
||||
for k, v := range m.data {
|
||||
delete(m.data, k)
|
||||
newMap[k] = v
|
||||
@ -188,6 +213,9 @@ func (m *AnyAnyMap) Pops(size int) map[interface{}]interface{} {
|
||||
func (m *AnyAnyMap) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[interface{}]interface{})
|
||||
}
|
||||
if v, ok := m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
@ -235,26 +263,26 @@ func (m *AnyAnyMap) GetOrSetFuncLock(key interface{}, f func() interface{}) inte
|
||||
}
|
||||
}
|
||||
|
||||
// GetVar returns a gvar.Var with the value by given <key>.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVar returns a Var with the value by given <key>.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *AnyAnyMap) GetVar(key interface{}) *gvar.Var {
|
||||
return gvar.New(m.Get(key))
|
||||
}
|
||||
|
||||
// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVarOrSet returns a Var with result from GetVarOrSet.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *AnyAnyMap) GetVarOrSet(key interface{}, value interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSet(key, value))
|
||||
}
|
||||
|
||||
// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVarOrSetFunc returns a Var with result from GetOrSetFunc.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *AnyAnyMap) GetVarOrSetFunc(key interface{}, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFunc(key, f))
|
||||
}
|
||||
|
||||
// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVarOrSetFuncLock returns a Var with result from GetOrSetFuncLock.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *AnyAnyMap) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFuncLock(key, f))
|
||||
}
|
||||
@ -293,21 +321,25 @@ func (m *AnyAnyMap) SetIfNotExistFuncLock(key interface{}, f func() interface{})
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (m *AnyAnyMap) Remove(key interface{}) interface{} {
|
||||
func (m *AnyAnyMap) Remove(key interface{}) (value interface{}) {
|
||||
m.mu.Lock()
|
||||
val, exists := m.data[key]
|
||||
if exists {
|
||||
delete(m.data, key)
|
||||
if m.data != nil {
|
||||
var ok bool
|
||||
if value, ok = m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return val
|
||||
return
|
||||
}
|
||||
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *AnyAnyMap) Removes(keys []interface{}) {
|
||||
m.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
if m.data != nil {
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
@ -315,36 +347,43 @@ func (m *AnyAnyMap) Removes(keys []interface{}) {
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (m *AnyAnyMap) Keys() []interface{} {
|
||||
m.mu.RLock()
|
||||
keys := make([]interface{}, len(m.data))
|
||||
index := 0
|
||||
defer m.mu.RUnlock()
|
||||
var (
|
||||
keys = make([]interface{}, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for key := range m.data {
|
||||
keys[index] = key
|
||||
index++
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *AnyAnyMap) Values() []interface{} {
|
||||
m.mu.RLock()
|
||||
values := make([]interface{}, len(m.data))
|
||||
index := 0
|
||||
defer m.mu.RUnlock()
|
||||
var (
|
||||
values = make([]interface{}, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for _, value := range m.data {
|
||||
values[index] = value
|
||||
index++
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return values
|
||||
}
|
||||
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (m *AnyAnyMap) Contains(key interface{}) bool {
|
||||
var ok bool
|
||||
m.mu.RLock()
|
||||
_, exists := m.data[key]
|
||||
if m.data != nil {
|
||||
_, ok = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return exists
|
||||
return ok
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
@ -405,6 +444,10 @@ func (m *AnyAnyMap) Flip() {
|
||||
func (m *AnyAnyMap) Merge(other *AnyAnyMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = other.MapCopy()
|
||||
return
|
||||
}
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
@ -414,6 +457,12 @@ func (m *AnyAnyMap) Merge(other *AnyAnyMap) {
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the map as a string.
|
||||
func (m *AnyAnyMap) String() string {
|
||||
b, _ := m.MarshalJSON()
|
||||
return gconv.UnsafeBytesToStr(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m *AnyAnyMap) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(gconv.Map(m.Map()))
|
||||
@ -421,12 +470,11 @@ func (m *AnyAnyMap) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (m *AnyAnyMap) UnmarshalJSON(b []byte) error {
|
||||
if m.mu == nil {
|
||||
m.mu = rwmutex.New()
|
||||
m.data = make(map[interface{}]interface{})
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[interface{}]interface{})
|
||||
}
|
||||
var data map[string]interface{}
|
||||
if err := json.Unmarshal(b, &data); err != nil {
|
||||
return err
|
||||
@ -439,12 +487,11 @@ func (m *AnyAnyMap) UnmarshalJSON(b []byte) error {
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for map.
|
||||
func (m *AnyAnyMap) UnmarshalValue(value interface{}) (err error) {
|
||||
if m.mu == nil {
|
||||
m.mu = rwmutex.New()
|
||||
m.data = make(map[interface{}]interface{})
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[interface{}]interface{})
|
||||
}
|
||||
for k, v := range gconv.Map(value) {
|
||||
m.data[k] = v
|
||||
}
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
|
||||
@ -18,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
type IntAnyMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
data map[int]interface{}
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ type IntAnyMap struct {
|
||||
// which is false in default.
|
||||
func NewIntAnyMap(safe ...bool) *IntAnyMap {
|
||||
return &IntAnyMap{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: make(map[int]interface{}),
|
||||
}
|
||||
}
|
||||
@ -37,12 +37,12 @@ func NewIntAnyMap(safe ...bool) *IntAnyMap {
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewIntAnyMapFrom(data map[int]interface{}, safe ...bool) *IntAnyMap {
|
||||
return &IntAnyMap{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// Iterator iterates the hash map readonly with custom callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (m *IntAnyMap) Iterator(f func(k int, v interface{}) bool) {
|
||||
m.mu.RLock()
|
||||
@ -98,6 +98,7 @@ func (m *IntAnyMap) MapCopy() map[int]interface{} {
|
||||
}
|
||||
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (m *IntAnyMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
for k, v := range m.data {
|
||||
@ -108,9 +109,23 @@ func (m *IntAnyMap) FilterEmpty() {
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// FilterNil deletes all key-value pair of which the value is nil.
|
||||
func (m *IntAnyMap) FilterNil() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for k, v := range m.data {
|
||||
if empty.IsNil(v) {
|
||||
delete(m.data, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *IntAnyMap) Set(key int, val interface{}) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]interface{})
|
||||
}
|
||||
m.data[key] = val
|
||||
m.mu.Unlock()
|
||||
}
|
||||
@ -118,8 +133,12 @@ func (m *IntAnyMap) Set(key int, val interface{}) {
|
||||
// Sets batch sets key-values to the hash map.
|
||||
func (m *IntAnyMap) Sets(data map[int]interface{}) {
|
||||
m.mu.Lock()
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
if m.data == nil {
|
||||
m.data = data
|
||||
} else {
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
@ -128,17 +147,21 @@ func (m *IntAnyMap) Sets(data map[int]interface{}) {
|
||||
// Second return parameter <found> is true if key was found, otherwise false.
|
||||
func (m *IntAnyMap) Search(key int) (value interface{}, found bool) {
|
||||
m.mu.RLock()
|
||||
value, found = m.data[key]
|
||||
if m.data != nil {
|
||||
value, found = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (m *IntAnyMap) Get(key int) interface{} {
|
||||
func (m *IntAnyMap) Get(key int) (value interface{}) {
|
||||
m.mu.RLock()
|
||||
val, _ := m.data[key]
|
||||
if m.data != nil {
|
||||
value, _ = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return val
|
||||
return
|
||||
}
|
||||
|
||||
// Pop retrieves and deletes an item from the map.
|
||||
@ -163,8 +186,10 @@ func (m *IntAnyMap) Pops(size int) map[int]interface{} {
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
index := 0
|
||||
newMap := make(map[int]interface{}, size)
|
||||
var (
|
||||
index = 0
|
||||
newMap = make(map[int]interface{}, size)
|
||||
)
|
||||
for k, v := range m.data {
|
||||
delete(m.data, k)
|
||||
newMap[k] = v
|
||||
@ -188,6 +213,9 @@ func (m *IntAnyMap) Pops(size int) map[int]interface{} {
|
||||
func (m *IntAnyMap) doSetWithLockCheck(key int, value interface{}) interface{} {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]interface{})
|
||||
}
|
||||
if v, ok := m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
@ -233,26 +261,26 @@ func (m *IntAnyMap) GetOrSetFuncLock(key int, f func() interface{}) interface{}
|
||||
}
|
||||
}
|
||||
|
||||
// GetVar returns a gvar.Var with the value by given <key>.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVar returns a Var with the value by given <key>.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *IntAnyMap) GetVar(key int) *gvar.Var {
|
||||
return gvar.New(m.Get(key))
|
||||
}
|
||||
|
||||
// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVarOrSet returns a Var with result from GetVarOrSet.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *IntAnyMap) GetVarOrSet(key int, value interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSet(key, value))
|
||||
}
|
||||
|
||||
// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVarOrSetFunc returns a Var with result from GetOrSetFunc.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *IntAnyMap) GetVarOrSetFunc(key int, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFunc(key, f))
|
||||
}
|
||||
|
||||
// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVarOrSetFuncLock returns a Var with result from GetOrSetFuncLock.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *IntAnyMap) GetVarOrSetFuncLock(key int, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFuncLock(key, f))
|
||||
}
|
||||
@ -293,28 +321,34 @@ func (m *IntAnyMap) SetIfNotExistFuncLock(key int, f func() interface{}) bool {
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *IntAnyMap) Removes(keys []int) {
|
||||
m.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
if m.data != nil {
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (m *IntAnyMap) Remove(key int) interface{} {
|
||||
func (m *IntAnyMap) Remove(key int) (value interface{}) {
|
||||
m.mu.Lock()
|
||||
val, exists := m.data[key]
|
||||
if exists {
|
||||
delete(m.data, key)
|
||||
if m.data != nil {
|
||||
var ok bool
|
||||
if value, ok = m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return val
|
||||
return
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (m *IntAnyMap) Keys() []int {
|
||||
m.mu.RLock()
|
||||
keys := make([]int, len(m.data))
|
||||
index := 0
|
||||
var (
|
||||
keys = make([]int, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for key := range m.data {
|
||||
keys[index] = key
|
||||
index++
|
||||
@ -326,8 +360,10 @@ func (m *IntAnyMap) Keys() []int {
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *IntAnyMap) Values() []interface{} {
|
||||
m.mu.RLock()
|
||||
values := make([]interface{}, len(m.data))
|
||||
index := 0
|
||||
var (
|
||||
values = make([]interface{}, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for _, value := range m.data {
|
||||
values[index] = value
|
||||
index++
|
||||
@ -339,10 +375,13 @@ func (m *IntAnyMap) Values() []interface{} {
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (m *IntAnyMap) Contains(key int) bool {
|
||||
var ok bool
|
||||
m.mu.RLock()
|
||||
_, exists := m.data[key]
|
||||
if m.data != nil {
|
||||
_, ok = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return exists
|
||||
return ok
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
@ -403,6 +442,10 @@ func (m *IntAnyMap) Flip() {
|
||||
func (m *IntAnyMap) Merge(other *IntAnyMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = other.MapCopy()
|
||||
return
|
||||
}
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
@ -412,6 +455,12 @@ func (m *IntAnyMap) Merge(other *IntAnyMap) {
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the map as a string.
|
||||
func (m *IntAnyMap) String() string {
|
||||
b, _ := m.MarshalJSON()
|
||||
return gconv.UnsafeBytesToStr(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m *IntAnyMap) MarshalJSON() ([]byte, error) {
|
||||
m.mu.RLock()
|
||||
@ -421,12 +470,11 @@ func (m *IntAnyMap) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (m *IntAnyMap) UnmarshalJSON(b []byte) error {
|
||||
if m.mu == nil {
|
||||
m.mu = rwmutex.New()
|
||||
m.data = make(map[int]interface{})
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]interface{})
|
||||
}
|
||||
if err := json.Unmarshal(b, &m.data); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -435,12 +483,11 @@ func (m *IntAnyMap) UnmarshalJSON(b []byte) error {
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for map.
|
||||
func (m *IntAnyMap) UnmarshalValue(value interface{}) (err error) {
|
||||
if m.mu == nil {
|
||||
m.mu = rwmutex.New()
|
||||
m.data = make(map[int]interface{})
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]interface{})
|
||||
}
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
return json.Unmarshal(gconv.Bytes(value), &m.data)
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
@ -16,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
type IntIntMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
data map[int]int
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ type IntIntMap struct {
|
||||
// which is false in default.
|
||||
func NewIntIntMap(safe ...bool) *IntIntMap {
|
||||
return &IntIntMap{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: make(map[int]int),
|
||||
}
|
||||
}
|
||||
@ -35,12 +35,12 @@ func NewIntIntMap(safe ...bool) *IntIntMap {
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewIntIntMapFrom(data map[int]int, safe ...bool) *IntIntMap {
|
||||
return &IntIntMap{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// Iterator iterates the hash map readonly with custom callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (m *IntIntMap) Iterator(f func(k int, v int) bool) {
|
||||
m.mu.RLock()
|
||||
@ -96,6 +96,7 @@ func (m *IntIntMap) MapCopy() map[int]int {
|
||||
}
|
||||
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (m *IntIntMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
for k, v := range m.data {
|
||||
@ -109,6 +110,9 @@ func (m *IntIntMap) FilterEmpty() {
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *IntIntMap) Set(key int, val int) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]int)
|
||||
}
|
||||
m.data[key] = val
|
||||
m.mu.Unlock()
|
||||
}
|
||||
@ -116,8 +120,12 @@ func (m *IntIntMap) Set(key int, val int) {
|
||||
// Sets batch sets key-values to the hash map.
|
||||
func (m *IntIntMap) Sets(data map[int]int) {
|
||||
m.mu.Lock()
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
if m.data == nil {
|
||||
m.data = data
|
||||
} else {
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
@ -126,17 +134,21 @@ func (m *IntIntMap) Sets(data map[int]int) {
|
||||
// Second return parameter <found> is true if key was found, otherwise false.
|
||||
func (m *IntIntMap) Search(key int) (value int, found bool) {
|
||||
m.mu.RLock()
|
||||
value, found = m.data[key]
|
||||
if m.data != nil {
|
||||
value, found = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (m *IntIntMap) Get(key int) int {
|
||||
func (m *IntIntMap) Get(key int) (value int) {
|
||||
m.mu.RLock()
|
||||
val, _ := m.data[key]
|
||||
if m.data != nil {
|
||||
value, _ = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return val
|
||||
return
|
||||
}
|
||||
|
||||
// Pop retrieves and deletes an item from the map.
|
||||
@ -161,8 +173,10 @@ func (m *IntIntMap) Pops(size int) map[int]int {
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
index := 0
|
||||
newMap := make(map[int]int, size)
|
||||
var (
|
||||
index = 0
|
||||
newMap = make(map[int]int, size)
|
||||
)
|
||||
for k, v := range m.data {
|
||||
delete(m.data, k)
|
||||
newMap[k] = v
|
||||
@ -181,12 +195,14 @@ func (m *IntIntMap) Pops(size int) map[int]int {
|
||||
// It returns value with given <key>.
|
||||
func (m *IntIntMap) doSetWithLockCheck(key int, value int) int {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]int)
|
||||
}
|
||||
if v, ok := m.data[key]; ok {
|
||||
m.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
m.data[key] = value
|
||||
m.mu.Unlock()
|
||||
return value
|
||||
}
|
||||
|
||||
@ -219,6 +235,9 @@ func (m *IntIntMap) GetOrSetFuncLock(key int, f func() int) int {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]int)
|
||||
}
|
||||
if v, ok = m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
@ -259,6 +278,9 @@ func (m *IntIntMap) SetIfNotExistFuncLock(key int, f func() int) bool {
|
||||
if !m.Contains(key) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]int)
|
||||
}
|
||||
if _, ok := m.data[key]; !ok {
|
||||
m.data[key] = f()
|
||||
}
|
||||
@ -270,28 +292,34 @@ func (m *IntIntMap) SetIfNotExistFuncLock(key int, f func() int) bool {
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *IntIntMap) Removes(keys []int) {
|
||||
m.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
if m.data != nil {
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (m *IntIntMap) Remove(key int) int {
|
||||
func (m *IntIntMap) Remove(key int) (value int) {
|
||||
m.mu.Lock()
|
||||
val, exists := m.data[key]
|
||||
if exists {
|
||||
delete(m.data, key)
|
||||
if m.data != nil {
|
||||
var ok bool
|
||||
if value, ok = m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return val
|
||||
return
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (m *IntIntMap) Keys() []int {
|
||||
m.mu.RLock()
|
||||
keys := make([]int, len(m.data))
|
||||
index := 0
|
||||
var (
|
||||
keys = make([]int, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for key := range m.data {
|
||||
keys[index] = key
|
||||
index++
|
||||
@ -303,8 +331,10 @@ func (m *IntIntMap) Keys() []int {
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *IntIntMap) Values() []int {
|
||||
m.mu.RLock()
|
||||
values := make([]int, len(m.data))
|
||||
index := 0
|
||||
var (
|
||||
values = make([]int, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for _, value := range m.data {
|
||||
values[index] = value
|
||||
index++
|
||||
@ -316,10 +346,13 @@ func (m *IntIntMap) Values() []int {
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (m *IntIntMap) Contains(key int) bool {
|
||||
var ok bool
|
||||
m.mu.RLock()
|
||||
_, exists := m.data[key]
|
||||
if m.data != nil {
|
||||
_, ok = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return exists
|
||||
return ok
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
@ -380,6 +413,10 @@ func (m *IntIntMap) Flip() {
|
||||
func (m *IntIntMap) Merge(other *IntIntMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = other.MapCopy()
|
||||
return
|
||||
}
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
@ -389,6 +426,12 @@ func (m *IntIntMap) Merge(other *IntIntMap) {
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the map as a string.
|
||||
func (m *IntIntMap) String() string {
|
||||
b, _ := m.MarshalJSON()
|
||||
return gconv.UnsafeBytesToStr(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m *IntIntMap) MarshalJSON() ([]byte, error) {
|
||||
m.mu.RLock()
|
||||
@ -398,12 +441,11 @@ func (m *IntIntMap) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (m *IntIntMap) UnmarshalJSON(b []byte) error {
|
||||
if m.mu == nil {
|
||||
m.mu = rwmutex.New()
|
||||
m.data = make(map[int]int)
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]int)
|
||||
}
|
||||
if err := json.Unmarshal(b, &m.data); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -412,12 +454,11 @@ func (m *IntIntMap) UnmarshalJSON(b []byte) error {
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for map.
|
||||
func (m *IntIntMap) UnmarshalValue(value interface{}) (err error) {
|
||||
if m.mu == nil {
|
||||
m.mu = rwmutex.New()
|
||||
m.data = make(map[int]int)
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]int)
|
||||
}
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
return json.Unmarshal(gconv.Bytes(value), &m.data)
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
|
||||
@ -16,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
type IntStrMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
data map[int]string
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ type IntStrMap struct {
|
||||
// which is false in default.
|
||||
func NewIntStrMap(safe ...bool) *IntStrMap {
|
||||
return &IntStrMap{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: make(map[int]string),
|
||||
}
|
||||
}
|
||||
@ -35,12 +35,12 @@ func NewIntStrMap(safe ...bool) *IntStrMap {
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewIntStrMapFrom(data map[int]string, safe ...bool) *IntStrMap {
|
||||
return &IntStrMap{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// Iterator iterates the hash map readonly with custom callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (m *IntStrMap) Iterator(f func(k int, v string) bool) {
|
||||
m.mu.RLock()
|
||||
@ -96,6 +96,7 @@ func (m *IntStrMap) MapCopy() map[int]string {
|
||||
}
|
||||
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (m *IntStrMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
for k, v := range m.data {
|
||||
@ -109,6 +110,9 @@ func (m *IntStrMap) FilterEmpty() {
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *IntStrMap) Set(key int, val string) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]string)
|
||||
}
|
||||
m.data[key] = val
|
||||
m.mu.Unlock()
|
||||
}
|
||||
@ -116,8 +120,12 @@ func (m *IntStrMap) Set(key int, val string) {
|
||||
// Sets batch sets key-values to the hash map.
|
||||
func (m *IntStrMap) Sets(data map[int]string) {
|
||||
m.mu.Lock()
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
if m.data == nil {
|
||||
m.data = data
|
||||
} else {
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
@ -126,17 +134,21 @@ func (m *IntStrMap) Sets(data map[int]string) {
|
||||
// Second return parameter <found> is true if key was found, otherwise false.
|
||||
func (m *IntStrMap) Search(key int) (value string, found bool) {
|
||||
m.mu.RLock()
|
||||
value, found = m.data[key]
|
||||
if m.data != nil {
|
||||
value, found = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (m *IntStrMap) Get(key int) string {
|
||||
func (m *IntStrMap) Get(key int) (value string) {
|
||||
m.mu.RLock()
|
||||
val, _ := m.data[key]
|
||||
if m.data != nil {
|
||||
value, _ = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return val
|
||||
return
|
||||
}
|
||||
|
||||
// Pop retrieves and deletes an item from the map.
|
||||
@ -161,8 +173,10 @@ func (m *IntStrMap) Pops(size int) map[int]string {
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
index := 0
|
||||
newMap := make(map[int]string, size)
|
||||
var (
|
||||
index = 0
|
||||
newMap = make(map[int]string, size)
|
||||
)
|
||||
for k, v := range m.data {
|
||||
delete(m.data, k)
|
||||
newMap[k] = v
|
||||
@ -182,6 +196,9 @@ func (m *IntStrMap) Pops(size int) map[int]string {
|
||||
func (m *IntStrMap) doSetWithLockCheck(key int, value string) string {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]string)
|
||||
}
|
||||
if v, ok := m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
@ -218,13 +235,14 @@ func (m *IntStrMap) GetOrSetFuncLock(key int, f func() string) string {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]string)
|
||||
}
|
||||
if v, ok = m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
v = f()
|
||||
if v != "" {
|
||||
m.data[key] = v
|
||||
}
|
||||
m.data[key] = v
|
||||
return v
|
||||
} else {
|
||||
return v
|
||||
@ -260,6 +278,9 @@ func (m *IntStrMap) SetIfNotExistFuncLock(key int, f func() string) bool {
|
||||
if !m.Contains(key) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]string)
|
||||
}
|
||||
if _, ok := m.data[key]; !ok {
|
||||
m.data[key] = f()
|
||||
}
|
||||
@ -271,28 +292,34 @@ func (m *IntStrMap) SetIfNotExistFuncLock(key int, f func() string) bool {
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *IntStrMap) Removes(keys []int) {
|
||||
m.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
if m.data != nil {
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (m *IntStrMap) Remove(key int) string {
|
||||
func (m *IntStrMap) Remove(key int) (value string) {
|
||||
m.mu.Lock()
|
||||
val, exists := m.data[key]
|
||||
if exists {
|
||||
delete(m.data, key)
|
||||
if m.data != nil {
|
||||
var ok bool
|
||||
if value, ok = m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return val
|
||||
return
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (m *IntStrMap) Keys() []int {
|
||||
m.mu.RLock()
|
||||
keys := make([]int, len(m.data))
|
||||
index := 0
|
||||
var (
|
||||
keys = make([]int, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for key := range m.data {
|
||||
keys[index] = key
|
||||
index++
|
||||
@ -304,8 +331,10 @@ func (m *IntStrMap) Keys() []int {
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *IntStrMap) Values() []string {
|
||||
m.mu.RLock()
|
||||
values := make([]string, len(m.data))
|
||||
index := 0
|
||||
var (
|
||||
values = make([]string, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for _, value := range m.data {
|
||||
values[index] = value
|
||||
index++
|
||||
@ -317,10 +346,13 @@ func (m *IntStrMap) Values() []string {
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (m *IntStrMap) Contains(key int) bool {
|
||||
var ok bool
|
||||
m.mu.RLock()
|
||||
_, exists := m.data[key]
|
||||
if m.data != nil {
|
||||
_, ok = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return exists
|
||||
return ok
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
@ -381,6 +413,10 @@ func (m *IntStrMap) Flip() {
|
||||
func (m *IntStrMap) Merge(other *IntStrMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = other.MapCopy()
|
||||
return
|
||||
}
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
@ -390,6 +426,12 @@ func (m *IntStrMap) Merge(other *IntStrMap) {
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the map as a string.
|
||||
func (m *IntStrMap) String() string {
|
||||
b, _ := m.MarshalJSON()
|
||||
return gconv.UnsafeBytesToStr(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m *IntStrMap) MarshalJSON() ([]byte, error) {
|
||||
m.mu.RLock()
|
||||
@ -399,12 +441,11 @@ func (m *IntStrMap) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (m *IntStrMap) UnmarshalJSON(b []byte) error {
|
||||
if m.mu == nil {
|
||||
m.mu = rwmutex.New()
|
||||
m.data = make(map[int]string)
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]string)
|
||||
}
|
||||
if err := json.Unmarshal(b, &m.data); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -413,12 +454,11 @@ func (m *IntStrMap) UnmarshalJSON(b []byte) error {
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for map.
|
||||
func (m *IntStrMap) UnmarshalValue(value interface{}) (err error) {
|
||||
if m.mu == nil {
|
||||
m.mu = rwmutex.New()
|
||||
m.data = make(map[int]string)
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[int]string)
|
||||
}
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
return json.Unmarshal(gconv.Bytes(value), &m.data)
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
|
||||
@ -18,7 +18,7 @@ import (
|
||||
)
|
||||
|
||||
type StrAnyMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
data map[string]interface{}
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ type StrAnyMap struct {
|
||||
// which is false in default.
|
||||
func NewStrAnyMap(safe ...bool) *StrAnyMap {
|
||||
return &StrAnyMap{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: make(map[string]interface{}),
|
||||
}
|
||||
}
|
||||
@ -37,12 +37,12 @@ func NewStrAnyMap(safe ...bool) *StrAnyMap {
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewStrAnyMapFrom(data map[string]interface{}, safe ...bool) *StrAnyMap {
|
||||
return &StrAnyMap{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// Iterator iterates the hash map readonly with custom callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (m *StrAnyMap) Iterator(f func(k string, v interface{}) bool) {
|
||||
m.mu.RLock()
|
||||
@ -92,6 +92,7 @@ func (m *StrAnyMap) MapCopy() map[string]interface{} {
|
||||
}
|
||||
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (m *StrAnyMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
for k, v := range m.data {
|
||||
@ -102,9 +103,23 @@ func (m *StrAnyMap) FilterEmpty() {
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// FilterNil deletes all key-value pair of which the value is nil.
|
||||
func (m *StrAnyMap) FilterNil() {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for k, v := range m.data {
|
||||
if empty.IsNil(v) {
|
||||
delete(m.data, k)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *StrAnyMap) Set(key string, val interface{}) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]interface{})
|
||||
}
|
||||
m.data[key] = val
|
||||
m.mu.Unlock()
|
||||
}
|
||||
@ -112,8 +127,12 @@ func (m *StrAnyMap) Set(key string, val interface{}) {
|
||||
// Sets batch sets key-values to the hash map.
|
||||
func (m *StrAnyMap) Sets(data map[string]interface{}) {
|
||||
m.mu.Lock()
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
if m.data == nil {
|
||||
m.data = data
|
||||
} else {
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
@ -122,17 +141,21 @@ func (m *StrAnyMap) Sets(data map[string]interface{}) {
|
||||
// Second return parameter <found> is true if key was found, otherwise false.
|
||||
func (m *StrAnyMap) Search(key string) (value interface{}, found bool) {
|
||||
m.mu.RLock()
|
||||
value, found = m.data[key]
|
||||
if m.data != nil {
|
||||
value, found = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (m *StrAnyMap) Get(key string) interface{} {
|
||||
func (m *StrAnyMap) Get(key string) (value interface{}) {
|
||||
m.mu.RLock()
|
||||
val, _ := m.data[key]
|
||||
if m.data != nil {
|
||||
value, _ = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return val
|
||||
return
|
||||
}
|
||||
|
||||
// Pop retrieves and deletes an item from the map.
|
||||
@ -157,8 +180,10 @@ func (m *StrAnyMap) Pops(size int) map[string]interface{} {
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
index := 0
|
||||
newMap := make(map[string]interface{}, size)
|
||||
var (
|
||||
index = 0
|
||||
newMap = make(map[string]interface{}, size)
|
||||
)
|
||||
for k, v := range m.data {
|
||||
delete(m.data, k)
|
||||
newMap[k] = v
|
||||
@ -182,6 +207,9 @@ func (m *StrAnyMap) Pops(size int) map[string]interface{} {
|
||||
func (m *StrAnyMap) doSetWithLockCheck(key string, value interface{}) interface{} {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]interface{})
|
||||
}
|
||||
if v, ok := m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
@ -229,26 +257,26 @@ func (m *StrAnyMap) GetOrSetFuncLock(key string, f func() interface{}) interface
|
||||
}
|
||||
}
|
||||
|
||||
// GetVar returns a gvar.Var with the value by given <key>.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVar returns a Var with the value by given <key>.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *StrAnyMap) GetVar(key string) *gvar.Var {
|
||||
return gvar.New(m.Get(key))
|
||||
}
|
||||
|
||||
// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVarOrSet returns a Var with result from GetVarOrSet.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *StrAnyMap) GetVarOrSet(key string, value interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSet(key, value))
|
||||
}
|
||||
|
||||
// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVarOrSetFunc returns a Var with result from GetOrSetFunc.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *StrAnyMap) GetVarOrSetFunc(key string, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFunc(key, f))
|
||||
}
|
||||
|
||||
// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVarOrSetFuncLock returns a Var with result from GetOrSetFuncLock.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *StrAnyMap) GetVarOrSetFuncLock(key string, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFuncLock(key, f))
|
||||
}
|
||||
@ -289,28 +317,34 @@ func (m *StrAnyMap) SetIfNotExistFuncLock(key string, f func() interface{}) bool
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *StrAnyMap) Removes(keys []string) {
|
||||
m.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
if m.data != nil {
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (m *StrAnyMap) Remove(key string) interface{} {
|
||||
func (m *StrAnyMap) Remove(key string) (value interface{}) {
|
||||
m.mu.Lock()
|
||||
val, exists := m.data[key]
|
||||
if exists {
|
||||
delete(m.data, key)
|
||||
if m.data != nil {
|
||||
var ok bool
|
||||
if value, ok = m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return val
|
||||
return
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (m *StrAnyMap) Keys() []string {
|
||||
m.mu.RLock()
|
||||
keys := make([]string, len(m.data))
|
||||
index := 0
|
||||
var (
|
||||
keys = make([]string, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for key := range m.data {
|
||||
keys[index] = key
|
||||
index++
|
||||
@ -322,8 +356,10 @@ func (m *StrAnyMap) Keys() []string {
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *StrAnyMap) Values() []interface{} {
|
||||
m.mu.RLock()
|
||||
values := make([]interface{}, len(m.data))
|
||||
index := 0
|
||||
var (
|
||||
values = make([]interface{}, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for _, value := range m.data {
|
||||
values[index] = value
|
||||
index++
|
||||
@ -335,10 +371,13 @@ func (m *StrAnyMap) Values() []interface{} {
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (m *StrAnyMap) Contains(key string) bool {
|
||||
var ok bool
|
||||
m.mu.RLock()
|
||||
_, exists := m.data[key]
|
||||
if m.data != nil {
|
||||
_, ok = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return exists
|
||||
return ok
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
@ -399,6 +438,10 @@ func (m *StrAnyMap) Flip() {
|
||||
func (m *StrAnyMap) Merge(other *StrAnyMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = other.MapCopy()
|
||||
return
|
||||
}
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
@ -408,6 +451,12 @@ func (m *StrAnyMap) Merge(other *StrAnyMap) {
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the map as a string.
|
||||
func (m *StrAnyMap) String() string {
|
||||
b, _ := m.MarshalJSON()
|
||||
return gconv.UnsafeBytesToStr(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m *StrAnyMap) MarshalJSON() ([]byte, error) {
|
||||
m.mu.RLock()
|
||||
@ -417,12 +466,11 @@ func (m *StrAnyMap) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (m *StrAnyMap) UnmarshalJSON(b []byte) error {
|
||||
if m.mu == nil {
|
||||
m.mu = rwmutex.New()
|
||||
m.data = make(map[string]interface{})
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]interface{})
|
||||
}
|
||||
if err := json.Unmarshal(b, &m.data); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -431,10 +479,6 @@ func (m *StrAnyMap) UnmarshalJSON(b []byte) error {
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for map.
|
||||
func (m *StrAnyMap) UnmarshalValue(value interface{}) (err error) {
|
||||
if m.mu == nil {
|
||||
m.mu = rwmutex.New()
|
||||
m.data = make(map[string]interface{})
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.data = gconv.Map(value)
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
"github.com/gogf/gf/internal/rwmutex"
|
||||
@ -16,7 +16,7 @@ import (
|
||||
)
|
||||
|
||||
type StrIntMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
data map[string]int
|
||||
}
|
||||
|
||||
@ -25,7 +25,7 @@ type StrIntMap struct {
|
||||
// which is false in default.
|
||||
func NewStrIntMap(safe ...bool) *StrIntMap {
|
||||
return &StrIntMap{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: make(map[string]int),
|
||||
}
|
||||
}
|
||||
@ -35,12 +35,12 @@ func NewStrIntMap(safe ...bool) *StrIntMap {
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewStrIntMapFrom(data map[string]int, safe ...bool) *StrIntMap {
|
||||
return &StrIntMap{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// Iterator iterates the hash map readonly with custom callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (m *StrIntMap) Iterator(f func(k string, v int) bool) {
|
||||
m.mu.RLock()
|
||||
@ -76,11 +76,11 @@ func (m *StrIntMap) Map() map[string]int {
|
||||
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
|
||||
func (m *StrIntMap) MapStrAny() map[string]interface{} {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
data := make(map[string]interface{}, len(m.data))
|
||||
for k, v := range m.data {
|
||||
data[k] = v
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return data
|
||||
}
|
||||
|
||||
@ -96,6 +96,7 @@ func (m *StrIntMap) MapCopy() map[string]int {
|
||||
}
|
||||
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (m *StrIntMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
for k, v := range m.data {
|
||||
@ -109,6 +110,9 @@ func (m *StrIntMap) FilterEmpty() {
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *StrIntMap) Set(key string, val int) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]int)
|
||||
}
|
||||
m.data[key] = val
|
||||
m.mu.Unlock()
|
||||
}
|
||||
@ -116,8 +120,12 @@ func (m *StrIntMap) Set(key string, val int) {
|
||||
// Sets batch sets key-values to the hash map.
|
||||
func (m *StrIntMap) Sets(data map[string]int) {
|
||||
m.mu.Lock()
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
if m.data == nil {
|
||||
m.data = data
|
||||
} else {
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
@ -126,17 +134,21 @@ func (m *StrIntMap) Sets(data map[string]int) {
|
||||
// Second return parameter <found> is true if key was found, otherwise false.
|
||||
func (m *StrIntMap) Search(key string) (value int, found bool) {
|
||||
m.mu.RLock()
|
||||
value, found = m.data[key]
|
||||
if m.data != nil {
|
||||
value, found = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (m *StrIntMap) Get(key string) int {
|
||||
func (m *StrIntMap) Get(key string) (value int) {
|
||||
m.mu.RLock()
|
||||
val, _ := m.data[key]
|
||||
if m.data != nil {
|
||||
value, _ = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return val
|
||||
return
|
||||
}
|
||||
|
||||
// Pop retrieves and deletes an item from the map.
|
||||
@ -161,8 +173,10 @@ func (m *StrIntMap) Pops(size int) map[string]int {
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
index := 0
|
||||
newMap := make(map[string]int, size)
|
||||
var (
|
||||
index = 0
|
||||
newMap = make(map[string]int, size)
|
||||
)
|
||||
for k, v := range m.data {
|
||||
delete(m.data, k)
|
||||
newMap[k] = v
|
||||
@ -181,6 +195,9 @@ func (m *StrIntMap) Pops(size int) map[string]int {
|
||||
// It returns value with given <key>.
|
||||
func (m *StrIntMap) doSetWithLockCheck(key string, value int) int {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]int)
|
||||
}
|
||||
if v, ok := m.data[key]; ok {
|
||||
m.mu.Unlock()
|
||||
return v
|
||||
@ -221,6 +238,9 @@ func (m *StrIntMap) GetOrSetFuncLock(key string, f func() int) int {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]int)
|
||||
}
|
||||
if v, ok = m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
@ -261,6 +281,9 @@ func (m *StrIntMap) SetIfNotExistFuncLock(key string, f func() int) bool {
|
||||
if !m.Contains(key) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]int)
|
||||
}
|
||||
if _, ok := m.data[key]; !ok {
|
||||
m.data[key] = f()
|
||||
}
|
||||
@ -272,28 +295,34 @@ func (m *StrIntMap) SetIfNotExistFuncLock(key string, f func() int) bool {
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *StrIntMap) Removes(keys []string) {
|
||||
m.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
if m.data != nil {
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (m *StrIntMap) Remove(key string) int {
|
||||
func (m *StrIntMap) Remove(key string) (value int) {
|
||||
m.mu.Lock()
|
||||
val, exists := m.data[key]
|
||||
if exists {
|
||||
delete(m.data, key)
|
||||
if m.data != nil {
|
||||
var ok bool
|
||||
if value, ok = m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return val
|
||||
return
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (m *StrIntMap) Keys() []string {
|
||||
m.mu.RLock()
|
||||
keys := make([]string, len(m.data))
|
||||
index := 0
|
||||
var (
|
||||
keys = make([]string, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for key := range m.data {
|
||||
keys[index] = key
|
||||
index++
|
||||
@ -305,8 +334,10 @@ func (m *StrIntMap) Keys() []string {
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *StrIntMap) Values() []int {
|
||||
m.mu.RLock()
|
||||
values := make([]int, len(m.data))
|
||||
index := 0
|
||||
var (
|
||||
values = make([]int, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for _, value := range m.data {
|
||||
values[index] = value
|
||||
index++
|
||||
@ -318,10 +349,13 @@ func (m *StrIntMap) Values() []int {
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (m *StrIntMap) Contains(key string) bool {
|
||||
var ok bool
|
||||
m.mu.RLock()
|
||||
_, exists := m.data[key]
|
||||
if m.data != nil {
|
||||
_, ok = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return exists
|
||||
return ok
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
@ -382,6 +416,10 @@ func (m *StrIntMap) Flip() {
|
||||
func (m *StrIntMap) Merge(other *StrIntMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = other.MapCopy()
|
||||
return
|
||||
}
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
@ -391,6 +429,12 @@ func (m *StrIntMap) Merge(other *StrIntMap) {
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the map as a string.
|
||||
func (m *StrIntMap) String() string {
|
||||
b, _ := m.MarshalJSON()
|
||||
return gconv.UnsafeBytesToStr(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m *StrIntMap) MarshalJSON() ([]byte, error) {
|
||||
m.mu.RLock()
|
||||
@ -400,12 +444,11 @@ func (m *StrIntMap) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (m *StrIntMap) UnmarshalJSON(b []byte) error {
|
||||
if m.mu == nil {
|
||||
m.mu = rwmutex.New()
|
||||
m.data = make(map[string]int)
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]int)
|
||||
}
|
||||
if err := json.Unmarshal(b, &m.data); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -414,12 +457,11 @@ func (m *StrIntMap) UnmarshalJSON(b []byte) error {
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for map.
|
||||
func (m *StrIntMap) UnmarshalValue(value interface{}) (err error) {
|
||||
if m.mu == nil {
|
||||
m.mu = rwmutex.New()
|
||||
m.data = make(map[string]int)
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]int)
|
||||
}
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
return json.Unmarshal(gconv.Bytes(value), &m.data)
|
||||
|
||||
@ -8,7 +8,7 @@
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
@ -17,7 +17,7 @@ import (
|
||||
)
|
||||
|
||||
type StrStrMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
data map[string]string
|
||||
}
|
||||
|
||||
@ -27,7 +27,7 @@ type StrStrMap struct {
|
||||
func NewStrStrMap(safe ...bool) *StrStrMap {
|
||||
return &StrStrMap{
|
||||
data: make(map[string]string),
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
}
|
||||
}
|
||||
|
||||
@ -36,12 +36,12 @@ func NewStrStrMap(safe ...bool) *StrStrMap {
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
func NewStrStrMapFrom(data map[string]string, safe ...bool) *StrStrMap {
|
||||
return &StrStrMap{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: data,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the hash map with custom callback function <f>.
|
||||
// Iterator iterates the hash map readonly with custom callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (m *StrStrMap) Iterator(f func(k string, v string) bool) {
|
||||
m.mu.RLock()
|
||||
@ -97,6 +97,7 @@ func (m *StrStrMap) MapCopy() map[string]string {
|
||||
}
|
||||
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
// Values like: 0, nil, false, "", len(slice/map/chan) == 0 are considered empty.
|
||||
func (m *StrStrMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
for k, v := range m.data {
|
||||
@ -110,6 +111,9 @@ func (m *StrStrMap) FilterEmpty() {
|
||||
// Set sets key-value to the hash map.
|
||||
func (m *StrStrMap) Set(key string, val string) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]string)
|
||||
}
|
||||
m.data[key] = val
|
||||
m.mu.Unlock()
|
||||
}
|
||||
@ -117,8 +121,12 @@ func (m *StrStrMap) Set(key string, val string) {
|
||||
// Sets batch sets key-values to the hash map.
|
||||
func (m *StrStrMap) Sets(data map[string]string) {
|
||||
m.mu.Lock()
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
if m.data == nil {
|
||||
m.data = data
|
||||
} else {
|
||||
for k, v := range data {
|
||||
m.data[k] = v
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
@ -127,17 +135,21 @@ func (m *StrStrMap) Sets(data map[string]string) {
|
||||
// Second return parameter <found> is true if key was found, otherwise false.
|
||||
func (m *StrStrMap) Search(key string) (value string, found bool) {
|
||||
m.mu.RLock()
|
||||
value, found = m.data[key]
|
||||
if m.data != nil {
|
||||
value, found = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
|
||||
// Get returns the value by given <key>.
|
||||
func (m *StrStrMap) Get(key string) string {
|
||||
func (m *StrStrMap) Get(key string) (value string) {
|
||||
m.mu.RLock()
|
||||
val, _ := m.data[key]
|
||||
if m.data != nil {
|
||||
value, _ = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return val
|
||||
return
|
||||
}
|
||||
|
||||
// Pop retrieves and deletes an item from the map.
|
||||
@ -162,8 +174,10 @@ func (m *StrStrMap) Pops(size int) map[string]string {
|
||||
if size == 0 {
|
||||
return nil
|
||||
}
|
||||
index := 0
|
||||
newMap := make(map[string]string, size)
|
||||
var (
|
||||
index = 0
|
||||
newMap = make(map[string]string, size)
|
||||
)
|
||||
for k, v := range m.data {
|
||||
delete(m.data, k)
|
||||
newMap[k] = v
|
||||
@ -183,6 +197,9 @@ func (m *StrStrMap) Pops(size int) map[string]string {
|
||||
func (m *StrStrMap) doSetWithLockCheck(key string, value string) string {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]string)
|
||||
}
|
||||
if v, ok := m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
@ -221,13 +238,14 @@ func (m *StrStrMap) GetOrSetFuncLock(key string, f func() string) string {
|
||||
if v, ok := m.Search(key); !ok {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]string)
|
||||
}
|
||||
if v, ok = m.data[key]; ok {
|
||||
return v
|
||||
}
|
||||
v = f()
|
||||
if v != "" {
|
||||
m.data[key] = v
|
||||
}
|
||||
m.data[key] = v
|
||||
return v
|
||||
} else {
|
||||
return v
|
||||
@ -263,6 +281,9 @@ func (m *StrStrMap) SetIfNotExistFuncLock(key string, f func() string) bool {
|
||||
if !m.Contains(key) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]string)
|
||||
}
|
||||
if _, ok := m.data[key]; !ok {
|
||||
m.data[key] = f()
|
||||
}
|
||||
@ -274,28 +295,34 @@ func (m *StrStrMap) SetIfNotExistFuncLock(key string, f func() string) bool {
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *StrStrMap) Removes(keys []string) {
|
||||
m.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
if m.data != nil {
|
||||
for _, key := range keys {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (m *StrStrMap) Remove(key string) string {
|
||||
func (m *StrStrMap) Remove(key string) (value string) {
|
||||
m.mu.Lock()
|
||||
val, exists := m.data[key]
|
||||
if exists {
|
||||
delete(m.data, key)
|
||||
if m.data != nil {
|
||||
var ok bool
|
||||
if value, ok = m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return val
|
||||
return
|
||||
}
|
||||
|
||||
// Keys returns all keys of the map as a slice.
|
||||
func (m *StrStrMap) Keys() []string {
|
||||
m.mu.RLock()
|
||||
keys := make([]string, len(m.data))
|
||||
index := 0
|
||||
var (
|
||||
keys = make([]string, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for key := range m.data {
|
||||
keys[index] = key
|
||||
index++
|
||||
@ -307,8 +334,10 @@ func (m *StrStrMap) Keys() []string {
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *StrStrMap) Values() []string {
|
||||
m.mu.RLock()
|
||||
values := make([]string, len(m.data))
|
||||
index := 0
|
||||
var (
|
||||
values = make([]string, len(m.data))
|
||||
index = 0
|
||||
)
|
||||
for _, value := range m.data {
|
||||
values[index] = value
|
||||
index++
|
||||
@ -320,10 +349,13 @@ func (m *StrStrMap) Values() []string {
|
||||
// Contains checks whether a key exists.
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (m *StrStrMap) Contains(key string) bool {
|
||||
var ok bool
|
||||
m.mu.RLock()
|
||||
_, exists := m.data[key]
|
||||
if m.data != nil {
|
||||
_, ok = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return exists
|
||||
return ok
|
||||
}
|
||||
|
||||
// Size returns the size of the map.
|
||||
@ -384,6 +416,10 @@ func (m *StrStrMap) Flip() {
|
||||
func (m *StrStrMap) Merge(other *StrStrMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = other.MapCopy()
|
||||
return
|
||||
}
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
@ -393,6 +429,12 @@ func (m *StrStrMap) Merge(other *StrStrMap) {
|
||||
}
|
||||
}
|
||||
|
||||
// String returns the map as a string.
|
||||
func (m *StrStrMap) String() string {
|
||||
b, _ := m.MarshalJSON()
|
||||
return gconv.UnsafeBytesToStr(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m *StrStrMap) MarshalJSON() ([]byte, error) {
|
||||
m.mu.RLock()
|
||||
@ -402,12 +444,11 @@ func (m *StrStrMap) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (m *StrStrMap) UnmarshalJSON(b []byte) error {
|
||||
if m.mu == nil {
|
||||
m.mu = rwmutex.New()
|
||||
m.data = make(map[string]string)
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[string]string)
|
||||
}
|
||||
if err := json.Unmarshal(b, &m.data); err != nil {
|
||||
return err
|
||||
}
|
||||
@ -416,9 +457,6 @@ func (m *StrStrMap) UnmarshalJSON(b []byte) error {
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for map.
|
||||
func (m *StrStrMap) UnmarshalValue(value interface{}) (err error) {
|
||||
if m.mu == nil {
|
||||
m.mu = rwmutex.New()
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
m.data = gconv.MapStrStr(value)
|
||||
|
||||
@ -7,7 +7,7 @@
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
|
||||
"github.com/gogf/gf/internal/empty"
|
||||
|
||||
@ -19,7 +19,7 @@ import (
|
||||
)
|
||||
|
||||
type ListMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
data map[interface{}]*glist.Element
|
||||
list *glist.List
|
||||
}
|
||||
@ -35,7 +35,7 @@ type gListMapNode struct {
|
||||
// which is false in default.
|
||||
func NewListMap(safe ...bool) *ListMap {
|
||||
return &ListMap{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: make(map[interface{}]*glist.Element),
|
||||
list: glist.New(),
|
||||
}
|
||||
@ -55,28 +55,32 @@ func (m *ListMap) Iterator(f func(key, value interface{}) bool) {
|
||||
m.IteratorAsc(f)
|
||||
}
|
||||
|
||||
// IteratorAsc iterates the map in ascending order with given callback function <f>.
|
||||
// IteratorAsc iterates the map readonly in ascending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (m *ListMap) IteratorAsc(f func(key interface{}, value interface{}) bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
node := (*gListMapNode)(nil)
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
return f(node.key, node.value)
|
||||
})
|
||||
if m.list != nil {
|
||||
node := (*gListMapNode)(nil)
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
return f(node.key, node.value)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// IteratorDesc iterates the map in descending order with given callback function <f>.
|
||||
// IteratorDesc iterates the map readonly in descending order with given callback function <f>.
|
||||
// If <f> returns true, then it continues iterating; or false to stop.
|
||||
func (m *ListMap) IteratorDesc(f func(key interface{}, value interface{}) bool) {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
node := (*gListMapNode)(nil)
|
||||
m.list.IteratorDesc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
return f(node.key, node.value)
|
||||
})
|
||||
if m.list != nil {
|
||||
node := (*gListMapNode)(nil)
|
||||
m.list.IteratorDesc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
return f(node.key, node.value)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
// Clone returns a new link map with copy of current map data.
|
||||
@ -110,13 +114,16 @@ func (m *ListMap) Replace(data map[interface{}]interface{}) {
|
||||
// Map returns a copy of the underlying data of the map.
|
||||
func (m *ListMap) Map() map[interface{}]interface{} {
|
||||
m.mu.RLock()
|
||||
node := (*gListMapNode)(nil)
|
||||
data := make(map[interface{}]interface{}, len(m.data))
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
data[node.key] = node.value
|
||||
return true
|
||||
})
|
||||
var node *gListMapNode
|
||||
var data map[interface{}]interface{}
|
||||
if m.list != nil {
|
||||
data = make(map[interface{}]interface{}, len(m.data))
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
data[node.key] = node.value
|
||||
return true
|
||||
})
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return data
|
||||
}
|
||||
@ -124,13 +131,16 @@ func (m *ListMap) Map() map[interface{}]interface{} {
|
||||
// MapStrAny returns a copy of the underlying data of the map as map[string]interface{}.
|
||||
func (m *ListMap) MapStrAny() map[string]interface{} {
|
||||
m.mu.RLock()
|
||||
node := (*gListMapNode)(nil)
|
||||
data := make(map[string]interface{}, len(m.data))
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
data[gconv.String(node.key)] = node.value
|
||||
return true
|
||||
})
|
||||
var node *gListMapNode
|
||||
var data map[string]interface{}
|
||||
if m.list != nil {
|
||||
data = make(map[string]interface{}, len(m.data))
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
data[gconv.String(node.key)] = node.value
|
||||
return true
|
||||
})
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return data
|
||||
}
|
||||
@ -138,20 +148,22 @@ func (m *ListMap) MapStrAny() map[string]interface{} {
|
||||
// FilterEmpty deletes all key-value pair of which the value is empty.
|
||||
func (m *ListMap) FilterEmpty() {
|
||||
m.mu.Lock()
|
||||
keys := make([]interface{}, 0)
|
||||
node := (*gListMapNode)(nil)
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
if empty.IsEmpty(node.value) {
|
||||
keys = append(keys, node.key)
|
||||
}
|
||||
return true
|
||||
})
|
||||
if len(keys) > 0 {
|
||||
for _, key := range keys {
|
||||
if e, ok := m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
m.list.Remove(e)
|
||||
if m.list != nil {
|
||||
keys := make([]interface{}, 0)
|
||||
node := (*gListMapNode)(nil)
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
node = e.Value.(*gListMapNode)
|
||||
if empty.IsEmpty(node.value) {
|
||||
keys = append(keys, node.key)
|
||||
}
|
||||
return true
|
||||
})
|
||||
if len(keys) > 0 {
|
||||
for _, key := range keys {
|
||||
if e, ok := m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
m.list.Remove(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -161,6 +173,10 @@ func (m *ListMap) FilterEmpty() {
|
||||
// Set sets key-value to the map.
|
||||
func (m *ListMap) Set(key interface{}, value interface{}) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[interface{}]*glist.Element)
|
||||
m.list = glist.New()
|
||||
}
|
||||
if e, ok := m.data[key]; !ok {
|
||||
m.data[key] = m.list.PushBack(&gListMapNode{key, value})
|
||||
} else {
|
||||
@ -172,6 +188,10 @@ func (m *ListMap) Set(key interface{}, value interface{}) {
|
||||
// Sets batch sets key-values to the map.
|
||||
func (m *ListMap) Sets(data map[interface{}]interface{}) {
|
||||
m.mu.Lock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[interface{}]*glist.Element)
|
||||
m.list = glist.New()
|
||||
}
|
||||
for key, value := range data {
|
||||
if e, ok := m.data[key]; !ok {
|
||||
m.data[key] = m.list.PushBack(&gListMapNode{key, value})
|
||||
@ -186,9 +206,11 @@ func (m *ListMap) Sets(data map[interface{}]interface{}) {
|
||||
// Second return parameter <found> is true if key was found, otherwise false.
|
||||
func (m *ListMap) Search(key interface{}) (value interface{}, found bool) {
|
||||
m.mu.RLock()
|
||||
if e, ok := m.data[key]; ok {
|
||||
value = e.Value.(*gListMapNode).value
|
||||
found = ok
|
||||
if m.data != nil {
|
||||
if e, ok := m.data[key]; ok {
|
||||
value = e.Value.(*gListMapNode).value
|
||||
found = ok
|
||||
}
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
@ -197,8 +219,10 @@ func (m *ListMap) Search(key interface{}) (value interface{}, found bool) {
|
||||
// Get returns the value by given <key>.
|
||||
func (m *ListMap) Get(key interface{}) (value interface{}) {
|
||||
m.mu.RLock()
|
||||
if e, ok := m.data[key]; ok {
|
||||
value = e.Value.(*gListMapNode).value
|
||||
if m.data != nil {
|
||||
if e, ok := m.data[key]; ok {
|
||||
value = e.Value.(*gListMapNode).value
|
||||
}
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
@ -255,6 +279,10 @@ func (m *ListMap) Pops(size int) map[interface{}]interface{} {
|
||||
func (m *ListMap) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[interface{}]*glist.Element)
|
||||
m.list = glist.New()
|
||||
}
|
||||
if e, ok := m.data[key]; ok {
|
||||
return e.Value.(*gListMapNode).value
|
||||
}
|
||||
@ -302,26 +330,26 @@ func (m *ListMap) GetOrSetFuncLock(key interface{}, f func() interface{}) interf
|
||||
}
|
||||
}
|
||||
|
||||
// GetVar returns a gvar.Var with the value by given <key>.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVar returns a Var with the value by given <key>.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *ListMap) GetVar(key interface{}) *gvar.Var {
|
||||
return gvar.New(m.Get(key))
|
||||
}
|
||||
|
||||
// GetVarOrSet returns a gvar.Var with result from GetVarOrSet.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVarOrSet returns a Var with result from GetVarOrSet.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *ListMap) GetVarOrSet(key interface{}, value interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSet(key, value))
|
||||
}
|
||||
|
||||
// GetVarOrSetFunc returns a gvar.Var with result from GetOrSetFunc.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVarOrSetFunc returns a Var with result from GetOrSetFunc.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *ListMap) GetVarOrSetFunc(key interface{}, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFunc(key, f))
|
||||
}
|
||||
|
||||
// GetVarOrSetFuncLock returns a gvar.Var with result from GetOrSetFuncLock.
|
||||
// The returned gvar.Var is un-concurrent safe.
|
||||
// GetVarOrSetFuncLock returns a Var with result from GetOrSetFuncLock.
|
||||
// The returned Var is un-concurrent safe.
|
||||
func (m *ListMap) GetVarOrSetFuncLock(key interface{}, f func() interface{}) *gvar.Var {
|
||||
return gvar.New(m.GetOrSetFuncLock(key, f))
|
||||
}
|
||||
@ -362,10 +390,12 @@ func (m *ListMap) SetIfNotExistFuncLock(key interface{}, f func() interface{}) b
|
||||
// Remove deletes value from map by given <key>, and return this deleted value.
|
||||
func (m *ListMap) Remove(key interface{}) (value interface{}) {
|
||||
m.mu.Lock()
|
||||
if e, ok := m.data[key]; ok {
|
||||
value = e.Value.(*gListMapNode).value
|
||||
delete(m.data, key)
|
||||
m.list.Remove(e)
|
||||
if m.data != nil {
|
||||
if e, ok := m.data[key]; ok {
|
||||
value = e.Value.(*gListMapNode).value
|
||||
delete(m.data, key)
|
||||
m.list.Remove(e)
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
return
|
||||
@ -374,10 +404,12 @@ func (m *ListMap) Remove(key interface{}) (value interface{}) {
|
||||
// Removes batch deletes values of the map by keys.
|
||||
func (m *ListMap) Removes(keys []interface{}) {
|
||||
m.mu.Lock()
|
||||
for _, key := range keys {
|
||||
if e, ok := m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
m.list.Remove(e)
|
||||
if m.data != nil {
|
||||
for _, key := range keys {
|
||||
if e, ok := m.data[key]; ok {
|
||||
delete(m.data, key)
|
||||
m.list.Remove(e)
|
||||
}
|
||||
}
|
||||
}
|
||||
m.mu.Unlock()
|
||||
@ -386,13 +418,17 @@ func (m *ListMap) Removes(keys []interface{}) {
|
||||
// Keys returns all keys of the map as a slice in ascending order.
|
||||
func (m *ListMap) Keys() []interface{} {
|
||||
m.mu.RLock()
|
||||
keys := make([]interface{}, m.list.Len())
|
||||
index := 0
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
keys[index] = e.Value.(*gListMapNode).key
|
||||
index++
|
||||
return true
|
||||
})
|
||||
var (
|
||||
keys = make([]interface{}, m.list.Len())
|
||||
index = 0
|
||||
)
|
||||
if m.list != nil {
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
keys[index] = e.Value.(*gListMapNode).key
|
||||
index++
|
||||
return true
|
||||
})
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
@ -400,13 +436,17 @@ func (m *ListMap) Keys() []interface{} {
|
||||
// Values returns all values of the map as a slice.
|
||||
func (m *ListMap) Values() []interface{} {
|
||||
m.mu.RLock()
|
||||
values := make([]interface{}, m.list.Len())
|
||||
index := 0
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
values[index] = e.Value.(*gListMapNode).value
|
||||
index++
|
||||
return true
|
||||
})
|
||||
var (
|
||||
values = make([]interface{}, m.list.Len())
|
||||
index = 0
|
||||
)
|
||||
if m.list != nil {
|
||||
m.list.IteratorAsc(func(e *glist.Element) bool {
|
||||
values[index] = e.Value.(*gListMapNode).value
|
||||
index++
|
||||
return true
|
||||
})
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return values
|
||||
}
|
||||
@ -415,7 +455,9 @@ func (m *ListMap) Values() []interface{} {
|
||||
// It returns true if the <key> exists, or else false.
|
||||
func (m *ListMap) Contains(key interface{}) (ok bool) {
|
||||
m.mu.RLock()
|
||||
_, ok = m.data[key]
|
||||
if m.data != nil {
|
||||
_, ok = m.data[key]
|
||||
}
|
||||
m.mu.RUnlock()
|
||||
return
|
||||
}
|
||||
@ -448,6 +490,10 @@ func (m *ListMap) Flip() {
|
||||
func (m *ListMap) Merge(other *ListMap) {
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[interface{}]*glist.Element)
|
||||
m.list = glist.New()
|
||||
}
|
||||
if other != m {
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
@ -464,6 +510,12 @@ func (m *ListMap) Merge(other *ListMap) {
|
||||
})
|
||||
}
|
||||
|
||||
// String returns the map as a string.
|
||||
func (m *ListMap) String() string {
|
||||
b, _ := m.MarshalJSON()
|
||||
return gconv.UnsafeBytesToStr(b)
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (m *ListMap) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(gconv.Map(m.Map()))
|
||||
@ -471,13 +523,12 @@ func (m *ListMap) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (m *ListMap) UnmarshalJSON(b []byte) error {
|
||||
if m.mu == nil {
|
||||
m.mu = rwmutex.New()
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[interface{}]*glist.Element)
|
||||
m.list = glist.New()
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
var data map[string]interface{}
|
||||
if err := json.Unmarshal(b, &data); err != nil {
|
||||
return err
|
||||
@ -494,13 +545,12 @@ func (m *ListMap) UnmarshalJSON(b []byte) error {
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for map.
|
||||
func (m *ListMap) UnmarshalValue(value interface{}) (err error) {
|
||||
if m.mu == nil {
|
||||
m.mu = rwmutex.New()
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
if m.data == nil {
|
||||
m.data = make(map[interface{}]*glist.Element)
|
||||
m.list = glist.New()
|
||||
}
|
||||
m.mu.Lock()
|
||||
defer m.mu.Unlock()
|
||||
for k, v := range gconv.Map(value) {
|
||||
if e, ok := m.data[k]; !ok {
|
||||
m.data[k] = m.list.PushBack(&gListMapNode{k, v})
|
||||
|
||||
@ -7,6 +7,7 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
@ -17,6 +18,55 @@ func getValue() interface{} {
|
||||
return 3
|
||||
}
|
||||
|
||||
func Test_Map_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.Map
|
||||
m.Set(1, 11)
|
||||
t.Assert(m.Get(1), 11)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.IntAnyMap
|
||||
m.Set(1, 11)
|
||||
t.Assert(m.Get(1), 11)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.IntIntMap
|
||||
m.Set(1, 11)
|
||||
t.Assert(m.Get(1), 11)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.IntStrMap
|
||||
m.Set(1, "11")
|
||||
t.Assert(m.Get(1), "11")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.StrAnyMap
|
||||
m.Set("1", "11")
|
||||
t.Assert(m.Get("1"), "11")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.StrStrMap
|
||||
m.Set("1", "11")
|
||||
t.Assert(m.Get("1"), "11")
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.StrIntMap
|
||||
m.Set("1", 11)
|
||||
t.Assert(m.Get("1"), 11)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.ListMap
|
||||
m.Set("1", 11)
|
||||
t.Assert(m.Get("1"), 11)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.TreeMap
|
||||
m.SetComparator(gutil.ComparatorString)
|
||||
m.Set("1", 11)
|
||||
t.Assert(m.Get("1"), 11)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_Map_Basic(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.New()
|
||||
|
||||
207
container/gmap/gmap_z_example_any_any_test.go
Normal file
207
container/gmap/gmap_z_example_any_any_test.go
Normal file
@ -0,0 +1,207 @@
|
||||
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
)
|
||||
|
||||
func ExampleNew() {
|
||||
m := gmap.New()
|
||||
|
||||
// Add data.
|
||||
m.Set("key1", "val1")
|
||||
|
||||
// Print size.
|
||||
fmt.Println(m.Size())
|
||||
|
||||
addMap := make(map[interface{}]interface{})
|
||||
addMap["key2"] = "val2"
|
||||
addMap["key3"] = "val3"
|
||||
addMap[1] = 1
|
||||
|
||||
fmt.Println(m.Values())
|
||||
|
||||
// Batch add data.
|
||||
m.Sets(addMap)
|
||||
|
||||
// Gets the value of the corresponding key.
|
||||
fmt.Println(m.Get("key3"))
|
||||
|
||||
// Get the value by key, or set it with given key-value if not exist.
|
||||
fmt.Println(m.GetOrSet("key4", "val4"))
|
||||
|
||||
// Set key-value if the key does not exist, then return true; or else return false.
|
||||
fmt.Println(m.SetIfNotExist("key3", "val3"))
|
||||
|
||||
// Remove key
|
||||
m.Remove("key2")
|
||||
fmt.Println(m.Keys())
|
||||
|
||||
// Batch remove keys.
|
||||
m.Removes([]interface{}{"key1", 1})
|
||||
fmt.Println(m.Keys())
|
||||
|
||||
// Contains checks whether a key exists.
|
||||
fmt.Println(m.Contains("key3"))
|
||||
|
||||
// Flip exchanges key-value of the map, it will change key-value to value-key.
|
||||
m.Flip()
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Clear deletes all data of the map.
|
||||
m.Clear()
|
||||
|
||||
fmt.Println(m.Size())
|
||||
|
||||
// May Output:
|
||||
// 1
|
||||
// [val1]
|
||||
// val3
|
||||
// val4
|
||||
// false
|
||||
// [key4 key1 key3 1]
|
||||
// [key4 key3]
|
||||
// true
|
||||
// map[val3:key3 val4:key4]
|
||||
// 0
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_Keys() {
|
||||
var m gmap.Map
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
fmt.Println(m.Keys())
|
||||
fmt.Println(m.Values())
|
||||
|
||||
// May Output:
|
||||
// [k1 k2 k3 k4]
|
||||
// [v2 v3 v4 v1]
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_Values() {
|
||||
var m gmap.Map
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
fmt.Println(m.Keys())
|
||||
fmt.Println(m.Values())
|
||||
|
||||
// May Output:
|
||||
// [k1 k2 k3 k4]
|
||||
// [v2 v3 v4 v1]
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_Flip() {
|
||||
var m gmap.Map
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
})
|
||||
m.Flip()
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// May Output:
|
||||
// map[v1:k1 v2:k2]
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_Pop() {
|
||||
var m gmap.Map
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
fmt.Println(m.Pop())
|
||||
fmt.Println(m.Pops(2))
|
||||
fmt.Println(m.Size())
|
||||
|
||||
// May Output:
|
||||
// k1 v1
|
||||
// map[k2:v2 k4:v4]
|
||||
// 1
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_Pops() {
|
||||
var m gmap.Map
|
||||
m.Sets(g.MapAnyAny{
|
||||
"k1": "v1",
|
||||
"k2": "v2",
|
||||
"k3": "v3",
|
||||
"k4": "v4",
|
||||
})
|
||||
fmt.Println(m.Pop())
|
||||
fmt.Println(m.Pops(2))
|
||||
fmt.Println(m.Size())
|
||||
|
||||
// May Output:
|
||||
// k1 v1
|
||||
// map[k2:v2 k4:v4]
|
||||
// 1
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_FilterEmpty() {
|
||||
m := gmap.NewFrom(g.MapAnyAny{
|
||||
"k1": "",
|
||||
"k2": nil,
|
||||
"k3": 0,
|
||||
"k4": 1,
|
||||
})
|
||||
m.FilterEmpty()
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// May Output:
|
||||
// map[k4:1]
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_FilterNil() {
|
||||
m := gmap.NewFrom(g.MapAnyAny{
|
||||
"k1": "",
|
||||
"k2": nil,
|
||||
"k3": 0,
|
||||
"k4": 1,
|
||||
})
|
||||
m.FilterNil()
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// May Output:
|
||||
// map[k1: k3:0 k4:1]
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_SetIfNotExist() {
|
||||
var m gmap.Map
|
||||
fmt.Println(m.SetIfNotExist("k1", "v1"))
|
||||
fmt.Println(m.SetIfNotExist("k1", "v1"))
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// map[k1:v1]
|
||||
}
|
||||
|
||||
func ExampleAnyAnyMap_Merge() {
|
||||
var m1, m2 gmap.Map
|
||||
m1.Set("key1", "val1")
|
||||
m2.Set("key2", "val2")
|
||||
m1.Merge(&m2)
|
||||
fmt.Println(m1.Map())
|
||||
|
||||
// May Output:
|
||||
// map[key1:val1 key2:val2]
|
||||
}
|
||||
@ -1,71 +0,0 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
)
|
||||
|
||||
func Example_normalBasic() {
|
||||
m := gmap.New()
|
||||
|
||||
//Add data
|
||||
m.Set("key1", "val1")
|
||||
|
||||
//Print size
|
||||
fmt.Println(m.Size())
|
||||
//output 1
|
||||
|
||||
add_map := make(map[interface{}]interface{})
|
||||
add_map["key2"] = "val2"
|
||||
add_map["key3"] = "val3"
|
||||
add_map[1] = 1
|
||||
|
||||
fmt.Println(m.Values())
|
||||
|
||||
//Batch add data
|
||||
m.Sets(add_map)
|
||||
|
||||
//Gets the value of the corresponding key
|
||||
key3_val := m.Get("key3")
|
||||
fmt.Println(key3_val)
|
||||
|
||||
//Get the value by key, or set it with given key-value if not exist.
|
||||
get_or_set_val := m.GetOrSet("key4", "val4")
|
||||
fmt.Println(get_or_set_val)
|
||||
|
||||
// Set key-value if the key does not exist, then return true; or else return false.
|
||||
is_set := m.SetIfNotExist("key3", "val3")
|
||||
fmt.Println(is_set)
|
||||
|
||||
//Remove key
|
||||
m.Remove("key2")
|
||||
fmt.Println(m.Keys())
|
||||
|
||||
//Batch remove keys
|
||||
remove_keys := []interface{}{"key1", 1}
|
||||
m.Removes(remove_keys)
|
||||
fmt.Println(m.Keys())
|
||||
|
||||
//Contains checks whether a key exists.
|
||||
is_contain := m.Contains("key3")
|
||||
fmt.Println(is_contain)
|
||||
|
||||
//Flip exchanges key-value of the map, it will change key-value to value-key.
|
||||
m.Flip()
|
||||
fmt.Println(m.Map())
|
||||
|
||||
// Clear deletes all data of the map,
|
||||
m.Clear()
|
||||
|
||||
fmt.Println(m.Size())
|
||||
|
||||
}
|
||||
func Example_normalMerge() {
|
||||
m1 := gmap.New()
|
||||
m2 := gmap.New()
|
||||
m1.Set("key1", "val1")
|
||||
m2.Set("key2", "val2")
|
||||
m1.Merge(m2)
|
||||
fmt.Println(m1.Map())
|
||||
}
|
||||
@ -7,18 +7,44 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"testing"
|
||||
|
||||
"github.com/gogf/gf/container/gmap"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
"time"
|
||||
)
|
||||
|
||||
func anyAnyCallBack(int, interface{}) bool {
|
||||
return true
|
||||
func Test_AnyAnyMap_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.AnyAnyMap
|
||||
m.Set(1, 1)
|
||||
|
||||
t.Assert(m.Get(1), 1)
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
t.Assert(m.GetOrSet(2, "2"), "2")
|
||||
t.Assert(m.SetIfNotExist(2, "2"), false)
|
||||
|
||||
t.Assert(m.SetIfNotExist(3, 3), true)
|
||||
|
||||
t.Assert(m.Remove(2), "2")
|
||||
t.Assert(m.Contains(2), false)
|
||||
|
||||
t.AssertIN(3, m.Keys())
|
||||
t.AssertIN(1, m.Keys())
|
||||
t.AssertIN(3, m.Values())
|
||||
t.AssertIN(1, m.Values())
|
||||
m.Flip()
|
||||
t.Assert(m.Map(), map[interface{}]int{1: 1, 3: 3})
|
||||
|
||||
m.Clear()
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_Basic(t *testing.T) {
|
||||
@ -192,6 +218,18 @@ func Test_AnyAnyMap_FilterEmpty(t *testing.T) {
|
||||
t.Assert(m.Get(1), nil)
|
||||
t.Assert(m.Get(2), 2)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewAnyAnyMap()
|
||||
m.Set(1, 0)
|
||||
m.Set("time1", time.Time{})
|
||||
m.Set("time2", time.Now())
|
||||
t.Assert(m.Get(1), 0)
|
||||
t.Assert(m.Get("time1"), time.Time{})
|
||||
m.FilterEmpty()
|
||||
t.Assert(m.Get(1), nil)
|
||||
t.Assert(m.Get("time1"), nil)
|
||||
t.AssertNE(m.Get("time2"), nil)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_AnyAnyMap_Json(t *testing.T) {
|
||||
|
||||
@ -7,9 +7,9 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"testing"
|
||||
|
||||
@ -20,9 +20,37 @@ import (
|
||||
func getAny() interface{} {
|
||||
return 123
|
||||
}
|
||||
func intAnyCallBack(int, interface{}) bool {
|
||||
return true
|
||||
|
||||
func Test_IntAnyMap_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.IntAnyMap
|
||||
m.Set(1, 1)
|
||||
|
||||
t.Assert(m.Get(1), 1)
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
t.Assert(m.GetOrSet(2, "2"), "2")
|
||||
t.Assert(m.SetIfNotExist(2, "2"), false)
|
||||
|
||||
t.Assert(m.SetIfNotExist(3, 3), true)
|
||||
|
||||
t.Assert(m.Remove(2), "2")
|
||||
t.Assert(m.Contains(2), false)
|
||||
|
||||
t.AssertIN(3, m.Keys())
|
||||
t.AssertIN(1, m.Keys())
|
||||
t.AssertIN(3, m.Values())
|
||||
t.AssertIN(1, m.Values())
|
||||
m.Flip()
|
||||
t.Assert(m.Map(), map[interface{}]int{1: 1, 3: 3})
|
||||
|
||||
m.Clear()
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntAnyMap_Basic(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntAnyMap()
|
||||
@ -55,6 +83,7 @@ func Test_IntAnyMap_Basic(t *testing.T) {
|
||||
t.Assert(m2.Map(), map[int]interface{}{1: 1, 2: "2"})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntAnyMap_Set_Fun(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntAnyMap()
|
||||
|
||||
@ -7,9 +7,9 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"testing"
|
||||
|
||||
@ -20,9 +20,41 @@ import (
|
||||
func getInt() int {
|
||||
return 123
|
||||
}
|
||||
|
||||
func intIntCallBack(int, int) bool {
|
||||
return true
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.IntIntMap
|
||||
m.Set(1, 1)
|
||||
|
||||
t.Assert(m.Get(1), 1)
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
t.Assert(m.GetOrSet(2, 2), 2)
|
||||
t.Assert(m.SetIfNotExist(2, 2), false)
|
||||
|
||||
t.Assert(m.SetIfNotExist(3, 3), true)
|
||||
|
||||
t.Assert(m.Remove(2), 2)
|
||||
t.Assert(m.Contains(2), false)
|
||||
|
||||
t.AssertIN(3, m.Keys())
|
||||
t.AssertIN(1, m.Keys())
|
||||
t.AssertIN(3, m.Values())
|
||||
t.AssertIN(1, m.Values())
|
||||
m.Flip()
|
||||
t.Assert(m.Map(), map[int]int{1: 1, 3: 3})
|
||||
|
||||
m.Clear()
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Basic(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntIntMap()
|
||||
@ -55,6 +87,7 @@ func Test_IntIntMap_Basic(t *testing.T) {
|
||||
t.Assert(m2.Map(), map[int]int{1: 1, 2: 2})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntIntMap_Set_Fun(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntIntMap()
|
||||
|
||||
@ -7,9 +7,9 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"testing"
|
||||
|
||||
@ -20,9 +20,40 @@ import (
|
||||
func getStr() string {
|
||||
return "z"
|
||||
}
|
||||
func intStrCallBack(int, string) bool {
|
||||
return true
|
||||
|
||||
func Test_IntStrMap_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.IntStrMap
|
||||
m.Set(1, "a")
|
||||
|
||||
t.Assert(m.Get(1), "a")
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
t.Assert(m.GetOrSet(2, "b"), "b")
|
||||
t.Assert(m.SetIfNotExist(2, "b"), false)
|
||||
|
||||
t.Assert(m.SetIfNotExist(3, "c"), true)
|
||||
|
||||
t.Assert(m.Remove(2), "b")
|
||||
t.Assert(m.Contains(2), false)
|
||||
|
||||
t.AssertIN(3, m.Keys())
|
||||
t.AssertIN(1, m.Keys())
|
||||
t.AssertIN("a", m.Values())
|
||||
t.AssertIN("c", m.Values())
|
||||
|
||||
m_f := gmap.NewIntStrMap()
|
||||
m_f.Set(1, "2")
|
||||
m_f.Flip()
|
||||
t.Assert(m_f.Map(), map[int]string{2: "1"})
|
||||
|
||||
m.Clear()
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntStrMap_Basic(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntStrMap()
|
||||
@ -60,6 +91,7 @@ func Test_IntStrMap_Basic(t *testing.T) {
|
||||
t.Assert(m2.Map(), map[int]string{1: "a", 2: "b"})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_IntStrMap_Set_Fun(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewIntStrMap()
|
||||
|
||||
@ -7,8 +7,8 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"testing"
|
||||
|
||||
@ -17,6 +17,38 @@ import (
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
)
|
||||
|
||||
func Test_ListMap_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.ListMap
|
||||
m.Set("key1", "val1")
|
||||
t.Assert(m.Keys(), []interface{}{"key1"})
|
||||
|
||||
t.Assert(m.Get("key1"), "val1")
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
t.Assert(m.GetOrSet("key2", "val2"), "val2")
|
||||
t.Assert(m.SetIfNotExist("key2", "val2"), false)
|
||||
|
||||
t.Assert(m.SetIfNotExist("key3", "val3"), true)
|
||||
t.Assert(m.Remove("key2"), "val2")
|
||||
t.Assert(m.Contains("key2"), false)
|
||||
|
||||
t.AssertIN("key3", m.Keys())
|
||||
t.AssertIN("key1", m.Keys())
|
||||
t.AssertIN("val3", m.Values())
|
||||
t.AssertIN("val1", m.Values())
|
||||
|
||||
m.Flip()
|
||||
|
||||
t.Assert(m.Map(), map[interface{}]interface{}{"val3": "key3", "val1": "key1"})
|
||||
|
||||
m.Clear()
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ListMap_Basic(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewListMap()
|
||||
@ -51,6 +83,7 @@ func Test_ListMap_Basic(t *testing.T) {
|
||||
t.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_ListMap_Set_Fun(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewListMap()
|
||||
|
||||
@ -7,9 +7,9 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"testing"
|
||||
|
||||
@ -17,9 +17,37 @@ import (
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
)
|
||||
|
||||
func stringAnyCallBack(string, interface{}) bool {
|
||||
return true
|
||||
func Test_StrAnyMap_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.StrAnyMap
|
||||
m.Set("a", 1)
|
||||
|
||||
t.Assert(m.Get("a"), 1)
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
t.Assert(m.GetOrSet("b", "2"), "2")
|
||||
t.Assert(m.SetIfNotExist("b", "2"), false)
|
||||
|
||||
t.Assert(m.SetIfNotExist("c", 3), true)
|
||||
|
||||
t.Assert(m.Remove("b"), "2")
|
||||
t.Assert(m.Contains("b"), false)
|
||||
|
||||
t.AssertIN("c", m.Keys())
|
||||
t.AssertIN("a", m.Keys())
|
||||
t.AssertIN(3, m.Values())
|
||||
t.AssertIN(1, m.Values())
|
||||
|
||||
m.Flip()
|
||||
t.Assert(m.Map(), map[string]interface{}{"1": "a", "3": "c"})
|
||||
|
||||
m.Clear()
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrAnyMap_Basic(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewStrAnyMap()
|
||||
@ -53,6 +81,7 @@ func Test_StrAnyMap_Basic(t *testing.T) {
|
||||
t.Assert(m2.Map(), map[string]interface{}{"a": 1, "b": "2"})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrAnyMap_Set_Fun(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewStrAnyMap()
|
||||
|
||||
@ -7,9 +7,9 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"testing"
|
||||
|
||||
@ -17,9 +17,39 @@ import (
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
)
|
||||
|
||||
func stringIntCallBack(string, int) bool {
|
||||
return true
|
||||
func Test_StrIntMap_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.StrIntMap
|
||||
m.Set("a", 1)
|
||||
|
||||
t.Assert(m.Get("a"), 1)
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
t.Assert(m.GetOrSet("b", 2), 2)
|
||||
t.Assert(m.SetIfNotExist("b", 2), false)
|
||||
|
||||
t.Assert(m.SetIfNotExist("c", 3), true)
|
||||
|
||||
t.Assert(m.Remove("b"), 2)
|
||||
t.Assert(m.Contains("b"), false)
|
||||
|
||||
t.AssertIN("c", m.Keys())
|
||||
t.AssertIN("a", m.Keys())
|
||||
t.AssertIN(3, m.Values())
|
||||
t.AssertIN(1, m.Values())
|
||||
|
||||
m_f := gmap.NewStrIntMap()
|
||||
m_f.Set("1", 2)
|
||||
m_f.Flip()
|
||||
t.Assert(m_f.Map(), map[string]int{"2": 1})
|
||||
|
||||
m.Clear()
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrIntMap_Basic(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewStrIntMap()
|
||||
@ -55,6 +85,7 @@ func Test_StrIntMap_Basic(t *testing.T) {
|
||||
t.Assert(m2.Map(), map[string]int{"a": 1, "b": 2})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrIntMap_Set_Fun(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewStrIntMap()
|
||||
|
||||
@ -7,9 +7,9 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"testing"
|
||||
|
||||
@ -17,9 +17,38 @@ import (
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
)
|
||||
|
||||
func stringStrCallBack(string, string) bool {
|
||||
return true
|
||||
func Test_StrStrMap_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.StrStrMap
|
||||
m.Set("a", "a")
|
||||
|
||||
t.Assert(m.Get("a"), "a")
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
t.Assert(m.GetOrSet("b", "b"), "b")
|
||||
t.Assert(m.SetIfNotExist("b", "b"), false)
|
||||
|
||||
t.Assert(m.SetIfNotExist("c", "c"), true)
|
||||
|
||||
t.Assert(m.Remove("b"), "b")
|
||||
t.Assert(m.Contains("b"), false)
|
||||
|
||||
t.AssertIN("c", m.Keys())
|
||||
t.AssertIN("a", m.Keys())
|
||||
t.AssertIN("a", m.Values())
|
||||
t.AssertIN("c", m.Values())
|
||||
|
||||
m.Flip()
|
||||
|
||||
t.Assert(m.Map(), map[string]string{"a": "a", "c": "c"})
|
||||
|
||||
m.Clear()
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrStrMap_Basic(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewStrStrMap()
|
||||
@ -54,6 +83,7 @@ func Test_StrStrMap_Basic(t *testing.T) {
|
||||
t.Assert(m2.Map(), map[string]string{"a": "a", "b": "b"})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_StrStrMap_Set_Fun(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewStrStrMap()
|
||||
|
||||
@ -7,8 +7,8 @@
|
||||
package gmap_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"testing"
|
||||
|
||||
@ -17,6 +17,39 @@ import (
|
||||
"github.com/gogf/gf/util/gutil"
|
||||
)
|
||||
|
||||
func Test_TreeMap_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var m gmap.TreeMap
|
||||
m.SetComparator(gutil.ComparatorString)
|
||||
m.Set("key1", "val1")
|
||||
t.Assert(m.Keys(), []interface{}{"key1"})
|
||||
|
||||
t.Assert(m.Get("key1"), "val1")
|
||||
t.Assert(m.Size(), 1)
|
||||
t.Assert(m.IsEmpty(), false)
|
||||
|
||||
t.Assert(m.GetOrSet("key2", "val2"), "val2")
|
||||
t.Assert(m.SetIfNotExist("key2", "val2"), false)
|
||||
|
||||
t.Assert(m.SetIfNotExist("key3", "val3"), true)
|
||||
|
||||
t.Assert(m.Remove("key2"), "val2")
|
||||
t.Assert(m.Contains("key2"), false)
|
||||
|
||||
t.AssertIN("key3", m.Keys())
|
||||
t.AssertIN("key1", m.Keys())
|
||||
t.AssertIN("val3", m.Values())
|
||||
t.AssertIN("val1", m.Values())
|
||||
|
||||
m.Flip()
|
||||
t.Assert(m.Map(), map[interface{}]interface{}{"val3": "key3", "val1": "key1"})
|
||||
|
||||
m.Clear()
|
||||
t.Assert(m.Size(), 0)
|
||||
t.Assert(m.IsEmpty(), true)
|
||||
})
|
||||
}
|
||||
|
||||
func Test_TreeMap_Basic(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewTreeMap(gutil.ComparatorString)
|
||||
@ -51,6 +84,7 @@ func Test_TreeMap_Basic(t *testing.T) {
|
||||
t.Assert(m2.Map(), map[interface{}]interface{}{1: 1, "key1": "val1"})
|
||||
})
|
||||
}
|
||||
|
||||
func Test_TreeMap_Set_Fun(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
m := gmap.NewTreeMap(gutil.ComparatorString)
|
||||
|
||||
@ -170,6 +170,7 @@ func (p *Pool) checkExpireItems() {
|
||||
if r := p.list.PopFront(); r != nil {
|
||||
item := r.(*poolItem)
|
||||
latestExpire = item.expire
|
||||
// TODO improve the auto-expiration mechanism of the pool.
|
||||
if item.expire > timestampMilli {
|
||||
p.list.PushFront(item)
|
||||
break
|
||||
|
||||
@ -14,6 +14,7 @@ import (
|
||||
"github.com/gogf/gf/internal/rwmutex"
|
||||
)
|
||||
|
||||
// Ring is a struct of ring structure.
|
||||
type Ring struct {
|
||||
mu *rwmutex.RWMutex
|
||||
ring *ring.Ring // Underlying ring.
|
||||
@ -22,6 +23,9 @@ type Ring struct {
|
||||
dirty *gtype.Bool // Dirty, which means the len and cap should be recalculated. It's marked dirty when the size of ring changes.
|
||||
}
|
||||
|
||||
// New creates and returns a Ring structure of <cap> elements.
|
||||
// The optional parameter <safe> specifies whether using this structure in concurrent safety,
|
||||
// which is false in default.
|
||||
func New(cap int, safe ...bool) *Ring {
|
||||
return &Ring{
|
||||
mu: rwmutex.New(safe...),
|
||||
|
||||
@ -1,3 +1,9 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gring_test
|
||||
|
||||
import (
|
||||
|
||||
@ -9,14 +9,14 @@ package gset
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/internal/rwmutex"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
type Set struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
data map[interface{}]struct{}
|
||||
}
|
||||
|
||||
@ -31,7 +31,7 @@ func New(safe ...bool) *Set {
|
||||
func NewSet(safe ...bool) *Set {
|
||||
return &Set{
|
||||
data: make(map[interface{}]struct{}),
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
}
|
||||
}
|
||||
|
||||
@ -44,13 +44,13 @@ func NewFrom(items interface{}, safe ...bool) *Set {
|
||||
}
|
||||
return &Set{
|
||||
data: m,
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the set with given callback function <f>,
|
||||
// Iterator iterates the set readonly with given callback function <f>,
|
||||
// if <f> returns true then continue iterating; or false to stop.
|
||||
func (set *Set) Iterator(f func(v interface{}) bool) *Set {
|
||||
func (set *Set) Iterator(f func(v interface{}) bool) {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for k, _ := range set.data {
|
||||
@ -58,77 +58,113 @@ func (set *Set) Iterator(f func(v interface{}) bool) *Set {
|
||||
break
|
||||
}
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// Add adds one or multiple items to the set.
|
||||
func (set *Set) Add(item ...interface{}) *Set {
|
||||
func (set *Set) Add(items ...interface{}) {
|
||||
set.mu.Lock()
|
||||
for _, v := range item {
|
||||
if set.data == nil {
|
||||
set.data = make(map[interface{}]struct{})
|
||||
}
|
||||
for _, v := range items {
|
||||
set.data[v] = struct{}{}
|
||||
}
|
||||
set.mu.Unlock()
|
||||
return set
|
||||
}
|
||||
|
||||
// AddIfNotExistFunc adds the returned value of callback function <f> to the set
|
||||
// if <item> does not exit in the set.
|
||||
func (set *Set) AddIfNotExistFunc(item interface{}, f func() interface{}) *Set {
|
||||
if !set.Contains(item) {
|
||||
set.doAddWithLockCheck(item, f())
|
||||
// AddIfNotExist checks whether item exists in the set,
|
||||
// it adds the item to set and returns true if it does not exists in the set,
|
||||
// or else it does nothing and returns false.
|
||||
//
|
||||
// Note that, if <item> is nil, it does nothing and returns false.
|
||||
func (set *Set) AddIfNotExist(item interface{}) bool {
|
||||
if item == nil {
|
||||
return false
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// AddIfNotExistFuncLock adds the returned value of callback function <f> to the set
|
||||
// if <item> does not exit in the set.
|
||||
//
|
||||
// Note that the callback function <f> is executed in the mutex.Lock of the set.
|
||||
func (set *Set) AddIfNotExistFuncLock(item interface{}, f func() interface{}) *Set {
|
||||
if !set.Contains(item) {
|
||||
set.doAddWithLockCheck(item, f)
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// doAddWithLockCheck checks whether item exists with mutex.Lock,
|
||||
// if not exists, it adds item to the set or else just returns the existing value.
|
||||
//
|
||||
// If <value> is type of <func() interface {}>,
|
||||
// it will be executed with mutex.Lock of the set,
|
||||
// and its return value will be added to the set.
|
||||
//
|
||||
// It returns item successfully added..
|
||||
func (set *Set) doAddWithLockCheck(item interface{}, value interface{}) interface{} {
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
if _, ok := set.data[item]; !ok && value != nil {
|
||||
if f, ok := value.(func() interface{}); ok {
|
||||
item = f()
|
||||
} else {
|
||||
item = value
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
if set.data == nil {
|
||||
set.data = make(map[interface{}]struct{})
|
||||
}
|
||||
if _, ok := set.data[item]; !ok {
|
||||
set.data[item] = struct{}{}
|
||||
return true
|
||||
}
|
||||
}
|
||||
if item != nil {
|
||||
set.data[item] = struct{}{}
|
||||
return false
|
||||
}
|
||||
|
||||
// AddIfNotExistFunc checks whether item exists in the set,
|
||||
// it adds the item to set and returns true if it does not exists in the set and
|
||||
// function <f> returns true, or else it does nothing and returns false.
|
||||
//
|
||||
// Note that, if <item> is nil, it does nothing and returns false. The function <f>
|
||||
// is executed without writing lock.
|
||||
func (set *Set) AddIfNotExistFunc(item interface{}, f func() bool) bool {
|
||||
if item == nil {
|
||||
return false
|
||||
}
|
||||
return item
|
||||
if !set.Contains(item) {
|
||||
if f() {
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
if set.data == nil {
|
||||
set.data = make(map[interface{}]struct{})
|
||||
}
|
||||
if _, ok := set.data[item]; !ok {
|
||||
set.data[item] = struct{}{}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AddIfNotExistFunc checks whether item exists in the set,
|
||||
// it adds the item to set and returns true if it does not exists in the set and
|
||||
// function <f> returns true, or else it does nothing and returns false.
|
||||
//
|
||||
// Note that, if <item> is nil, it does nothing and returns false. The function <f>
|
||||
// is executed within writing lock.
|
||||
func (set *Set) AddIfNotExistFuncLock(item interface{}, f func() bool) bool {
|
||||
if item == nil {
|
||||
return false
|
||||
}
|
||||
if !set.Contains(item) {
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
if set.data == nil {
|
||||
set.data = make(map[interface{}]struct{})
|
||||
}
|
||||
if f() {
|
||||
if _, ok := set.data[item]; !ok {
|
||||
set.data[item] = struct{}{}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Contains checks whether the set contains <item>.
|
||||
func (set *Set) Contains(item interface{}) bool {
|
||||
var ok bool
|
||||
set.mu.RLock()
|
||||
_, exists := set.data[item]
|
||||
if set.data != nil {
|
||||
_, ok = set.data[item]
|
||||
}
|
||||
set.mu.RUnlock()
|
||||
return exists
|
||||
return ok
|
||||
}
|
||||
|
||||
// Remove deletes <item> from set.
|
||||
func (set *Set) Remove(item interface{}) *Set {
|
||||
func (set *Set) Remove(item interface{}) {
|
||||
set.mu.Lock()
|
||||
delete(set.data, item)
|
||||
if set.data != nil {
|
||||
delete(set.data, item)
|
||||
}
|
||||
set.mu.Unlock()
|
||||
return set
|
||||
}
|
||||
|
||||
// Size returns the size of the set.
|
||||
@ -140,18 +176,19 @@ func (set *Set) Size() int {
|
||||
}
|
||||
|
||||
// Clear deletes all items of the set.
|
||||
func (set *Set) Clear() *Set {
|
||||
func (set *Set) Clear() {
|
||||
set.mu.Lock()
|
||||
set.data = make(map[interface{}]struct{})
|
||||
set.mu.Unlock()
|
||||
return set
|
||||
}
|
||||
|
||||
// Slice returns the a of items of the set as slice.
|
||||
func (set *Set) Slice() []interface{} {
|
||||
set.mu.RLock()
|
||||
i := 0
|
||||
ret := make([]interface{}, len(set.data))
|
||||
var (
|
||||
i = 0
|
||||
ret = make([]interface{}, len(set.data))
|
||||
)
|
||||
for item := range set.data {
|
||||
ret[i] = item
|
||||
i++
|
||||
@ -164,9 +201,14 @@ func (set *Set) Slice() []interface{} {
|
||||
func (set *Set) Join(glue string) string {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
l := len(set.data)
|
||||
i := 0
|
||||
if len(set.data) == 0 {
|
||||
return ""
|
||||
}
|
||||
var (
|
||||
l = len(set.data)
|
||||
i = 0
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
)
|
||||
for k, _ := range set.data {
|
||||
buffer.WriteString(gconv.String(k))
|
||||
if i != l-1 {
|
||||
@ -181,11 +223,13 @@ func (set *Set) Join(glue string) string {
|
||||
func (set *Set) String() string {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
var (
|
||||
s = ""
|
||||
l = len(set.data)
|
||||
i = 0
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
)
|
||||
buffer.WriteByte('[')
|
||||
s := ""
|
||||
l := len(set.data)
|
||||
i := 0
|
||||
for k, _ := range set.data {
|
||||
s = gconv.String(k)
|
||||
if gstr.IsNumeric(s) {
|
||||
@ -256,7 +300,7 @@ func (set *Set) IsSubsetOf(other *Set) bool {
|
||||
// Union returns a new set which is the union of <set> and <others>.
|
||||
// Which means, all the items in <newSet> are in <set> or in <others>.
|
||||
func (set *Set) Union(others ...*Set) (newSet *Set) {
|
||||
newSet = NewSet(true)
|
||||
newSet = NewSet()
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for _, other := range others {
|
||||
@ -282,7 +326,7 @@ func (set *Set) Union(others ...*Set) (newSet *Set) {
|
||||
// Diff returns a new set which is the difference set from <set> to <others>.
|
||||
// Which means, all the items in <newSet> are in <set> but not in <others>.
|
||||
func (set *Set) Diff(others ...*Set) (newSet *Set) {
|
||||
newSet = NewSet(true)
|
||||
newSet = NewSet()
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for _, other := range others {
|
||||
@ -303,7 +347,7 @@ func (set *Set) Diff(others ...*Set) (newSet *Set) {
|
||||
// Intersect returns a new set which is the intersection from <set> to <others>.
|
||||
// Which means, all the items in <newSet> are in <set> and also in <others>.
|
||||
func (set *Set) Intersect(others ...*Set) (newSet *Set) {
|
||||
newSet = NewSet(true)
|
||||
newSet = NewSet()
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for _, other := range others {
|
||||
@ -328,7 +372,7 @@ func (set *Set) Intersect(others ...*Set) (newSet *Set) {
|
||||
// It returns the difference between <full> and <set>
|
||||
// if the given set <full> is not the full set of <set>.
|
||||
func (set *Set) Complement(full *Set) (newSet *Set) {
|
||||
newSet = NewSet(true)
|
||||
newSet = NewSet()
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
if set != full {
|
||||
@ -408,6 +452,18 @@ func (set *Set) Pops(size int) []interface{} {
|
||||
return array
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of set.
|
||||
func (set *Set) Walk(f func(item interface{}) interface{}) *Set {
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
m := make(map[interface{}]struct{}, len(set.data))
|
||||
for k, v := range set.data {
|
||||
m[f(k)] = v
|
||||
}
|
||||
set.data = m
|
||||
return set
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (set *Set) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(set.Slice())
|
||||
@ -415,12 +471,11 @@ func (set *Set) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (set *Set) UnmarshalJSON(b []byte) error {
|
||||
if set.mu == nil {
|
||||
set.mu = rwmutex.New()
|
||||
set.data = make(map[interface{}]struct{})
|
||||
}
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
if set.data == nil {
|
||||
set.data = make(map[interface{}]struct{})
|
||||
}
|
||||
var array []interface{}
|
||||
if err := json.Unmarshal(b, &array); err != nil {
|
||||
return err
|
||||
@ -433,12 +488,11 @@ func (set *Set) UnmarshalJSON(b []byte) error {
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for set.
|
||||
func (set *Set) UnmarshalValue(value interface{}) (err error) {
|
||||
if set.mu == nil {
|
||||
set.mu = rwmutex.New()
|
||||
set.data = make(map[interface{}]struct{})
|
||||
}
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
if set.data == nil {
|
||||
set.data = make(map[interface{}]struct{})
|
||||
}
|
||||
var array []interface{}
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
|
||||
@ -9,13 +9,13 @@ package gset
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/internal/rwmutex"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
)
|
||||
|
||||
type IntSet struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
data map[int]struct{}
|
||||
}
|
||||
|
||||
@ -24,7 +24,7 @@ type IntSet struct {
|
||||
// which is false in default.
|
||||
func NewIntSet(safe ...bool) *IntSet {
|
||||
return &IntSet{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: make(map[int]struct{}),
|
||||
}
|
||||
}
|
||||
@ -36,14 +36,14 @@ func NewIntSetFrom(items []int, safe ...bool) *IntSet {
|
||||
m[v] = struct{}{}
|
||||
}
|
||||
return &IntSet{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: m,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the set with given callback function <f>,
|
||||
// Iterator iterates the set readonly with given callback function <f>,
|
||||
// if <f> returns true then continue iterating; or false to stop.
|
||||
func (set *IntSet) Iterator(f func(v int) bool) *IntSet {
|
||||
func (set *IntSet) Iterator(f func(v int) bool) {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for k, _ := range set.data {
|
||||
@ -51,75 +51,102 @@ func (set *IntSet) Iterator(f func(v int) bool) *IntSet {
|
||||
break
|
||||
}
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// Add adds one or multiple items to the set.
|
||||
func (set *IntSet) Add(item ...int) *IntSet {
|
||||
func (set *IntSet) Add(item ...int) {
|
||||
set.mu.Lock()
|
||||
if set.data == nil {
|
||||
set.data = make(map[int]struct{})
|
||||
}
|
||||
for _, v := range item {
|
||||
set.data[v] = struct{}{}
|
||||
}
|
||||
set.mu.Unlock()
|
||||
return set
|
||||
}
|
||||
|
||||
// AddIfNotExistFunc adds the returned value of callback function <f> to the set
|
||||
// if <item> does not exit in the set.
|
||||
func (set *IntSet) AddIfNotExistFunc(item int, f func() int) *IntSet {
|
||||
// AddIfNotExist checks whether item exists in the set,
|
||||
// it adds the item to set and returns true if it does not exists in the set,
|
||||
// or else it does nothing and returns false.
|
||||
//
|
||||
// Note that, if <item> is nil, it does nothing and returns false.
|
||||
func (set *IntSet) AddIfNotExist(item int) bool {
|
||||
if !set.Contains(item) {
|
||||
set.doAddWithLockCheck(item, f())
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// AddIfNotExistFuncLock adds the returned value of callback function <f> to the set
|
||||
// if <item> does not exit in the set.
|
||||
//
|
||||
// Note that the callback function <f> is executed in the mutex.Lock of the set.
|
||||
func (set *IntSet) AddIfNotExistFuncLock(item int, f func() int) *IntSet {
|
||||
if !set.Contains(item) {
|
||||
set.doAddWithLockCheck(item, f)
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// doAddWithLockCheck checks whether item exists with mutex.Lock,
|
||||
// if not exists, it adds item to the set or else just returns the existing value.
|
||||
//
|
||||
// If <value> is type of <func() interface {}>,
|
||||
// it will be executed with mutex.Lock of the set,
|
||||
// and its return value will be added to the set.
|
||||
//
|
||||
// It returns item successfully added..
|
||||
func (set *IntSet) doAddWithLockCheck(item int, value interface{}) int {
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
if _, ok := set.data[item]; !ok && value != nil {
|
||||
if f, ok := value.(func() int); ok {
|
||||
item = f()
|
||||
} else {
|
||||
item = value.(int)
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
if set.data == nil {
|
||||
set.data = make(map[int]struct{})
|
||||
}
|
||||
if _, ok := set.data[item]; !ok {
|
||||
set.data[item] = struct{}{}
|
||||
return true
|
||||
}
|
||||
}
|
||||
set.data[item] = struct{}{}
|
||||
return item
|
||||
return false
|
||||
}
|
||||
|
||||
// AddIfNotExistFunc checks whether item exists in the set,
|
||||
// it adds the item to set and returns true if it does not exists in the set and
|
||||
// function <f> returns true, or else it does nothing and returns false.
|
||||
//
|
||||
// Note that, the function <f> is executed without writing lock.
|
||||
func (set *IntSet) AddIfNotExistFunc(item int, f func() bool) bool {
|
||||
if !set.Contains(item) {
|
||||
if f() {
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
if set.data == nil {
|
||||
set.data = make(map[int]struct{})
|
||||
}
|
||||
if _, ok := set.data[item]; !ok {
|
||||
set.data[item] = struct{}{}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// AddIfNotExistFunc checks whether item exists in the set,
|
||||
// it adds the item to set and returns true if it does not exists in the set and
|
||||
// function <f> returns true, or else it does nothing and returns false.
|
||||
//
|
||||
// Note that, the function <f> is executed without writing lock.
|
||||
func (set *IntSet) AddIfNotExistFuncLock(item int, f func() bool) bool {
|
||||
if !set.Contains(item) {
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
if set.data == nil {
|
||||
set.data = make(map[int]struct{})
|
||||
}
|
||||
if f() {
|
||||
if _, ok := set.data[item]; !ok {
|
||||
set.data[item] = struct{}{}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Contains checks whether the set contains <item>.
|
||||
func (set *IntSet) Contains(item int) bool {
|
||||
var ok bool
|
||||
set.mu.RLock()
|
||||
_, exists := set.data[item]
|
||||
if set.data != nil {
|
||||
_, ok = set.data[item]
|
||||
}
|
||||
set.mu.RUnlock()
|
||||
return exists
|
||||
return ok
|
||||
}
|
||||
|
||||
// Remove deletes <item> from set.
|
||||
func (set *IntSet) Remove(item int) *IntSet {
|
||||
func (set *IntSet) Remove(item int) {
|
||||
set.mu.Lock()
|
||||
delete(set.data, item)
|
||||
if set.data != nil {
|
||||
delete(set.data, item)
|
||||
}
|
||||
set.mu.Unlock()
|
||||
return set
|
||||
}
|
||||
|
||||
// Size returns the size of the set.
|
||||
@ -131,18 +158,19 @@ func (set *IntSet) Size() int {
|
||||
}
|
||||
|
||||
// Clear deletes all items of the set.
|
||||
func (set *IntSet) Clear() *IntSet {
|
||||
func (set *IntSet) Clear() {
|
||||
set.mu.Lock()
|
||||
set.data = make(map[int]struct{})
|
||||
set.mu.Unlock()
|
||||
return set
|
||||
}
|
||||
|
||||
// Slice returns the a of items of the set as slice.
|
||||
func (set *IntSet) Slice() []int {
|
||||
set.mu.RLock()
|
||||
ret := make([]int, len(set.data))
|
||||
i := 0
|
||||
var (
|
||||
i = 0
|
||||
ret = make([]int, len(set.data))
|
||||
)
|
||||
for k, _ := range set.data {
|
||||
ret[i] = k
|
||||
i++
|
||||
@ -155,9 +183,14 @@ func (set *IntSet) Slice() []int {
|
||||
func (set *IntSet) Join(glue string) string {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
l := len(set.data)
|
||||
i := 0
|
||||
if len(set.data) == 0 {
|
||||
return ""
|
||||
}
|
||||
var (
|
||||
l = len(set.data)
|
||||
i = 0
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
)
|
||||
for k, _ := range set.data {
|
||||
buffer.WriteString(gconv.String(k))
|
||||
if i != l-1 {
|
||||
@ -227,7 +260,7 @@ func (set *IntSet) IsSubsetOf(other *IntSet) bool {
|
||||
// Union returns a new set which is the union of <set> and <other>.
|
||||
// Which means, all the items in <newSet> are in <set> or in <other>.
|
||||
func (set *IntSet) Union(others ...*IntSet) (newSet *IntSet) {
|
||||
newSet = NewIntSet(true)
|
||||
newSet = NewIntSet()
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for _, other := range others {
|
||||
@ -253,7 +286,7 @@ func (set *IntSet) Union(others ...*IntSet) (newSet *IntSet) {
|
||||
// Diff returns a new set which is the difference set from <set> to <other>.
|
||||
// Which means, all the items in <newSet> are in <set> but not in <other>.
|
||||
func (set *IntSet) Diff(others ...*IntSet) (newSet *IntSet) {
|
||||
newSet = NewIntSet(true)
|
||||
newSet = NewIntSet()
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for _, other := range others {
|
||||
@ -274,7 +307,7 @@ func (set *IntSet) Diff(others ...*IntSet) (newSet *IntSet) {
|
||||
// Intersect returns a new set which is the intersection from <set> to <other>.
|
||||
// Which means, all the items in <newSet> are in <set> and also in <other>.
|
||||
func (set *IntSet) Intersect(others ...*IntSet) (newSet *IntSet) {
|
||||
newSet = NewIntSet(true)
|
||||
newSet = NewIntSet()
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for _, other := range others {
|
||||
@ -299,7 +332,7 @@ func (set *IntSet) Intersect(others ...*IntSet) (newSet *IntSet) {
|
||||
// It returns the difference between <full> and <set>
|
||||
// if the given set <full> is not the full set of <set>.
|
||||
func (set *IntSet) Complement(full *IntSet) (newSet *IntSet) {
|
||||
newSet = NewIntSet(true)
|
||||
newSet = NewIntSet()
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
if set != full {
|
||||
@ -379,6 +412,18 @@ func (set *IntSet) Pops(size int) []int {
|
||||
return array
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of set.
|
||||
func (set *IntSet) Walk(f func(item int) int) *IntSet {
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
m := make(map[int]struct{}, len(set.data))
|
||||
for k, v := range set.data {
|
||||
m[f(k)] = v
|
||||
}
|
||||
set.data = m
|
||||
return set
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (set *IntSet) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(set.Slice())
|
||||
@ -386,12 +431,11 @@ func (set *IntSet) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (set *IntSet) UnmarshalJSON(b []byte) error {
|
||||
if set.mu == nil {
|
||||
set.mu = rwmutex.New()
|
||||
set.data = make(map[int]struct{})
|
||||
}
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
if set.data == nil {
|
||||
set.data = make(map[int]struct{})
|
||||
}
|
||||
var array []int
|
||||
if err := json.Unmarshal(b, &array); err != nil {
|
||||
return err
|
||||
@ -404,12 +448,11 @@ func (set *IntSet) UnmarshalJSON(b []byte) error {
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for set.
|
||||
func (set *IntSet) UnmarshalValue(value interface{}) (err error) {
|
||||
if set.mu == nil {
|
||||
set.mu = rwmutex.New()
|
||||
set.data = make(map[int]struct{})
|
||||
}
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
if set.data == nil {
|
||||
set.data = make(map[int]struct{})
|
||||
}
|
||||
var array []int
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
|
||||
@ -9,14 +9,15 @@ package gset
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/internal/rwmutex"
|
||||
"github.com/gogf/gf/text/gstr"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type StrSet struct {
|
||||
mu *rwmutex.RWMutex
|
||||
mu rwmutex.RWMutex
|
||||
data map[string]struct{}
|
||||
}
|
||||
|
||||
@ -25,7 +26,7 @@ type StrSet struct {
|
||||
// which is false in default.
|
||||
func NewStrSet(safe ...bool) *StrSet {
|
||||
return &StrSet{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: make(map[string]struct{}),
|
||||
}
|
||||
}
|
||||
@ -37,14 +38,14 @@ func NewStrSetFrom(items []string, safe ...bool) *StrSet {
|
||||
m[v] = struct{}{}
|
||||
}
|
||||
return &StrSet{
|
||||
mu: rwmutex.New(safe...),
|
||||
mu: rwmutex.Create(safe...),
|
||||
data: m,
|
||||
}
|
||||
}
|
||||
|
||||
// Iterator iterates the set with given callback function <f>,
|
||||
// Iterator iterates the set readonly with given callback function <f>,
|
||||
// if <f> returns true then continue iterating; or false to stop.
|
||||
func (set *StrSet) Iterator(f func(v string) bool) *StrSet {
|
||||
func (set *StrSet) Iterator(f func(v string) bool) {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for k, _ := range set.data {
|
||||
@ -52,77 +53,113 @@ func (set *StrSet) Iterator(f func(v string) bool) *StrSet {
|
||||
break
|
||||
}
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// Add adds one or multiple items to the set.
|
||||
func (set *StrSet) Add(item ...string) *StrSet {
|
||||
func (set *StrSet) Add(item ...string) {
|
||||
set.mu.Lock()
|
||||
if set.data == nil {
|
||||
set.data = make(map[string]struct{})
|
||||
}
|
||||
for _, v := range item {
|
||||
set.data[v] = struct{}{}
|
||||
}
|
||||
set.mu.Unlock()
|
||||
return set
|
||||
}
|
||||
|
||||
// AddIfNotExistFunc adds the returned value of callback function <f> to the set
|
||||
// if <item> does not exit in the set.
|
||||
func (set *StrSet) AddIfNotExistFunc(item string, f func() string) *StrSet {
|
||||
// AddIfNotExist checks whether item exists in the set,
|
||||
// it adds the item to set and returns true if it does not exists in the set,
|
||||
// or else it does nothing and returns false.
|
||||
func (set *StrSet) AddIfNotExist(item string) bool {
|
||||
if !set.Contains(item) {
|
||||
set.doAddWithLockCheck(item, f())
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// AddIfNotExistFuncLock adds the returned value of callback function <f> to the set
|
||||
// if <item> does not exit in the set.
|
||||
//
|
||||
// Note that the callback function <f> is executed in the mutex.Lock of the set.
|
||||
func (set *StrSet) AddIfNotExistFuncLock(item string, f func() string) *StrSet {
|
||||
if !set.Contains(item) {
|
||||
set.doAddWithLockCheck(item, f)
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// doAddWithLockCheck checks whether item exists with mutex.Lock,
|
||||
// if not exists, it adds item to the set or else just returns the existing value.
|
||||
//
|
||||
// If <value> is type of <func() interface {}>,
|
||||
// it will be executed with mutex.Lock of the set,
|
||||
// and its return value will be added to the set.
|
||||
//
|
||||
// It returns item successfully added..
|
||||
func (set *StrSet) doAddWithLockCheck(item string, value interface{}) string {
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
if _, ok := set.data[item]; !ok && value != nil {
|
||||
if f, ok := value.(func() string); ok {
|
||||
item = f()
|
||||
} else {
|
||||
item = value.(string)
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
if set.data == nil {
|
||||
set.data = make(map[string]struct{})
|
||||
}
|
||||
if _, ok := set.data[item]; !ok {
|
||||
set.data[item] = struct{}{}
|
||||
return true
|
||||
}
|
||||
}
|
||||
if item != "" {
|
||||
set.data[item] = struct{}{}
|
||||
return false
|
||||
}
|
||||
|
||||
// AddIfNotExistFunc checks whether item exists in the set,
|
||||
// it adds the item to set and returns true if it does not exists in the set and
|
||||
// function <f> returns true, or else it does nothing and returns false.
|
||||
//
|
||||
// Note that, the function <f> is executed without writing lock.
|
||||
func (set *StrSet) AddIfNotExistFunc(item string, f func() bool) bool {
|
||||
if !set.Contains(item) {
|
||||
if f() {
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
if set.data == nil {
|
||||
set.data = make(map[string]struct{})
|
||||
}
|
||||
if _, ok := set.data[item]; !ok {
|
||||
set.data[item] = struct{}{}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return item
|
||||
return false
|
||||
}
|
||||
|
||||
// AddIfNotExistFunc checks whether item exists in the set,
|
||||
// it adds the item to set and returns true if it does not exists in the set and
|
||||
// function <f> returns true, or else it does nothing and returns false.
|
||||
//
|
||||
// Note that, the function <f> is executed without writing lock.
|
||||
func (set *StrSet) AddIfNotExistFuncLock(item string, f func() bool) bool {
|
||||
if !set.Contains(item) {
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
if set.data == nil {
|
||||
set.data = make(map[string]struct{})
|
||||
}
|
||||
if f() {
|
||||
if _, ok := set.data[item]; !ok {
|
||||
set.data[item] = struct{}{}
|
||||
return true
|
||||
}
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Contains checks whether the set contains <item>.
|
||||
func (set *StrSet) Contains(item string) bool {
|
||||
var ok bool
|
||||
set.mu.RLock()
|
||||
_, exists := set.data[item]
|
||||
if set.data != nil {
|
||||
_, ok = set.data[item]
|
||||
}
|
||||
set.mu.RUnlock()
|
||||
return exists
|
||||
return ok
|
||||
}
|
||||
|
||||
// ContainsI checks whether a value exists in the set with case-insensitively.
|
||||
// Note that it internally iterates the whole set to do the comparison with case-insensitively.
|
||||
func (set *StrSet) ContainsI(item string) bool {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for k, _ := range set.data {
|
||||
if strings.EqualFold(k, item) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Remove deletes <item> from set.
|
||||
func (set *StrSet) Remove(item string) *StrSet {
|
||||
func (set *StrSet) Remove(item string) {
|
||||
set.mu.Lock()
|
||||
delete(set.data, item)
|
||||
if set.data != nil {
|
||||
delete(set.data, item)
|
||||
}
|
||||
set.mu.Unlock()
|
||||
return set
|
||||
}
|
||||
|
||||
// Size returns the size of the set.
|
||||
@ -134,18 +171,19 @@ func (set *StrSet) Size() int {
|
||||
}
|
||||
|
||||
// Clear deletes all items of the set.
|
||||
func (set *StrSet) Clear() *StrSet {
|
||||
func (set *StrSet) Clear() {
|
||||
set.mu.Lock()
|
||||
set.data = make(map[string]struct{})
|
||||
set.mu.Unlock()
|
||||
return set
|
||||
}
|
||||
|
||||
// Slice returns the a of items of the set as slice.
|
||||
func (set *StrSet) Slice() []string {
|
||||
set.mu.RLock()
|
||||
ret := make([]string, len(set.data))
|
||||
i := 0
|
||||
var (
|
||||
i = 0
|
||||
ret = make([]string, len(set.data))
|
||||
)
|
||||
for item := range set.data {
|
||||
ret[i] = item
|
||||
i++
|
||||
@ -159,9 +197,14 @@ func (set *StrSet) Slice() []string {
|
||||
func (set *StrSet) Join(glue string) string {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
l := len(set.data)
|
||||
i := 0
|
||||
if len(set.data) == 0 {
|
||||
return ""
|
||||
}
|
||||
var (
|
||||
l = len(set.data)
|
||||
i = 0
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
)
|
||||
for k, _ := range set.data {
|
||||
buffer.WriteString(k)
|
||||
if i != l-1 {
|
||||
@ -176,9 +219,11 @@ func (set *StrSet) Join(glue string) string {
|
||||
func (set *StrSet) String() string {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
l := len(set.data)
|
||||
i := 0
|
||||
var (
|
||||
l = len(set.data)
|
||||
i = 0
|
||||
buffer = bytes.NewBuffer(nil)
|
||||
)
|
||||
for k, _ := range set.data {
|
||||
buffer.WriteString(`"` + gstr.QuoteMeta(k, `"\`) + `"`)
|
||||
if i != l-1 {
|
||||
@ -243,7 +288,7 @@ func (set *StrSet) IsSubsetOf(other *StrSet) bool {
|
||||
// Union returns a new set which is the union of <set> and <other>.
|
||||
// Which means, all the items in <newSet> are in <set> or in <other>.
|
||||
func (set *StrSet) Union(others ...*StrSet) (newSet *StrSet) {
|
||||
newSet = NewStrSet(true)
|
||||
newSet = NewStrSet()
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for _, other := range others {
|
||||
@ -269,7 +314,7 @@ func (set *StrSet) Union(others ...*StrSet) (newSet *StrSet) {
|
||||
// Diff returns a new set which is the difference set from <set> to <other>.
|
||||
// Which means, all the items in <newSet> are in <set> but not in <other>.
|
||||
func (set *StrSet) Diff(others ...*StrSet) (newSet *StrSet) {
|
||||
newSet = NewStrSet(true)
|
||||
newSet = NewStrSet()
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for _, other := range others {
|
||||
@ -290,7 +335,7 @@ func (set *StrSet) Diff(others ...*StrSet) (newSet *StrSet) {
|
||||
// Intersect returns a new set which is the intersection from <set> to <other>.
|
||||
// Which means, all the items in <newSet> are in <set> and also in <other>.
|
||||
func (set *StrSet) Intersect(others ...*StrSet) (newSet *StrSet) {
|
||||
newSet = NewStrSet(true)
|
||||
newSet = NewStrSet()
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for _, other := range others {
|
||||
@ -315,7 +360,7 @@ func (set *StrSet) Intersect(others ...*StrSet) (newSet *StrSet) {
|
||||
// It returns the difference between <full> and <set>
|
||||
// if the given set <full> is not the full set of <set>.
|
||||
func (set *StrSet) Complement(full *StrSet) (newSet *StrSet) {
|
||||
newSet = NewStrSet(true)
|
||||
newSet = NewStrSet()
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
if set != full {
|
||||
@ -395,6 +440,18 @@ func (set *StrSet) Pops(size int) []string {
|
||||
return array
|
||||
}
|
||||
|
||||
// Walk applies a user supplied function <f> to every item of set.
|
||||
func (set *StrSet) Walk(f func(item string) string) *StrSet {
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
m := make(map[string]struct{}, len(set.data))
|
||||
for k, v := range set.data {
|
||||
m[f(k)] = v
|
||||
}
|
||||
set.data = m
|
||||
return set
|
||||
}
|
||||
|
||||
// MarshalJSON implements the interface MarshalJSON for json.Marshal.
|
||||
func (set *StrSet) MarshalJSON() ([]byte, error) {
|
||||
return json.Marshal(set.Slice())
|
||||
@ -402,12 +459,11 @@ func (set *StrSet) MarshalJSON() ([]byte, error) {
|
||||
|
||||
// UnmarshalJSON implements the interface UnmarshalJSON for json.Unmarshal.
|
||||
func (set *StrSet) UnmarshalJSON(b []byte) error {
|
||||
if set.mu == nil {
|
||||
set.mu = rwmutex.New()
|
||||
set.data = make(map[string]struct{})
|
||||
}
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
if set.data == nil {
|
||||
set.data = make(map[string]struct{})
|
||||
}
|
||||
var array []string
|
||||
if err := json.Unmarshal(b, &array); err != nil {
|
||||
return err
|
||||
@ -420,12 +476,11 @@ func (set *StrSet) UnmarshalJSON(b []byte) error {
|
||||
|
||||
// UnmarshalValue is an interface implement which sets any type of value for set.
|
||||
func (set *StrSet) UnmarshalValue(value interface{}) (err error) {
|
||||
if set.mu == nil {
|
||||
set.mu = rwmutex.New()
|
||||
set.data = make(map[string]struct{})
|
||||
}
|
||||
set.mu.Lock()
|
||||
defer set.mu.Unlock()
|
||||
if set.data == nil {
|
||||
set.data = make(map[string]struct{})
|
||||
}
|
||||
var array []string
|
||||
switch value.(type) {
|
||||
case string, []byte:
|
||||
|
||||
166
container/gset/gset_z_example_any_test.go
Normal file
166
container/gset/gset_z_example_any_test.go
Normal file
@ -0,0 +1,166 @@
|
||||
// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gset_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/gset"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func ExampleSet_Intersect() {
|
||||
s1 := gset.NewFrom(g.Slice{1, 2, 3})
|
||||
s2 := gset.NewFrom(g.Slice{4, 5, 6})
|
||||
s3 := gset.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7})
|
||||
|
||||
fmt.Println(s3.Intersect(s1).Slice())
|
||||
fmt.Println(s3.Diff(s1).Slice())
|
||||
fmt.Println(s1.Union(s2).Slice())
|
||||
fmt.Println(s1.Complement(s3).Slice())
|
||||
|
||||
// May Output:
|
||||
// [2 3 1]
|
||||
// [5 6 7 4]
|
||||
// [6 1 2 3 4 5]
|
||||
// [4 5 6 7]
|
||||
}
|
||||
|
||||
func ExampleSet_Diff() {
|
||||
s1 := gset.NewFrom(g.Slice{1, 2, 3})
|
||||
s2 := gset.NewFrom(g.Slice{4, 5, 6})
|
||||
s3 := gset.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7})
|
||||
|
||||
fmt.Println(s3.Intersect(s1).Slice())
|
||||
fmt.Println(s3.Diff(s1).Slice())
|
||||
fmt.Println(s1.Union(s2).Slice())
|
||||
fmt.Println(s1.Complement(s3).Slice())
|
||||
|
||||
// May Output:
|
||||
// [2 3 1]
|
||||
// [5 6 7 4]
|
||||
// [6 1 2 3 4 5]
|
||||
// [4 5 6 7]
|
||||
}
|
||||
|
||||
func ExampleSet_Union() {
|
||||
s1 := gset.NewFrom(g.Slice{1, 2, 3})
|
||||
s2 := gset.NewFrom(g.Slice{4, 5, 6})
|
||||
s3 := gset.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7})
|
||||
|
||||
fmt.Println(s3.Intersect(s1).Slice())
|
||||
fmt.Println(s3.Diff(s1).Slice())
|
||||
fmt.Println(s1.Union(s2).Slice())
|
||||
fmt.Println(s1.Complement(s3).Slice())
|
||||
|
||||
// May Output:
|
||||
// [2 3 1]
|
||||
// [5 6 7 4]
|
||||
// [6 1 2 3 4 5]
|
||||
// [4 5 6 7]
|
||||
}
|
||||
|
||||
func ExampleSet_Complement() {
|
||||
s1 := gset.NewFrom(g.Slice{1, 2, 3})
|
||||
s2 := gset.NewFrom(g.Slice{4, 5, 6})
|
||||
s3 := gset.NewFrom(g.Slice{1, 2, 3, 4, 5, 6, 7})
|
||||
|
||||
fmt.Println(s3.Intersect(s1).Slice())
|
||||
fmt.Println(s3.Diff(s1).Slice())
|
||||
fmt.Println(s1.Union(s2).Slice())
|
||||
fmt.Println(s1.Complement(s3).Slice())
|
||||
|
||||
// May Output:
|
||||
// [2 3 1]
|
||||
// [5 6 7 4]
|
||||
// [6 1 2 3 4 5]
|
||||
// [4 5 6 7]
|
||||
}
|
||||
|
||||
func ExampleSet_IsSubsetOf() {
|
||||
var s1, s2 gset.Set
|
||||
s1.Add(g.Slice{1, 2, 3}...)
|
||||
s2.Add(g.Slice{2, 3}...)
|
||||
fmt.Println(s1.IsSubsetOf(&s2))
|
||||
fmt.Println(s2.IsSubsetOf(&s1))
|
||||
|
||||
// Output:
|
||||
// false
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleSet_AddIfNotExist() {
|
||||
var set gset.Set
|
||||
fmt.Println(set.AddIfNotExist(1))
|
||||
fmt.Println(set.AddIfNotExist(1))
|
||||
fmt.Println(set.Slice())
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// [1]
|
||||
}
|
||||
|
||||
func ExampleSet_Pop() {
|
||||
var set gset.Set
|
||||
set.Add(1, 2, 3, 4)
|
||||
fmt.Println(set.Pop())
|
||||
fmt.Println(set.Pops(2))
|
||||
fmt.Println(set.Size())
|
||||
|
||||
// May Output:
|
||||
// 1
|
||||
// [2 3]
|
||||
// 1
|
||||
}
|
||||
|
||||
func ExampleSet_Pops() {
|
||||
var set gset.Set
|
||||
set.Add(1, 2, 3, 4)
|
||||
fmt.Println(set.Pop())
|
||||
fmt.Println(set.Pops(2))
|
||||
fmt.Println(set.Size())
|
||||
|
||||
// May Output:
|
||||
// 1
|
||||
// [2 3]
|
||||
// 1
|
||||
}
|
||||
|
||||
func ExampleSet_Join() {
|
||||
var set gset.Set
|
||||
set.Add("a", "b", "c", "d")
|
||||
fmt.Println(set.Join(","))
|
||||
|
||||
// May Output:
|
||||
// a,b,c,d
|
||||
}
|
||||
|
||||
func ExampleSet_Contains() {
|
||||
var set gset.StrSet
|
||||
set.Add("a")
|
||||
fmt.Println(set.Contains("a"))
|
||||
fmt.Println(set.Contains("A"))
|
||||
fmt.Println(set.ContainsI("A"))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleSet_ContainsI() {
|
||||
var set gset.StrSet
|
||||
set.Add("a")
|
||||
fmt.Println(set.Contains("a"))
|
||||
fmt.Println(set.Contains("A"))
|
||||
fmt.Println(set.ContainsI("A"))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// true
|
||||
}
|
||||
23
container/gset/gset_z_example_int_test.go
Normal file
23
container/gset/gset_z_example_int_test.go
Normal file
@ -0,0 +1,23 @@
|
||||
// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gset_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/gset"
|
||||
)
|
||||
|
||||
func ExampleIntSet_Contains() {
|
||||
var set gset.IntSet
|
||||
set.Add(1)
|
||||
fmt.Println(set.Contains(1))
|
||||
fmt.Println(set.Contains(2))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
}
|
||||
43
container/gset/gset_z_example_str_test.go
Normal file
43
container/gset/gset_z_example_str_test.go
Normal file
@ -0,0 +1,43 @@
|
||||
// Copyright 2020 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gset_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/container/gset"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
)
|
||||
|
||||
func ExampleStrSet_Contains() {
|
||||
var set gset.StrSet
|
||||
set.Add("a")
|
||||
fmt.Println(set.Contains("a"))
|
||||
fmt.Println(set.Contains("A"))
|
||||
fmt.Println(set.ContainsI("A"))
|
||||
|
||||
// Output:
|
||||
// true
|
||||
// false
|
||||
// true
|
||||
}
|
||||
|
||||
func ExampleStrSet_Walk() {
|
||||
var (
|
||||
set gset.StrSet
|
||||
names = g.SliceStr{"user", "user_detail"}
|
||||
prefix = "gf_"
|
||||
)
|
||||
set.Add(names...)
|
||||
// Add prefix for given table names.
|
||||
set.Walk(func(item string) string {
|
||||
return prefix + item
|
||||
})
|
||||
fmt.Println(set.Slice())
|
||||
|
||||
// May Output:
|
||||
// [gf_user gf_user_detail]
|
||||
}
|
||||
@ -9,10 +9,12 @@
|
||||
package gset_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/container/gset"
|
||||
@ -21,10 +23,30 @@ import (
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSet_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var s gset.Set
|
||||
s.Add(1, 1, 2)
|
||||
s.Add([]interface{}{3, 4}...)
|
||||
t.Assert(s.Size(), 4)
|
||||
t.AssertIN(1, s.Slice())
|
||||
t.AssertIN(2, s.Slice())
|
||||
t.AssertIN(3, s.Slice())
|
||||
t.AssertIN(4, s.Slice())
|
||||
t.AssertNI(0, s.Slice())
|
||||
t.Assert(s.Contains(4), true)
|
||||
t.Assert(s.Contains(5), false)
|
||||
s.Remove(1)
|
||||
t.Assert(s.Size(), 3)
|
||||
s.Clear()
|
||||
t.Assert(s.Size(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSet_New(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.New()
|
||||
s.Add(1).Add(1).Add(2)
|
||||
s.Add(1, 1, 2)
|
||||
s.Add([]interface{}{3, 4}...)
|
||||
t.Assert(s.Size(), 4)
|
||||
t.AssertIN(1, s.Slice())
|
||||
@ -44,7 +66,7 @@ func TestSet_New(t *testing.T) {
|
||||
func TestSet_Basic(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.NewSet()
|
||||
s.Add(1).Add(1).Add(2)
|
||||
s.Add(1, 1, 2)
|
||||
s.Add([]interface{}{3, 4}...)
|
||||
t.Assert(s.Size(), 4)
|
||||
t.AssertIN(1, s.Slice())
|
||||
@ -64,7 +86,7 @@ func TestSet_Basic(t *testing.T) {
|
||||
func TestSet_Iterator(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.NewSet()
|
||||
s.Add(1).Add(2).Add(3)
|
||||
s.Add(1, 2, 3)
|
||||
t.Assert(s.Size(), 3)
|
||||
|
||||
a1 := garray.New(true)
|
||||
@ -85,7 +107,7 @@ func TestSet_Iterator(t *testing.T) {
|
||||
func TestSet_LockFunc(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.NewSet()
|
||||
s.Add(1).Add(2).Add(3)
|
||||
s.Add(1, 2, 3)
|
||||
t.Assert(s.Size(), 3)
|
||||
s.LockFunc(func(m map[interface{}]struct{}) {
|
||||
delete(m, 1)
|
||||
@ -105,9 +127,9 @@ func TestSet_Equal(t *testing.T) {
|
||||
s1 := gset.NewSet()
|
||||
s2 := gset.NewSet()
|
||||
s3 := gset.NewSet()
|
||||
s1.Add(1).Add(2).Add(3)
|
||||
s2.Add(1).Add(2).Add(3)
|
||||
s3.Add(1).Add(2).Add(3).Add(4)
|
||||
s1.Add(1, 2, 3)
|
||||
s2.Add(1, 2, 3)
|
||||
s3.Add(1, 2, 3, 4)
|
||||
t.Assert(s1.Equal(s2), true)
|
||||
t.Assert(s1.Equal(s3), false)
|
||||
})
|
||||
@ -118,9 +140,9 @@ func TestSet_IsSubsetOf(t *testing.T) {
|
||||
s1 := gset.NewSet()
|
||||
s2 := gset.NewSet()
|
||||
s3 := gset.NewSet()
|
||||
s1.Add(1).Add(2)
|
||||
s2.Add(1).Add(2).Add(3)
|
||||
s3.Add(1).Add(2).Add(3).Add(4)
|
||||
s1.Add(1, 2)
|
||||
s2.Add(1, 2, 3)
|
||||
s3.Add(1, 2, 3, 4)
|
||||
t.Assert(s1.IsSubsetOf(s2), true)
|
||||
t.Assert(s2.IsSubsetOf(s3), true)
|
||||
t.Assert(s1.IsSubsetOf(s3), true)
|
||||
@ -133,8 +155,8 @@ func TestSet_Union(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := gset.NewSet()
|
||||
s2 := gset.NewSet()
|
||||
s1.Add(1).Add(2)
|
||||
s2.Add(3).Add(4)
|
||||
s1.Add(1, 2)
|
||||
s2.Add(3, 4)
|
||||
s3 := s1.Union(s2)
|
||||
t.Assert(s3.Contains(1), true)
|
||||
t.Assert(s3.Contains(2), true)
|
||||
@ -147,8 +169,8 @@ func TestSet_Diff(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := gset.NewSet()
|
||||
s2 := gset.NewSet()
|
||||
s1.Add(1).Add(2).Add(3)
|
||||
s2.Add(3).Add(4).Add(5)
|
||||
s1.Add(1, 2, 3)
|
||||
s2.Add(3, 4, 5)
|
||||
s3 := s1.Diff(s2)
|
||||
t.Assert(s3.Contains(1), true)
|
||||
t.Assert(s3.Contains(2), true)
|
||||
@ -161,8 +183,8 @@ func TestSet_Intersect(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := gset.NewSet()
|
||||
s2 := gset.NewSet()
|
||||
s1.Add(1).Add(2).Add(3)
|
||||
s2.Add(3).Add(4).Add(5)
|
||||
s1.Add(1, 2, 3)
|
||||
s2.Add(3, 4, 5)
|
||||
s3 := s1.Intersect(s2)
|
||||
t.Assert(s3.Contains(1), false)
|
||||
t.Assert(s3.Contains(2), false)
|
||||
@ -175,8 +197,8 @@ func TestSet_Complement(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := gset.NewSet()
|
||||
s2 := gset.NewSet()
|
||||
s1.Add(1).Add(2).Add(3)
|
||||
s2.Add(3).Add(4).Add(5)
|
||||
s1.Add(1, 2, 3)
|
||||
s2.Add(3, 4, 5)
|
||||
s3 := s1.Complement(s2)
|
||||
t.Assert(s3.Contains(1), false)
|
||||
t.Assert(s3.Contains(2), false)
|
||||
@ -203,9 +225,9 @@ func TestNewFrom(t *testing.T) {
|
||||
func TestNew(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := gset.New()
|
||||
s1.Add("a").Add(2)
|
||||
s1.Add("a", 2)
|
||||
s2 := gset.New(true)
|
||||
s2.Add("b").Add(3)
|
||||
s2.Add("b", 3)
|
||||
t.Assert(s1.Contains("a"), true)
|
||||
|
||||
})
|
||||
@ -214,13 +236,13 @@ func TestNew(t *testing.T) {
|
||||
func TestSet_Join(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := gset.New(true)
|
||||
s1.Add("a").Add("a1").Add("b").Add("c")
|
||||
s1.Add("a", "a1", "b", "c")
|
||||
str1 := s1.Join(",")
|
||||
t.Assert(strings.Contains(str1, "a1"), true)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := gset.New(true)
|
||||
s1.Add("a").Add(`"b"`).Add(`\c`)
|
||||
s1.Add("a", `"b"`, `\c`)
|
||||
str1 := s1.Join(",")
|
||||
t.Assert(strings.Contains(str1, `"b"`), true)
|
||||
t.Assert(strings.Contains(str1, `\c`), true)
|
||||
@ -231,7 +253,7 @@ func TestSet_Join(t *testing.T) {
|
||||
func TestSet_String(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := gset.New(true)
|
||||
s1.Add("a").Add("a2").Add("b").Add("c")
|
||||
s1.Add("a", "a2", "b", "c")
|
||||
str1 := s1.String()
|
||||
t.Assert(strings.Contains(str1, "["), true)
|
||||
t.Assert(strings.Contains(str1, "]"), true)
|
||||
@ -243,8 +265,8 @@ func TestSet_Merge(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := gset.New(true)
|
||||
s2 := gset.New(true)
|
||||
s1.Add("a").Add("a2").Add("b").Add("c")
|
||||
s2.Add("b").Add("b1").Add("e").Add("f")
|
||||
s1.Add("a", "a2", "b", "c")
|
||||
s2.Add("b", "b1", "e", "f")
|
||||
ss := s1.Merge(s2)
|
||||
t.Assert(ss.Contains("a2"), true)
|
||||
t.Assert(ss.Contains("b1"), true)
|
||||
@ -255,7 +277,7 @@ func TestSet_Merge(t *testing.T) {
|
||||
func TestSet_Sum(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := gset.New(true)
|
||||
s1.Add(1).Add(2).Add(3).Add(4)
|
||||
s1.Add(1, 2, 3, 4)
|
||||
t.Assert(s1.Sum(), int(10))
|
||||
|
||||
})
|
||||
@ -264,7 +286,7 @@ func TestSet_Sum(t *testing.T) {
|
||||
func TestSet_Pop(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.New(true)
|
||||
s.Add(1).Add(2).Add(3).Add(4)
|
||||
s.Add(1, 2, 3, 4)
|
||||
t.Assert(s.Size(), 4)
|
||||
t.AssertIN(s.Pop(), []int{1, 2, 3, 4})
|
||||
t.Assert(s.Size(), 3)
|
||||
@ -274,7 +296,7 @@ func TestSet_Pop(t *testing.T) {
|
||||
func TestSet_Pops(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.New(true)
|
||||
s.Add(1).Add(2).Add(3).Add(4)
|
||||
s.Add(1, 2, 3, 4)
|
||||
t.Assert(s.Size(), 4)
|
||||
t.Assert(s.Pops(0), nil)
|
||||
t.AssertIN(s.Pops(1), []int{1, 2, 3, 4})
|
||||
@ -324,43 +346,84 @@ func TestSet_Json(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestSet_AddIfNotExist(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.New(true)
|
||||
s.Add(1)
|
||||
t.Assert(s.Contains(1), true)
|
||||
t.Assert(s.AddIfNotExist(1), false)
|
||||
t.Assert(s.AddIfNotExist(2), true)
|
||||
t.Assert(s.Contains(2), true)
|
||||
t.Assert(s.AddIfNotExist(2), false)
|
||||
t.Assert(s.Contains(2), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSet_AddIfNotExistFunc(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.New(true)
|
||||
s.Add(1)
|
||||
t.Assert(s.Contains(1), true)
|
||||
t.Assert(s.Contains(2), false)
|
||||
|
||||
s.AddIfNotExistFunc(2, func() interface{} {
|
||||
return 3
|
||||
})
|
||||
t.Assert(s.AddIfNotExistFunc(2, func() bool { return false }), false)
|
||||
t.Assert(s.Contains(2), false)
|
||||
t.Assert(s.Contains(3), true)
|
||||
|
||||
s.AddIfNotExistFunc(3, func() interface{} {
|
||||
return 4
|
||||
})
|
||||
t.Assert(s.Contains(3), true)
|
||||
t.Assert(s.Contains(4), false)
|
||||
t.Assert(s.AddIfNotExistFunc(2, func() bool { return true }), true)
|
||||
t.Assert(s.Contains(2), true)
|
||||
t.Assert(s.AddIfNotExistFunc(2, func() bool { return true }), false)
|
||||
t.Assert(s.Contains(2), true)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.New(true)
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
r := s.AddIfNotExistFunc(1, func() bool {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
return true
|
||||
})
|
||||
t.Assert(r, false)
|
||||
}()
|
||||
s.Add(1)
|
||||
t.Assert(s.Contains(1), true)
|
||||
t.Assert(s.Contains(2), false)
|
||||
wg.Wait()
|
||||
})
|
||||
}
|
||||
|
||||
s.AddIfNotExistFuncLock(2, func() interface{} {
|
||||
return 3
|
||||
func TestSet_Walk(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var set gset.Set
|
||||
set.Add(g.Slice{1, 2}...)
|
||||
set.Walk(func(item interface{}) interface{} {
|
||||
return gconv.Int(item) + 10
|
||||
})
|
||||
t.Assert(s.Contains(2), false)
|
||||
t.Assert(s.Contains(3), true)
|
||||
t.Assert(set.Size(), 2)
|
||||
t.Assert(set.Contains(11), true)
|
||||
t.Assert(set.Contains(12), true)
|
||||
})
|
||||
}
|
||||
|
||||
s.AddIfNotExistFuncLock(3, func() interface{} {
|
||||
return 4
|
||||
})
|
||||
t.Assert(s.Contains(3), true)
|
||||
t.Assert(s.Contains(4), false)
|
||||
func TestSet_AddIfNotExistFuncLock(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.New(true)
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
r := s.AddIfNotExistFuncLock(1, func() bool {
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
return true
|
||||
})
|
||||
t.Assert(r, true)
|
||||
}()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
r := s.AddIfNotExistFuncLock(1, func() bool {
|
||||
return true
|
||||
})
|
||||
t.Assert(r, false)
|
||||
}()
|
||||
wg.Wait()
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -9,21 +9,43 @@
|
||||
package gset_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/container/gset"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
)
|
||||
|
||||
func TestIntSet_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
var s gset.IntSet
|
||||
s.Add(1, 1, 2)
|
||||
s.Add([]int{3, 4}...)
|
||||
t.Assert(s.Size(), 4)
|
||||
t.AssertIN(1, s.Slice())
|
||||
t.AssertIN(2, s.Slice())
|
||||
t.AssertIN(3, s.Slice())
|
||||
t.AssertIN(4, s.Slice())
|
||||
t.AssertNI(0, s.Slice())
|
||||
t.Assert(s.Contains(4), true)
|
||||
t.Assert(s.Contains(5), false)
|
||||
s.Remove(1)
|
||||
t.Assert(s.Size(), 3)
|
||||
s.Clear()
|
||||
t.Assert(s.Size(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntSet_Basic(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.NewIntSet()
|
||||
s.Add(1).Add(1).Add(2)
|
||||
s.Add(1, 1, 2)
|
||||
s.Add([]int{3, 4}...)
|
||||
t.Assert(s.Size(), 4)
|
||||
t.AssertIN(1, s.Slice())
|
||||
@ -43,7 +65,7 @@ func TestIntSet_Basic(t *testing.T) {
|
||||
func TestIntSet_Iterator(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.NewIntSet()
|
||||
s.Add(1).Add(2).Add(3)
|
||||
s.Add(1, 2, 3)
|
||||
t.Assert(s.Size(), 3)
|
||||
|
||||
a1 := garray.New(true)
|
||||
@ -64,7 +86,7 @@ func TestIntSet_Iterator(t *testing.T) {
|
||||
func TestIntSet_LockFunc(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.NewIntSet()
|
||||
s.Add(1).Add(2).Add(3)
|
||||
s.Add(1, 2, 3)
|
||||
t.Assert(s.Size(), 3)
|
||||
s.LockFunc(func(m map[int]struct{}) {
|
||||
delete(m, 1)
|
||||
@ -84,9 +106,9 @@ func TestIntSet_Equal(t *testing.T) {
|
||||
s1 := gset.NewIntSet()
|
||||
s2 := gset.NewIntSet()
|
||||
s3 := gset.NewIntSet()
|
||||
s1.Add(1).Add(2).Add(3)
|
||||
s2.Add(1).Add(2).Add(3)
|
||||
s3.Add(1).Add(2).Add(3).Add(4)
|
||||
s1.Add(1, 2, 3)
|
||||
s2.Add(1, 2, 3)
|
||||
s3.Add(1, 2, 3, 4)
|
||||
t.Assert(s1.Equal(s2), true)
|
||||
t.Assert(s1.Equal(s3), false)
|
||||
})
|
||||
@ -97,9 +119,9 @@ func TestIntSet_IsSubsetOf(t *testing.T) {
|
||||
s1 := gset.NewIntSet()
|
||||
s2 := gset.NewIntSet()
|
||||
s3 := gset.NewIntSet()
|
||||
s1.Add(1).Add(2)
|
||||
s2.Add(1).Add(2).Add(3)
|
||||
s3.Add(1).Add(2).Add(3).Add(4)
|
||||
s1.Add(1, 2)
|
||||
s2.Add(1, 2, 3)
|
||||
s3.Add(1, 2, 3, 4)
|
||||
t.Assert(s1.IsSubsetOf(s2), true)
|
||||
t.Assert(s2.IsSubsetOf(s3), true)
|
||||
t.Assert(s1.IsSubsetOf(s3), true)
|
||||
@ -112,8 +134,8 @@ func TestIntSet_Union(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := gset.NewIntSet()
|
||||
s2 := gset.NewIntSet()
|
||||
s1.Add(1).Add(2)
|
||||
s2.Add(3).Add(4)
|
||||
s1.Add(1, 2)
|
||||
s2.Add(3, 4)
|
||||
s3 := s1.Union(s2)
|
||||
t.Assert(s3.Contains(1), true)
|
||||
t.Assert(s3.Contains(2), true)
|
||||
@ -126,8 +148,8 @@ func TestIntSet_Diff(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := gset.NewIntSet()
|
||||
s2 := gset.NewIntSet()
|
||||
s1.Add(1).Add(2).Add(3)
|
||||
s2.Add(3).Add(4).Add(5)
|
||||
s1.Add(1, 2, 3)
|
||||
s2.Add(3, 4, 5)
|
||||
s3 := s1.Diff(s2)
|
||||
t.Assert(s3.Contains(1), true)
|
||||
t.Assert(s3.Contains(2), true)
|
||||
@ -140,8 +162,8 @@ func TestIntSet_Intersect(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := gset.NewIntSet()
|
||||
s2 := gset.NewIntSet()
|
||||
s1.Add(1).Add(2).Add(3)
|
||||
s2.Add(3).Add(4).Add(5)
|
||||
s1.Add(1, 2, 3)
|
||||
s2.Add(3, 4, 5)
|
||||
s3 := s1.Intersect(s2)
|
||||
t.Assert(s3.Contains(1), false)
|
||||
t.Assert(s3.Contains(2), false)
|
||||
@ -154,8 +176,8 @@ func TestIntSet_Complement(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := gset.NewIntSet()
|
||||
s2 := gset.NewIntSet()
|
||||
s1.Add(1).Add(2).Add(3)
|
||||
s2.Add(3).Add(4).Add(5)
|
||||
s1.Add(1, 2, 3)
|
||||
s2.Add(3, 4, 5)
|
||||
s3 := s1.Complement(s2)
|
||||
t.Assert(s3.Contains(1), false)
|
||||
t.Assert(s3.Contains(2), false)
|
||||
@ -167,7 +189,7 @@ func TestIntSet_Complement(t *testing.T) {
|
||||
func TestIntSet_Size(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := gset.NewIntSet(true)
|
||||
s1.Add(1).Add(2).Add(3)
|
||||
s1.Add(1, 2, 3)
|
||||
t.Assert(s1.Size(), 3)
|
||||
|
||||
})
|
||||
@ -178,8 +200,8 @@ func TestIntSet_Merge(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := gset.NewIntSet()
|
||||
s2 := gset.NewIntSet()
|
||||
s1.Add(1).Add(2).Add(3)
|
||||
s2.Add(3).Add(4).Add(5)
|
||||
s1.Add(1, 2, 3)
|
||||
s2.Add(3, 4, 5)
|
||||
s3 := s1.Merge(s2)
|
||||
t.Assert(s3.Contains(1), true)
|
||||
t.Assert(s3.Contains(5), true)
|
||||
@ -190,7 +212,7 @@ func TestIntSet_Merge(t *testing.T) {
|
||||
func TestIntSet_Join(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := gset.NewIntSet()
|
||||
s1.Add(1).Add(2).Add(3)
|
||||
s1.Add(1, 2, 3)
|
||||
s3 := s1.Join(",")
|
||||
t.Assert(strings.Contains(s3, "1"), true)
|
||||
t.Assert(strings.Contains(s3, "2"), true)
|
||||
@ -201,7 +223,7 @@ func TestIntSet_Join(t *testing.T) {
|
||||
func TestIntSet_String(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := gset.NewIntSet()
|
||||
s1.Add(1).Add(2).Add(3)
|
||||
s1.Add(1, 2, 3)
|
||||
s3 := s1.String()
|
||||
t.Assert(strings.Contains(s3, "["), true)
|
||||
t.Assert(strings.Contains(s3, "]"), true)
|
||||
@ -214,9 +236,9 @@ func TestIntSet_String(t *testing.T) {
|
||||
func TestIntSet_Sum(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := gset.NewIntSet()
|
||||
s1.Add(1).Add(2).Add(3)
|
||||
s1.Add(1, 2, 3)
|
||||
s2 := gset.NewIntSet()
|
||||
s2.Add(5).Add(6).Add(7)
|
||||
s2.Add(5, 6, 7)
|
||||
t.Assert(s2.Sum(), 18)
|
||||
|
||||
})
|
||||
@ -226,7 +248,7 @@ func TestIntSet_Sum(t *testing.T) {
|
||||
func TestIntSet_Pop(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.NewIntSet()
|
||||
s.Add(4).Add(2).Add(3)
|
||||
s.Add(4, 2, 3)
|
||||
t.Assert(s.Size(), 3)
|
||||
t.AssertIN(s.Pop(), []int{4, 2, 3})
|
||||
t.AssertIN(s.Pop(), []int{4, 2, 3})
|
||||
@ -237,7 +259,7 @@ func TestIntSet_Pop(t *testing.T) {
|
||||
func TestIntSet_Pops(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.NewIntSet()
|
||||
s.Add(1).Add(4).Add(2).Add(3)
|
||||
s.Add(1, 4, 2, 3)
|
||||
t.Assert(s.Size(), 4)
|
||||
t.Assert(s.Pops(0), nil)
|
||||
t.AssertIN(s.Pops(1), []int{1, 4, 2, 3})
|
||||
@ -258,6 +280,74 @@ func TestIntSet_Pops(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntSet_AddIfNotExist(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.NewIntSet(true)
|
||||
s.Add(1)
|
||||
t.Assert(s.Contains(1), true)
|
||||
t.Assert(s.AddIfNotExist(1), false)
|
||||
t.Assert(s.AddIfNotExist(2), true)
|
||||
t.Assert(s.Contains(2), true)
|
||||
t.Assert(s.AddIfNotExist(2), false)
|
||||
t.Assert(s.Contains(2), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntSet_AddIfNotExistFunc(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.NewIntSet(true)
|
||||
s.Add(1)
|
||||
t.Assert(s.Contains(1), true)
|
||||
t.Assert(s.Contains(2), false)
|
||||
t.Assert(s.AddIfNotExistFunc(2, func() bool { return false }), false)
|
||||
t.Assert(s.Contains(2), false)
|
||||
t.Assert(s.AddIfNotExistFunc(2, func() bool { return true }), true)
|
||||
t.Assert(s.Contains(2), true)
|
||||
t.Assert(s.AddIfNotExistFunc(2, func() bool { return true }), false)
|
||||
t.Assert(s.Contains(2), true)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.NewIntSet(true)
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
r := s.AddIfNotExistFunc(1, func() bool {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
return true
|
||||
})
|
||||
t.Assert(r, false)
|
||||
}()
|
||||
s.Add(1)
|
||||
wg.Wait()
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntSet_AddIfNotExistFuncLock(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.NewIntSet(true)
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
r := s.AddIfNotExistFuncLock(1, func() bool {
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
return true
|
||||
})
|
||||
t.Assert(r, true)
|
||||
}()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
r := s.AddIfNotExistFuncLock(1, func() bool {
|
||||
return true
|
||||
})
|
||||
t.Assert(r, false)
|
||||
}()
|
||||
wg.Wait()
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntSet_Json(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []int{1, 3, 2, 4}
|
||||
@ -287,43 +377,16 @@ func TestIntSet_Json(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntSet_AddIfNotExistFunc(t *testing.T) {
|
||||
func TestIntSet_Walk(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.NewIntSet(true)
|
||||
s.Add(1)
|
||||
t.Assert(s.Contains(1), true)
|
||||
t.Assert(s.Contains(2), false)
|
||||
|
||||
s.AddIfNotExistFunc(2, func() int {
|
||||
return 3
|
||||
var set gset.IntSet
|
||||
set.Add(g.SliceInt{1, 2}...)
|
||||
set.Walk(func(item int) int {
|
||||
return item + 10
|
||||
})
|
||||
t.Assert(s.Contains(2), false)
|
||||
t.Assert(s.Contains(3), true)
|
||||
|
||||
s.AddIfNotExistFunc(3, func() int {
|
||||
return 4
|
||||
})
|
||||
t.Assert(s.Contains(3), true)
|
||||
t.Assert(s.Contains(4), false)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.NewIntSet(true)
|
||||
s.Add(1)
|
||||
t.Assert(s.Contains(1), true)
|
||||
t.Assert(s.Contains(2), false)
|
||||
|
||||
s.AddIfNotExistFuncLock(2, func() int {
|
||||
return 3
|
||||
})
|
||||
t.Assert(s.Contains(2), false)
|
||||
t.Assert(s.Contains(3), true)
|
||||
|
||||
s.AddIfNotExistFuncLock(3, func() int {
|
||||
return 4
|
||||
})
|
||||
t.Assert(s.Contains(3), true)
|
||||
t.Assert(s.Contains(4), false)
|
||||
t.Assert(set.Size(), 2)
|
||||
t.Assert(set.Contains(11), true)
|
||||
t.Assert(set.Contains(12), true)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -9,21 +9,23 @@
|
||||
package gset_test
|
||||
|
||||
import (
|
||||
"encoding/json"
|
||||
"github.com/gogf/gf/frame/g"
|
||||
"github.com/gogf/gf/internal/json"
|
||||
"github.com/gogf/gf/util/gconv"
|
||||
"strings"
|
||||
"sync"
|
||||
"testing"
|
||||
"time"
|
||||
|
||||
"github.com/gogf/gf/container/garray"
|
||||
"github.com/gogf/gf/container/gset"
|
||||
"github.com/gogf/gf/test/gtest"
|
||||
)
|
||||
|
||||
func TestStrSet_Basic(t *testing.T) {
|
||||
func TestStrSet_Var(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.NewStrSet()
|
||||
s.Add("1").Add("1").Add("2")
|
||||
var s gset.StrSet
|
||||
s.Add("1", "1", "2")
|
||||
s.Add([]string{"3", "4"}...)
|
||||
t.Assert(s.Size(), 4)
|
||||
t.AssertIN("1", s.Slice())
|
||||
@ -40,10 +42,40 @@ func TestStrSet_Basic(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrSet_Basic(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.NewStrSet()
|
||||
s.Add("1", "1", "2")
|
||||
s.Add([]string{"3", "4"}...)
|
||||
t.Assert(s.Size(), 4)
|
||||
t.AssertIN("1", s.Slice())
|
||||
t.AssertIN("2", s.Slice())
|
||||
t.AssertIN("3", s.Slice())
|
||||
t.AssertIN("4", s.Slice())
|
||||
t.AssertNI("0", s.Slice())
|
||||
t.Assert(s.Contains("4"), true)
|
||||
t.Assert(s.Contains("5"), false)
|
||||
s.Remove("1")
|
||||
t.Assert(s.Size(), 3)
|
||||
s.Clear()
|
||||
t.Assert(s.Size(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrSet_ContainsI(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.NewStrSet()
|
||||
s.Add("a", "b", "C")
|
||||
t.Assert(s.Contains("A"), false)
|
||||
t.Assert(s.Contains("a"), true)
|
||||
t.Assert(s.ContainsI("A"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrSet_Iterator(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.NewStrSet()
|
||||
s.Add("1").Add("2").Add("3")
|
||||
s.Add("1", "2", "3")
|
||||
t.Assert(s.Size(), 3)
|
||||
|
||||
a1 := garray.New(true)
|
||||
@ -64,7 +96,7 @@ func TestStrSet_Iterator(t *testing.T) {
|
||||
func TestStrSet_LockFunc(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.NewStrSet()
|
||||
s.Add("1").Add("2").Add("3")
|
||||
s.Add("1", "2", "3")
|
||||
t.Assert(s.Size(), 3)
|
||||
s.LockFunc(func(m map[string]struct{}) {
|
||||
delete(m, "1")
|
||||
@ -84,9 +116,9 @@ func TestStrSet_Equal(t *testing.T) {
|
||||
s1 := gset.NewStrSet()
|
||||
s2 := gset.NewStrSet()
|
||||
s3 := gset.NewStrSet()
|
||||
s1.Add("1").Add("2").Add("3")
|
||||
s2.Add("1").Add("2").Add("3")
|
||||
s3.Add("1").Add("2").Add("3").Add("4")
|
||||
s1.Add("1", "2", "3")
|
||||
s2.Add("1", "2", "3")
|
||||
s3.Add("1", "2", "3", "4")
|
||||
t.Assert(s1.Equal(s2), true)
|
||||
t.Assert(s1.Equal(s3), false)
|
||||
})
|
||||
@ -97,9 +129,9 @@ func TestStrSet_IsSubsetOf(t *testing.T) {
|
||||
s1 := gset.NewStrSet()
|
||||
s2 := gset.NewStrSet()
|
||||
s3 := gset.NewStrSet()
|
||||
s1.Add("1").Add("2")
|
||||
s2.Add("1").Add("2").Add("3")
|
||||
s3.Add("1").Add("2").Add("3").Add("4")
|
||||
s1.Add("1", "2")
|
||||
s2.Add("1", "2", "3")
|
||||
s3.Add("1", "2", "3", "4")
|
||||
t.Assert(s1.IsSubsetOf(s2), true)
|
||||
t.Assert(s2.IsSubsetOf(s3), true)
|
||||
t.Assert(s1.IsSubsetOf(s3), true)
|
||||
@ -112,8 +144,8 @@ func TestStrSet_Union(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := gset.NewStrSet()
|
||||
s2 := gset.NewStrSet()
|
||||
s1.Add("1").Add("2")
|
||||
s2.Add("3").Add("4")
|
||||
s1.Add("1", "2")
|
||||
s2.Add("3", "4")
|
||||
s3 := s1.Union(s2)
|
||||
t.Assert(s3.Contains("1"), true)
|
||||
t.Assert(s3.Contains("2"), true)
|
||||
@ -126,8 +158,8 @@ func TestStrSet_Diff(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := gset.NewStrSet()
|
||||
s2 := gset.NewStrSet()
|
||||
s1.Add("1").Add("2").Add("3")
|
||||
s2.Add("3").Add("4").Add("5")
|
||||
s1.Add("1", "2", "3")
|
||||
s2.Add("3", "4", "5")
|
||||
s3 := s1.Diff(s2)
|
||||
t.Assert(s3.Contains("1"), true)
|
||||
t.Assert(s3.Contains("2"), true)
|
||||
@ -140,8 +172,8 @@ func TestStrSet_Intersect(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := gset.NewStrSet()
|
||||
s2 := gset.NewStrSet()
|
||||
s1.Add("1").Add("2").Add("3")
|
||||
s2.Add("3").Add("4").Add("5")
|
||||
s1.Add("1", "2", "3")
|
||||
s2.Add("3", "4", "5")
|
||||
s3 := s1.Intersect(s2)
|
||||
t.Assert(s3.Contains("1"), false)
|
||||
t.Assert(s3.Contains("2"), false)
|
||||
@ -154,8 +186,8 @@ func TestStrSet_Complement(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := gset.NewStrSet()
|
||||
s2 := gset.NewStrSet()
|
||||
s1.Add("1").Add("2").Add("3")
|
||||
s2.Add("3").Add("4").Add("5")
|
||||
s1.Add("1", "2", "3")
|
||||
s2.Add("3", "4", "5")
|
||||
s3 := s1.Complement(s2)
|
||||
t.Assert(s3.Contains("1"), false)
|
||||
t.Assert(s3.Contains("2"), false)
|
||||
@ -179,8 +211,8 @@ func TestStrSet_Merge(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := gset.NewStrSet()
|
||||
s2 := gset.NewStrSet()
|
||||
s1.Add("1").Add("2").Add("3")
|
||||
s2.Add("3").Add("4").Add("5")
|
||||
s1.Add("1", "2", "3")
|
||||
s2.Add("3", "4", "5")
|
||||
s3 := s1.Merge(s2)
|
||||
t.Assert(s3.Contains("1"), true)
|
||||
t.Assert(s3.Contains("6"), false)
|
||||
@ -207,7 +239,7 @@ func TestStrSet_Join(t *testing.T) {
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := gset.NewStrSet()
|
||||
s1.Add("a").Add(`"b"`).Add(`\c`)
|
||||
s1.Add("a", `"b"`, `\c`)
|
||||
str1 := s1.Join(",")
|
||||
t.Assert(strings.Contains(str1, `"b"`), true)
|
||||
t.Assert(strings.Contains(str1, `\c`), true)
|
||||
@ -225,7 +257,7 @@ func TestStrSet_String(t *testing.T) {
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := gset.New(true)
|
||||
s1.Add("a").Add("a2").Add("b").Add("c")
|
||||
s1.Add("a", "a2", "b", "c")
|
||||
str1 := s1.String()
|
||||
t.Assert(strings.Contains(str1, "["), true)
|
||||
t.Assert(strings.Contains(str1, "]"), true)
|
||||
@ -253,7 +285,7 @@ func TestStrSet_Size(t *testing.T) {
|
||||
func TestStrSet_Remove(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := gset.NewStrSetFrom([]string{"a", "b", "c"}, true)
|
||||
s1 = s1.Remove("b")
|
||||
s1.Remove("b")
|
||||
t.Assert(s1.Contains("b"), false)
|
||||
t.Assert(s1.Contains("c"), true)
|
||||
})
|
||||
@ -294,6 +326,74 @@ func TestStrSet_Pops(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrSet_AddIfNotExist(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.NewStrSet(true)
|
||||
s.Add("1")
|
||||
t.Assert(s.Contains("1"), true)
|
||||
t.Assert(s.AddIfNotExist("1"), false)
|
||||
t.Assert(s.AddIfNotExist("2"), true)
|
||||
t.Assert(s.Contains("2"), true)
|
||||
t.Assert(s.AddIfNotExist("2"), false)
|
||||
t.Assert(s.Contains("2"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrSet_AddIfNotExistFunc(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.NewStrSet(true)
|
||||
s.Add("1")
|
||||
t.Assert(s.Contains("1"), true)
|
||||
t.Assert(s.Contains("2"), false)
|
||||
t.Assert(s.AddIfNotExistFunc("2", func() bool { return false }), false)
|
||||
t.Assert(s.Contains("2"), false)
|
||||
t.Assert(s.AddIfNotExistFunc("2", func() bool { return true }), true)
|
||||
t.Assert(s.Contains("2"), true)
|
||||
t.Assert(s.AddIfNotExistFunc("2", func() bool { return true }), false)
|
||||
t.Assert(s.Contains("2"), true)
|
||||
})
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.NewStrSet(true)
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(1)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
r := s.AddIfNotExistFunc("1", func() bool {
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
return true
|
||||
})
|
||||
t.Assert(r, false)
|
||||
}()
|
||||
s.Add("1")
|
||||
wg.Wait()
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrSet_AddIfNotExistFuncLock(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.NewStrSet(true)
|
||||
wg := sync.WaitGroup{}
|
||||
wg.Add(2)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
r := s.AddIfNotExistFuncLock("1", func() bool {
|
||||
time.Sleep(500 * time.Millisecond)
|
||||
return true
|
||||
})
|
||||
t.Assert(r, true)
|
||||
}()
|
||||
time.Sleep(100 * time.Millisecond)
|
||||
go func() {
|
||||
defer wg.Done()
|
||||
r := s.AddIfNotExistFuncLock("1", func() bool {
|
||||
return true
|
||||
})
|
||||
t.Assert(r, false)
|
||||
}()
|
||||
wg.Wait()
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrSet_Json(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s1 := []string{"a", "b", "d", "c"}
|
||||
@ -323,43 +423,21 @@ func TestStrSet_Json(t *testing.T) {
|
||||
})
|
||||
}
|
||||
|
||||
func TestStrSet_AddIfNotExistFunc(t *testing.T) {
|
||||
func TestStrSet_Walk(t *testing.T) {
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.NewStrSet(true)
|
||||
s.Add("1")
|
||||
t.Assert(s.Contains("1"), true)
|
||||
t.Assert(s.Contains("2"), false)
|
||||
|
||||
s.AddIfNotExistFunc("2", func() string {
|
||||
return "3"
|
||||
var (
|
||||
set gset.StrSet
|
||||
names = g.SliceStr{"user", "user_detail"}
|
||||
prefix = "gf_"
|
||||
)
|
||||
set.Add(names...)
|
||||
// Add prefix for given table names.
|
||||
set.Walk(func(item string) string {
|
||||
return prefix + item
|
||||
})
|
||||
t.Assert(s.Contains("2"), false)
|
||||
t.Assert(s.Contains("3"), true)
|
||||
|
||||
s.AddIfNotExistFunc("3", func() string {
|
||||
return "4"
|
||||
})
|
||||
t.Assert(s.Contains("3"), true)
|
||||
t.Assert(s.Contains("4"), false)
|
||||
})
|
||||
|
||||
gtest.C(t, func(t *gtest.T) {
|
||||
s := gset.NewStrSet(true)
|
||||
s.Add("1")
|
||||
t.Assert(s.Contains("1"), true)
|
||||
t.Assert(s.Contains("2"), false)
|
||||
|
||||
s.AddIfNotExistFuncLock("2", func() string {
|
||||
return "3"
|
||||
})
|
||||
t.Assert(s.Contains("2"), false)
|
||||
t.Assert(s.Contains("3"), true)
|
||||
|
||||
s.AddIfNotExistFuncLock("3", func() string {
|
||||
return "4"
|
||||
})
|
||||
t.Assert(s.Contains("3"), true)
|
||||
t.Assert(s.Contains("4"), false)
|
||||
t.Assert(set.Size(), 2)
|
||||
t.Assert(set.Contains("gf_user"), true)
|
||||
t.Assert(set.Contains("gf_user_detail"), true)
|
||||
})
|
||||
}
|
||||
|
||||
|
||||
@ -5,4 +5,7 @@
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gtree provides concurrent-safe/unsafe tree containers.
|
||||
//
|
||||
// Some implements are from: https://github.com/emirpasic/gods
|
||||
// Thanks!
|
||||
package gtree
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user