mirror of
https://gitee.com/johng/gf
synced 2026-06-07 18:26:02 +08:00
Compare commits
62 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 9797701881 | |||
| 8a50b180c0 | |||
| 159190d187 | |||
| 989d543a1f | |||
| fcc3a1b2f6 | |||
| cfdeb87093 | |||
| 74eef34ec2 | |||
| 2e87d5322f | |||
| e89a49f39a | |||
| 5d3fd91f0b | |||
| f455d22893 | |||
| c00f528098 | |||
| 6f02ad60eb | |||
| ca6c0791ae | |||
| ee485dbfd8 | |||
| 2ecaa12647 | |||
| 580e099cb7 | |||
| 83e50f0d38 | |||
| 48c770b475 | |||
| ba67101942 | |||
| 72efde09b3 | |||
| 76c49170bd | |||
| 150aa7e2c2 | |||
| 0762fec696 | |||
| 1447496efa | |||
| 6fb9eafef0 | |||
| 16bde5e9fc | |||
| 7b85c44444 | |||
| 87d553fca2 | |||
| ba050d4c86 | |||
| 90cd7f49fd | |||
| 29b42290c7 | |||
| 6c6c64bb3b | |||
| ae7db2cf9f | |||
| 1afab62dec | |||
| 4171ca992e | |||
| e1166a8a80 | |||
| 74a7f71894 | |||
| f2e149d3b3 | |||
| 92c0bf9cdc | |||
| 020b050fb1 | |||
| 08d71cede3 | |||
| be1f6cfbae | |||
| 9aa5e139cf | |||
| 1d5c3d62dd | |||
| 0e611ae94b | |||
| 908a46d27d | |||
| 85677b952f | |||
| 5eaa6183b5 | |||
| 4036d40c58 | |||
| 0b80cbb0dc | |||
| 79cb386d82 | |||
| 09be68831b | |||
| 0ac13c2b81 | |||
| 94ef38e3cc | |||
| 76882ac01c | |||
| 59945fbe91 | |||
| c4962ec017 | |||
| 58d60bc899 | |||
| 5dd0a78423 | |||
| 141ea7cc2d | |||
| a488d1dbf7 |
@ -9,7 +9,7 @@ branches:
|
||||
- develop
|
||||
|
||||
env:
|
||||
- GITEE_GF=$GOPATH/src/gitee.com/johng/gf GO111MODULE=on
|
||||
- GO111MODULE=on
|
||||
|
||||
services:
|
||||
- mysql
|
||||
@ -19,11 +19,9 @@ before_install:
|
||||
|
||||
install:
|
||||
- pwd
|
||||
- mkdir -p $GITEE_GF
|
||||
- cp * $GITEE_GF -R
|
||||
- cd $GITEE_GF/g
|
||||
|
||||
script:
|
||||
- cd g
|
||||
- GOARCH=386 go test -v ./...
|
||||
- GOARCH=amd64 go test -v ./... -race -coverprofile=coverage.txt -covermode=atomic
|
||||
|
||||
|
||||
2
LICENSE
2
LICENSE
@ -1,6 +1,6 @@
|
||||
MIT License
|
||||
|
||||
Copyright (c) 2017 john@johng.cn http://johng.cn
|
||||
Copyright (c) 2017 john@goframe.org https://goframe.org
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining a copy
|
||||
of this software and associated documentation files (the "Software"), to deal
|
||||
|
||||
27
README.MD
27
README.MD
@ -1,10 +1,10 @@
|
||||
# GoFrame
|
||||
<img align="right" height="150px" src="https://gfer.me/cover.png">
|
||||
<img align="right" height="150px" src="https://goframe.org/cover.png">
|
||||
|
||||
[](https://godoc.org/github.com/gogf/gf)
|
||||
[](https://travis-ci.org/gogf/gf)
|
||||
[](https://goreportcard.com/report/github.com/gogf/gf)
|
||||
[](https://gfer.me)
|
||||
[](https://goframe.org)
|
||||
[](https://github.com/gogf/gf)
|
||||
[](https://github.com/gogf/gf)
|
||||
[](https://github.com/gogf/gf/releases)
|
||||
@ -14,15 +14,15 @@
|
||||
[](https://www.codetriage.com/gogf/gf)
|
||||
-->
|
||||
|
||||
`GF(GoFrame)` is a modular, lightweight, loosely coupled, high performance application development framework written in Go. Supporting graceful server, hot updates, multi-domain, multi-port, multi-service, HTTP/HTTPS, dynamic/hook routing and many more features. Providing a series of core components and dozens of practical modules.
|
||||
`GF(GoFrame)` is a modular, loose-coupled and production-ready application development framework written in Go. Providing a series of core components and dozens of practical modules, such as: cache, logging, array/queue/set/map, timer/timing tasks, file/memory lock, object pool, validator, database ORM, etc. Supporting web server with graceful server, hot updates, multi-domain, multi-port, multi-service, HTTP/HTTPS, dynamic/hook routing, rewrite rules and many more features.
|
||||
|
||||
# Installation
|
||||
```
|
||||
go get -u gitee.com/johng/gf
|
||||
go get -u github.com/gogf/gf
|
||||
```
|
||||
or use `go.mod`
|
||||
or use `go.mod`:
|
||||
```
|
||||
require gitee.com/johng/gf latest
|
||||
require github.com/gogf/gf latest
|
||||
```
|
||||
|
||||
# Limitation
|
||||
@ -32,11 +32,12 @@ golang version >= 1.9.2
|
||||
|
||||
# Documentation
|
||||
|
||||
* [GoDoc](https://godoc.org/github.com/gogf/gf)
|
||||
* [中文文档](https://goframe.org)
|
||||
|
||||
# Architecture
|
||||
<div align=center>
|
||||
<img src="https://gfer.me/images/arch.png"/>
|
||||
<img src="https://goframe.org/images/arch.png"/>
|
||||
</div>
|
||||
|
||||
# Quick Start
|
||||
@ -45,8 +46,8 @@ golang version >= 1.9.2
|
||||
package main
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g"
|
||||
"gitee.com/johng/gf/g/net/ghttp"
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -58,7 +59,7 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
[View More..](https://gfer.me/start/index)
|
||||
[View More..](https://goframe.org/start/index)
|
||||
|
||||
|
||||
# License
|
||||
@ -71,7 +72,7 @@ func main() {
|
||||
|
||||
<a href="https://gitee.com/wenzi1" target="_blank" title="蚊子"><img src="https://images.gitee.com/uploads/22/1923122_wenzi1.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/zseeker" target="_blank" title="zseeker"><img src="https://gfer.me/images/contributors/zseeker.png" width="60" align="left"></a>
|
||||
<a href="https://gitee.com/zseeker" target="_blank" title="zseeker"><img src="https://goframe.org/images/contributors/zseeker.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/ymrjqyy" target="_blank" title="一墨染尽青衣颜"><img src="https://images.gitee.com/uploads/27/876827_ymrjqyy.png" width="60" align="left"></a>
|
||||
|
||||
@ -84,7 +85,7 @@ func main() {
|
||||
|
||||
<a href="https://gitee.com/zhangjinfu" target="_blank" title="张金富"><img src="https://images.gitee.com/uploads/63/356163_zhangjinfu.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/garfieldkwong" target="_blank" title="GarfieldKwong"><img src="https://gfer.me/images/contributors/garfieldkwong.png" width="60" align="left"></a>
|
||||
<a href="https://gitee.com/garfieldkwong" target="_blank" title="GarfieldKwong"><img src="https://goframe.org/images/contributors/garfieldkwong.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/qq1054000800" target="_blank" title="hello"><img src="https://gitee.com/uploads/9/2209_qq1054000800.jpg" width="60" align="left"></a>
|
||||
|
||||
@ -92,6 +93,8 @@ func main() {
|
||||
|
||||
# Donators
|
||||
|
||||
<a href="https://gitee.com/tiangenglan" target="_blank" title="zhuhuan12"><img src="https://images.gitee.com/uploads/99/1167099_tiangenglan.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/zhuhuan12" target="_blank" title="zhuhuan12"><img src="https://gitee.com/uploads/39/751839_zhuhuan12.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/zfan_codes" target="_blank" title="范钟"><img src="https://images.gitee.com/uploads/32/2044832_zfan_codes.png" width="60" align="left"></a>
|
||||
|
||||
32
README_ZH.MD
32
README_ZH.MD
@ -1,10 +1,10 @@
|
||||
# GoFrame
|
||||
<img align="right" height="150px" src="https://gfer.me/cover.png">
|
||||
<img align="right" height="150px" src="https://goframe.org/cover.png">
|
||||
|
||||
[](https://godoc.org/github.com/gogf/gf)
|
||||
[](https://travis-ci.org/gogf/gf)
|
||||
[](https://goreportcard.com/report/github.com/gogf/gf)
|
||||
[](https://gfer.me)
|
||||
[](https://goframe.org)
|
||||
[](https://github.com/gogf/gf)
|
||||
[](https://github.com/gogf/gf)
|
||||
[](https://github.com/gogf/gf/releases)
|
||||
@ -14,10 +14,8 @@
|
||||
[](https://www.codetriage.com/gogf/gf)
|
||||
-->
|
||||
|
||||
`GF(Go Frame)`是一款模块化、松耦合、轻量级、高性能的Go应用开发框架。支持热重启、热更新、多域名、多端口、多服务、HTTP/HTTPS、动态路由等特性
|
||||
,并提供了Web服务开发的系列核心组件,如:Router、Cookie、Session、服务注册、配置管理、模板引擎、数据校验、分页管理、数据库ORM等等等等,
|
||||
并且提供了数十个内置核心开发模块集,如:缓存、日志、时间、命令行、二进制、文件锁、内存锁、对象池、连接池、数据编码、进程管理、进程通信、文件监控、定时任务、TCP/UDP组件、
|
||||
并发安全容器等等等等等等。
|
||||
`GF(Go Frame)`是一款模块化、松耦合、生产级Go应用开发框架。提供了常用的核心开发组件,如:缓存、日志、文件、时间、队列、数组、集合、字符串、定时器、命令行、文件锁、内存锁、对象池、连接池、数据校验、数据编码、文件监控、定时任务、数据库ORM、TCP/UDP组件、进程管理/通信、
|
||||
并发安全容器等等。并提供了Web服务开发的系列核心组件,如:Router、Cookie、Session、服务注册、配置管理、模板引擎等等,支持热重启、热更新、多域名、多端口、多服务、HTTPS、Rewrite等特性。
|
||||
|
||||
|
||||
# 特点
|
||||
@ -31,13 +29,15 @@
|
||||
|
||||
# 安装
|
||||
```html
|
||||
go get -u gitee.com/johng/gf
|
||||
go get -u github.com/gogf/gf
|
||||
```
|
||||
或者
|
||||
`go.mod`
|
||||
`go.mod`:
|
||||
```
|
||||
require gitee.com/johng/gf latest
|
||||
require github.com/gogf/gf latest
|
||||
```
|
||||
> 如果您是从旧版本`1.x`升级到`1.5.0`那么请参考:[1.x升级到1.5.0](https://goframe.org/upgradeto150)
|
||||
|
||||
# 限制
|
||||
```shell
|
||||
golang版本 >= 1.9.2
|
||||
@ -45,7 +45,7 @@ golang版本 >= 1.9.2
|
||||
|
||||
# 架构
|
||||
<div align=center>
|
||||
<img src="https://gfer.me/images/arch.png"/>
|
||||
<img src="https://goframe.org/images/arch.png"/>
|
||||
</div>
|
||||
|
||||
|
||||
@ -61,8 +61,8 @@ golang版本 >= 1.9.2
|
||||
package main
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g"
|
||||
"gitee.com/johng/gf/g/net/ghttp"
|
||||
"github.com/gogf/gf/g"
|
||||
"github.com/gogf/gf/g/net/ghttp"
|
||||
)
|
||||
|
||||
func main() {
|
||||
@ -74,7 +74,7 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
[更多..](https://gfer.me/start/index)
|
||||
[更多..](https://goframe.org/start/index)
|
||||
|
||||
|
||||
# 协议
|
||||
@ -86,20 +86,22 @@ func main() {
|
||||
|
||||
<a href="https://gitee.com/johng" target="_blank" title="John"><img src="https://gitee.com/uploads/27/1309327_johng.png" width="60" align="left"></a>
|
||||
<a href="https://gitee.com/wenzi1" target="_blank" title="蚊子"><img src="https://images.gitee.com/uploads/22/1923122_wenzi1.png" width="60" align="left"></a>
|
||||
<a href="https://gitee.com/zseeker" target="_blank" title="zseeker"><img src="https://gfer.me/images/contributors/zseeker.png" width="60" align="left"></a>
|
||||
<a href="https://gitee.com/zseeker" target="_blank" title="zseeker"><img src="https://goframe.org/images/contributors/zseeker.png" width="60" align="left"></a>
|
||||
<a href="https://gitee.com/ymrjqyy" target="_blank" title="一墨染尽青衣颜"><img src="https://images.gitee.com/uploads/27/876827_ymrjqyy.png" width="60" align="left"></a>
|
||||
<a href="https://github.com/chenyang351" target="_blank" title="chenyang351"><img src="https://avatars1.githubusercontent.com/u/30063958?s=60&v=4" width="60" align="left"></a>
|
||||
<a href="https://gitee.com/wxkj" target="_blank" title="wxkj"><img src="https://gitee.com/uploads/56/91356_wxkj.png" width="60" align="left"></a>
|
||||
<a href="https://github.com/wxkj001" target="_blank" title="3wxkj001
|
||||
"><img src="https://avatars0.githubusercontent.com/u/7794279?s=60&v=4" width="60" align="left"></a>
|
||||
<a href="https://gitee.com/zhangjinfu" target="_blank" title="张金富"><img src="https://images.gitee.com/uploads/63/356163_zhangjinfu.png" width="60" align="left"></a>
|
||||
<a href="https://gitee.com/garfieldkwong" target="_blank" title="GarfieldKwong"><img src="https://gfer.me/images/contributors/garfieldkwong.png" width="60" align="left"></a>
|
||||
<a href="https://gitee.com/garfieldkwong" target="_blank" title="GarfieldKwong"><img src="https://goframe.org/images/contributors/garfieldkwong.png" width="60" align="left"></a>
|
||||
<a href="https://gitee.com/qq1054000800" target="_blank" title="hello"><img src="https://gitee.com/uploads/9/2209_qq1054000800.jpg" width="60" align="left"></a>
|
||||
|
||||
<br /><br /><br />
|
||||
|
||||
# 捐赠者
|
||||
|
||||
<a href="https://gitee.com/tiangenglan" target="_blank" title="zhuhuan12"><img src="https://images.gitee.com/uploads/99/1167099_tiangenglan.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/zhuhuan12" target="_blank" title="zhuhuan12"><img src="https://gitee.com/uploads/39/751839_zhuhuan12.png" width="60" align="left"></a>
|
||||
|
||||
<a href="https://gitee.com/zfan_codes" target="_blank" title="范钟"><img src="https://images.gitee.com/uploads/32/2044832_zfan_codes.png" width="60" align="left"></a>
|
||||
|
||||
42
RELEASE.MD
42
RELEASE.MD
@ -31,20 +31,20 @@
|
||||
# `v1.3.8` (2018-12-26)
|
||||
|
||||
## 新特性
|
||||
1. 对`gform`完成重构,以提高扩展性,并修复部分细节问题、完善单元测试用例([https://gfer.me/database/orm/index](https://gfer.me/database/orm/index));
|
||||
1. `WebServer`路由注册新增分组路由特性([https://gfer.me/net/ghttp/group](https://gfer.me/net/ghttp/group));
|
||||
1. `WebServer`新增`Rewrite`路由重写特性([https://gfer.me/net/ghttp/static](https://gfer.me/net/ghttp/static));
|
||||
1. 对`gform`完成重构,以提高扩展性,并修复部分细节问题、完善单元测试用例([https://goframe.org/database/orm/index](https://goframe.org/database/orm/index));
|
||||
1. `WebServer`路由注册新增分组路由特性([https://goframe.org/net/ghttp/group](https://goframe.org/net/ghttp/group));
|
||||
1. `WebServer`新增`Rewrite`路由重写特性([https://goframe.org/net/ghttp/static](https://goframe.org/net/ghttp/static));
|
||||
1. 增加框架运行时对开发环境的自动识别;
|
||||
1. 增加了`Travis CI`自动化构建/测试;
|
||||
|
||||
## 新功能
|
||||
1. 改进`WebServer`静态文件服务功能,增加`SetStaticPath`/`AddStaticPath`方法([https://gfer.me/net/ghttp/static](https://gfer.me/net/ghttp/static));
|
||||
1. `gform`新增`Filter`链式操作方法,用于过滤参数中的非表字段键值对([https://gfer.me/database/orm/linkop](https://gfer.me/database/orm/linkop));
|
||||
1. 改进`WebServer`静态文件服务功能,增加`SetStaticPath`/`AddStaticPath`方法([https://goframe.org/net/ghttp/static](https://goframe.org/net/ghttp/static));
|
||||
1. `gform`新增`Filter`链式操作方法,用于过滤参数中的非表字段键值对([https://goframe.org/database/orm/linkop](https://goframe.org/database/orm/linkop));
|
||||
1. `gcache`新增`Data`方法,用以获取所有的缓存数据项;
|
||||
1. `gredis`增加`GetConn`方法获取原生redis连接对象;
|
||||
|
||||
## 功能改进
|
||||
1. 改进`gform`的`Where`方法,支持`slice`类型的参数,并更方便地支持`in`操作查询([https://gfer.me/database/orm/linkop](https://gfer.me/database/orm/linkop));
|
||||
1. 改进`gform`的`Where`方法,支持`slice`类型的参数,并更方便地支持`in`操作查询([https://goframe.org/database/orm/linkop](https://goframe.org/database/orm/linkop));
|
||||
1. 改进`gproc`进程间通信数据结构,将`pid`字段从`16bit`扩展为`24bit`;
|
||||
1. 改进`gconv`/`gmap`/`garray`,增加若干操作方法;
|
||||
1. 改进`gview`模板引擎中的`date`内置函数,当给定的时间戳为空时打印当前的系统时间;
|
||||
@ -67,20 +67,20 @@
|
||||
|
||||
# `v1.2.11` (2018-11-26)
|
||||
## 新特性
|
||||
1. `ORM`新增对`SQLServer`及`Oracle`的支持([https://gfer.me/database/orm/database](https://gfer.me/database/orm/database));
|
||||
1. 完成`gvalid`模块校验结果的顺序特性([https://gfer.me/util/gvalid/checkmap](https://gfer.me/util/gvalid/checkmap));
|
||||
1. 改进`ghttp.Request.Exit`,使得调用该方法时立即退出业务执行,开发者无需调用`Exit`方法时再使用`return`返回([https://gfer.me/net/ghttp/service/object](https://gfer.me/net/ghttp/service/object));
|
||||
1. 模板引擎新增若干内置函数:`text/html/htmldecode/url/urldecode/date/compare/substr/strlimit/hidestr/highlight/toupper/tolower/nl2br` ([https://gfer.me/os/gview/funcs](https://gfer.me/os/gview/funcs));
|
||||
1. 模板引擎新增内置变量`Config` ([https://gfer.me/os/gview/vars](https://gfer.me/os/gview/vars));
|
||||
1. `ORM`新增对`SQLServer`及`Oracle`的支持([https://goframe.org/database/orm/database](https://goframe.org/database/orm/database));
|
||||
1. 完成`gvalid`模块校验结果的顺序特性([https://goframe.org/util/gvalid/checkmap](https://goframe.org/util/gvalid/checkmap));
|
||||
1. 改进`ghttp.Request.Exit`,使得调用该方法时立即退出业务执行,开发者无需调用`Exit`方法时再使用`return`返回([https://goframe.org/net/ghttp/service/object](https://goframe.org/net/ghttp/service/object));
|
||||
1. 模板引擎新增若干内置函数:`text/html/htmldecode/url/urldecode/date/compare/substr/strlimit/hidestr/highlight/toupper/tolower/nl2br` ([https://goframe.org/os/gview/funcs](https://goframe.org/os/gview/funcs));
|
||||
1. 模板引擎新增内置变量`Config` ([https://goframe.org/os/gview/vars](https://goframe.org/os/gview/vars));
|
||||
1. 改进`gconv.Struct`转换默认规则,支持不区分大小写的键名与属性名称匹配;
|
||||
1. `gform`配置文件支持`linkinfo`自定义数据库连接字段([https://gfer.me/database/orm/config](https://gfer.me/database/orm/config));
|
||||
1. `gfsnotify`模块增加对特定回调的取消注册功能([https://gfer.me/os/gfsnotify/index](https://gfer.me/os/gfsnotify/index));
|
||||
1. `gform`配置文件支持`linkinfo`自定义数据库连接字段([https://goframe.org/database/orm/config](https://goframe.org/database/orm/config));
|
||||
1. `gfsnotify`模块增加对特定回调的取消注册功能([https://goframe.org/os/gfsnotify/index](https://goframe.org/os/gfsnotify/index));
|
||||
|
||||
|
||||
|
||||
## 新功能
|
||||
1. 改进`ghttp.Request`,增加`SetParam/GetParam`请求流程自定义变量设置/获取方法,用于在请求流程中的回调函数共享变量([https://gfer.me/net/ghttp/request](https://gfer.me/net/ghttp/request));
|
||||
1. 改进`ghttp.Response`,增加`ServeFileDownload`方法,用于WebServer引导客户端下载文件([https://gfer.me/net/ghttp/response](https://gfer.me/net/ghttp/response));
|
||||
1. 改进`ghttp.Request`,增加`SetParam/GetParam`请求流程自定义变量设置/获取方法,用于在请求流程中的回调函数共享变量([https://goframe.org/net/ghttp/request](https://goframe.org/net/ghttp/request));
|
||||
1. 改进`ghttp.Response`,增加`ServeFileDownload`方法,用于WebServer引导客户端下载文件([https://goframe.org/net/ghttp/response](https://goframe.org/net/ghttp/response));
|
||||
1. `gvar`模块新增`gvar.VarRead`只读接口,用于控制对外只暴露数据读取功能;
|
||||
1. 增加`g.Throw`抛异常方法,`g.TryCatch`异常捕获方法封装;
|
||||
1. 改进`gcron`模块,增加自定义的Cron管理对象,增加`New/Start/Stop`方法;
|
||||
@ -88,9 +88,9 @@
|
||||
|
||||
## 功能改进
|
||||
1. WebServer添加`RouterCacheExpire`配置参数,用于设置路由检索缓存过期时间;
|
||||
1. WebServer允许同一`HOOK`事件被多次绑定注册,先注册的回调函数优先级更高([https://gfer.me/net/ghttp/service/hook](https://gfer.me/net/ghttp/service/hook));
|
||||
1. WebServer允许同一`HOOK`事件被多次绑定注册,先注册的回调函数优先级更高([https://goframe.org/net/ghttp/service/hook](https://goframe.org/net/ghttp/service/hook));
|
||||
1. 当前工作目录为系统临时目录时,`gcfg`/`gview`/`ghttp`模块默认不添加工作目录到搜索路径;
|
||||
1. 改进`WebSocket`默认支持跨域请求([https://gfer.me/net/ghttp/websocket](https://gfer.me/net/ghttp/websocket));
|
||||
1. 改进`WebSocket`默认支持跨域请求([https://goframe.org/net/ghttp/websocket](https://goframe.org/net/ghttp/websocket));
|
||||
1. 改进`gtime.Format`支持中文;
|
||||
1. 改进`gfsnotify`,支持编辑器对文件非执行标准编辑时(RENAME+CHMOD)的热更新问题;
|
||||
1. 改进`gtype.Set`方法,增加Set原子操作返回旧的变量值;
|
||||
@ -99,12 +99,12 @@
|
||||
1. `gstr`模块增加对中文截取方法;
|
||||
1. 改进`gtime.StrToTime`对常用时间格式匹配模式,新增`gtime.ParseTimeFromContent`方法;
|
||||
1. 修改配置管理、模板引擎、调试模式的环境变量名称为大写下划线标准格式;
|
||||
1. 改进`grand`模块随机数生成设计,底层使用`crypto/rand`+缓冲区实现高速的随机数生成([https://gfer.me/util/grand/index](https://gfer.me/util/grand/index));
|
||||
1. 改进`grand`模块随机数生成设计,底层使用`crypto/rand`+缓冲区实现高速的随机数生成([https://goframe.org/util/grand/index](https://goframe.org/util/grand/index));
|
||||
|
||||
## 问题修复
|
||||
1. 修复`gspath`模块在`windows`下搜索失效问题;
|
||||
1. 修复`gspath`模块Search时带有indexFiles的检索问题;
|
||||
1. bug fix INZS1([https://gitee.com/johng/gf/issues/INZS1](https://gitee.com/johng/gf/issues/INZS1));
|
||||
1. bug fix INZS1([https://github.com/gogf/gf/issues/INZS1](https://github.com/gogf/gf/issues/INZS1));
|
||||
1. 修复`gproc.ShellRun`在windows下的执行问题;
|
||||
|
||||
|
||||
@ -343,8 +343,8 @@
|
||||
12、gdb数据库ORM包增加And/Or条件链式方法,并改进Where/Data方法参数灵活性;
|
||||
13、对于新增加的模块,同时也增加了对应的开发文档,并梳理完善了现有的其他模块开发文档;
|
||||
14、修复ISSUE:
|
||||
#IISWI gitee.com/johng/gf/issues/IISWI,
|
||||
#IISMY gitee.com/johng/gf/issues/IISMY,
|
||||
#IISWI github.com/gogf/gf/issues/IISWI,
|
||||
#IISMY github.com/gogf/gf/issues/IISMY,
|
||||
反馈并跟踪完成第三方依赖mxj包的ISSUE修复(github.com/clbanning/mxj/issues/48);
|
||||
|
||||
|
||||
|
||||
12
TODO.MD
12
TODO.MD
@ -46,7 +46,17 @@
|
||||
1. grpool性能压测结果变慢的问题;
|
||||
1. 增加jumplist的数据结构容器;
|
||||
1. DelayQueue/PriorityQueue;
|
||||
1. gconv针对struct的转换增加json tag支持,gconv.Map默认也支持json tag;
|
||||
1. gconv针对struct的转换增加json tag支持,gconv.Map默认也支持json tag, 完善开发文档;
|
||||
1. 增加SO_REUSEPORT的支持;
|
||||
1. 权限管理模块;
|
||||
1. 从ghttp中剥离SESSION功能构成单独的模块gsession;
|
||||
1. 改进gproc进程间通信处理逻辑,提高稳定性,以应对进程间大批量的数据发送/接收;
|
||||
1. gdb的Data方法支持struct参数传入;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# DONE
|
||||
1. gconv完善针对不同类型的判断,例如:尽量减少sprintf("%v", xxx)来执行string类型的转换;
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package container
|
||||
|
||||
@ -1,14 +1,11 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package garray provides kinds of concurrent-safe(alternative) arrays.
|
||||
//
|
||||
// 并发安全的数组.
|
||||
// 并发安全数组.
|
||||
package garray
|
||||
|
||||
func New(size int, cap int, unsafe...bool) *Array {
|
||||
return NewArray(size, cap, unsafe...)
|
||||
}
|
||||
8
g/container/garray/garray_func.go
Normal file
8
g/container/garray/garray_func.go
Normal file
@ -0,0 +1,8 @@
|
||||
// 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 this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package garray
|
||||
|
||||
@ -1,176 +0,0 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf.
|
||||
|
||||
package garray
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/internal/rwmutex"
|
||||
)
|
||||
|
||||
type IntArray struct {
|
||||
mu *rwmutex.RWMutex // 互斥锁
|
||||
cap int // 初始化设置的数组容量
|
||||
size int // 初始化设置的数组大小
|
||||
array []int // 底层数组
|
||||
}
|
||||
|
||||
func NewIntArray(size int, cap int, unsafe...bool) *IntArray {
|
||||
a := &IntArray{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
a.size = size
|
||||
if cap > 0 {
|
||||
a.cap = cap
|
||||
a.array = make([]int, size, cap)
|
||||
} else {
|
||||
a.array = make([]int, size)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// 获取指定索引的数据项, 调用方注意判断数组边界
|
||||
func (a *IntArray) Get(index int) int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
value := a.array[index]
|
||||
return value
|
||||
}
|
||||
|
||||
// 设置指定索引的数据项, 调用方注意判断数组边界
|
||||
func (a *IntArray) Set(index int, value int) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
a.array[index] = value
|
||||
}
|
||||
|
||||
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界
|
||||
func (a *IntArray) InsertBefore(index int, value int) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
rear := append([]int{}, a.array[index : ]...)
|
||||
a.array = append(a.array[0 : index], value)
|
||||
a.array = append(a.array, rear...)
|
||||
}
|
||||
|
||||
// 在当前索引位置后插入一个数据项, 调用方注意判断数组边界
|
||||
func (a *IntArray) InsertAfter(index int, value int) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
rear := append([]int{}, a.array[index + 1:]...)
|
||||
a.array = append(a.array[0 : index + 1], value)
|
||||
a.array = append(a.array, rear...)
|
||||
|
||||
}
|
||||
|
||||
// 删除指定索引的数据项, 调用方注意判断数组边界
|
||||
func (a *IntArray) Remove(index int) int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
// 边界删除判断,以提高删除效率
|
||||
if index == 0 {
|
||||
value := a.array[0]
|
||||
a.array = a.array[1 : ]
|
||||
return value
|
||||
} else if index == len(a.array) - 1 {
|
||||
value := a.array[index]
|
||||
a.array = a.array[: index]
|
||||
return value
|
||||
}
|
||||
// 如果非边界删除,会涉及到数组创建,那么删除的效率差一些
|
||||
value := a.array[index]
|
||||
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
|
||||
return value
|
||||
}
|
||||
|
||||
// 追加数据项
|
||||
func (a *IntArray) Append(value...int) {
|
||||
a.mu.Lock()
|
||||
a.array = append(a.array, value...)
|
||||
a.mu.Unlock()
|
||||
}
|
||||
|
||||
// 数组长度
|
||||
func (a *IntArray) Len() int {
|
||||
a.mu.RLock()
|
||||
length := len(a.array)
|
||||
a.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// 返回原始数据数组
|
||||
func (a *IntArray) Slice() []int {
|
||||
array := ([]int)(nil)
|
||||
if a.mu.IsSafe() {
|
||||
a.mu.RLock()
|
||||
array = make([]int, len(a.array))
|
||||
for k, v := range a.array {
|
||||
array[k] = v
|
||||
}
|
||||
a.mu.RUnlock()
|
||||
} else {
|
||||
array = a.array
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// 清空数据数组
|
||||
func (a *IntArray) Clear() {
|
||||
a.mu.Lock()
|
||||
if len(a.array) > 0 {
|
||||
if a.cap > 0 {
|
||||
a.array = make([]int, a.size, a.cap)
|
||||
} else {
|
||||
a.array = make([]int, a.size)
|
||||
}
|
||||
}
|
||||
a.mu.Unlock()
|
||||
}
|
||||
|
||||
// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1
|
||||
func (a *IntArray) Search(value int) int {
|
||||
if len(a.array) == 0 {
|
||||
return -1
|
||||
}
|
||||
a.mu.RLock()
|
||||
result := -1
|
||||
for index, v := range a.array {
|
||||
if v == value {
|
||||
result = index
|
||||
break
|
||||
}
|
||||
}
|
||||
a.mu.RUnlock()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// 清理数组中重复的元素项
|
||||
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++ {
|
||||
if a.array[i] == a.array[j] {
|
||||
a.array = append(a.array[ : j], a.array[j + 1 : ]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// 使用自定义方法执行加锁修改操作
|
||||
func (a *IntArray) LockFunc(f func(array []int)) {
|
||||
a.mu.Lock(true)
|
||||
defer a.mu.Unlock(true)
|
||||
f(a.array)
|
||||
}
|
||||
|
||||
// 使用自定义方法执行加锁读取操作
|
||||
func (a *IntArray) RLockFunc(f func(array []int)) {
|
||||
a.mu.RLock(true)
|
||||
defer a.mu.RUnlock(true)
|
||||
f(a.array)
|
||||
}
|
||||
@ -1,194 +0,0 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf.
|
||||
|
||||
package garray
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/internal/rwmutex"
|
||||
)
|
||||
|
||||
type Array struct {
|
||||
mu *rwmutex.RWMutex // 互斥锁
|
||||
cap int // 初始化设置的数组容量
|
||||
size int // 初始化设置的数组大小
|
||||
array []interface{} // 底层数组
|
||||
}
|
||||
|
||||
func NewArray(size int, cap int, unsafe...bool) *Array {
|
||||
a := &Array{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
a.size = size
|
||||
if cap > 0 {
|
||||
a.cap = cap
|
||||
a.array = make([]interface{}, size, cap)
|
||||
} else {
|
||||
a.array = make([]interface{}, size)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// 获取指定索引的数据项, 调用方注意判断数组边界
|
||||
func (a *Array) Get(index int) interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
value := a.array[index]
|
||||
return value
|
||||
}
|
||||
|
||||
// 设置指定索引的数据项, 调用方注意判断数组边界
|
||||
func (a *Array) Set(index int, value interface{}) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
a.array[index] = value
|
||||
}
|
||||
|
||||
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界
|
||||
func (a *Array) InsertBefore(index int, value interface{}) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
rear := append([]interface{}{}, a.array[index : ]...)
|
||||
a.array = append(a.array[0 : index], value)
|
||||
a.array = append(a.array, rear...)
|
||||
}
|
||||
|
||||
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界
|
||||
func (a *Array) InsertAfter(index int, value interface{}) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
rear := append([]interface{}{}, a.array[index + 1 : ]...)
|
||||
a.array = append(a.array[0 : index + 1], value)
|
||||
a.array = append(a.array, rear...)
|
||||
}
|
||||
|
||||
// 删除指定索引的数据项, 调用方注意判断数组边界
|
||||
func (a *Array) Remove(index int) interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
// 边界删除判断,以提高删除效率
|
||||
if index == 0 {
|
||||
value := a.array[0]
|
||||
a.array = a.array[1 : ]
|
||||
return value
|
||||
} else if index == len(a.array) - 1 {
|
||||
value := a.array[index]
|
||||
a.array = a.array[: index]
|
||||
return value
|
||||
}
|
||||
// 如果非边界删除,会涉及到数组创建,那么删除的效率差一些
|
||||
value := a.array[index]
|
||||
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
|
||||
return value
|
||||
}
|
||||
|
||||
// 将最左端(索引为0)的数据项移出数组,并返回该数据项
|
||||
func (a *Array) PopLeft() interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
value := a.array[0]
|
||||
a.array = a.array[1 : ]
|
||||
return value
|
||||
}
|
||||
|
||||
// 将最右端(索引为length - 1)的数据项移出数组,并返回该数据项
|
||||
func (a *Array) PopRight() interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - 1
|
||||
value := a.array[index]
|
||||
a.array = a.array[: index]
|
||||
return value
|
||||
}
|
||||
|
||||
// 追加数据项
|
||||
func (a *Array) Append(value...interface{}) {
|
||||
a.mu.Lock()
|
||||
a.array = append(a.array, value...)
|
||||
a.mu.Unlock()
|
||||
}
|
||||
|
||||
// 数组长度
|
||||
func (a *Array) Len() int {
|
||||
a.mu.RLock()
|
||||
length := len(a.array)
|
||||
a.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// 返回原始数据数组
|
||||
func (a *Array) Slice() []interface{} {
|
||||
array := ([]interface{})(nil)
|
||||
if a.mu.IsSafe() {
|
||||
a.mu.RLock()
|
||||
array = make([]interface{}, len(a.array))
|
||||
for k, v := range a.array {
|
||||
array[k] = v
|
||||
}
|
||||
a.mu.RUnlock()
|
||||
} else {
|
||||
array = a.array
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// 清空数据数组
|
||||
func (a *Array) Clear() {
|
||||
a.mu.Lock()
|
||||
if len(a.array) > 0 {
|
||||
if a.cap > 0 {
|
||||
a.array = make([]interface{}, a.size, a.cap)
|
||||
} else {
|
||||
a.array = make([]interface{}, a.size)
|
||||
}
|
||||
}
|
||||
a.mu.Unlock()
|
||||
}
|
||||
|
||||
// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1
|
||||
func (a *Array) Search(value interface{}) int {
|
||||
if len(a.array) == 0 {
|
||||
return -1
|
||||
}
|
||||
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) Unique() *Array {
|
||||
a.mu.Lock()
|
||||
for i := 0; i < len(a.array) - 1; i++ {
|
||||
for j := i + 1; j < len(a.array); j++ {
|
||||
if a.array[i] == a.array[j] {
|
||||
a.array = append(a.array[ : j], a.array[j + 1 : ]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// 使用自定义方法执行加锁修改操作
|
||||
func (a *Array) LockFunc(f func(array []interface{})) {
|
||||
a.mu.Lock(true)
|
||||
defer a.mu.Unlock(true)
|
||||
f(a.array)
|
||||
}
|
||||
|
||||
// 使用自定义方法执行加锁读取操作
|
||||
func (a *Array) RLockFunc(f func(array []interface{})) {
|
||||
a.mu.RLock(true)
|
||||
defer a.mu.RUnlock(true)
|
||||
f(a.array)
|
||||
}
|
||||
592
g/container/garray/garray_normal_int.go
Normal file
592
g/container/garray/garray_normal_int.go
Normal file
@ -0,0 +1,592 @@
|
||||
// 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
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/util/grand"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type IntArray struct {
|
||||
mu *rwmutex.RWMutex // 互斥锁
|
||||
array []int // 底层数组
|
||||
}
|
||||
|
||||
// Create an empty array.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 创建一个空的数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
func NewIntArray(unsafe...bool) *IntArray {
|
||||
return NewIntArraySize(0, 0, unsafe...)
|
||||
}
|
||||
|
||||
// Create an array with given size and cap.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 创建一个指定大小的数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
func NewIntArraySize(size int, cap int, unsafe...bool) *IntArray {
|
||||
return &IntArray{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
array : make([]int, size, cap),
|
||||
}
|
||||
}
|
||||
|
||||
// Create an array with given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 通过给定的slice变量创建数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
func NewIntArrayFrom(array []int, unsafe...bool) *IntArray {
|
||||
return &IntArray{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
array : array,
|
||||
}
|
||||
}
|
||||
|
||||
// Get value by index.
|
||||
//
|
||||
// 获取指定索引的数据项, 调用方注意判断数组边界。
|
||||
func (a *IntArray) Get(index int) int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
value := a.array[index]
|
||||
return value
|
||||
}
|
||||
|
||||
// Set value by index.
|
||||
//
|
||||
// 设置指定索引的数据项, 调用方注意判断数组边界。
|
||||
func (a *IntArray) Set(index int, value int) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
a.array[index] = value
|
||||
return a
|
||||
}
|
||||
|
||||
// Set the underlying slice array with the given <array> param.
|
||||
//
|
||||
// 设置底层数组变量.
|
||||
func (a *IntArray) SetArray(array []int) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
a.array = array
|
||||
return a
|
||||
}
|
||||
|
||||
// Replace the array items by given <array> from the beginning of array.
|
||||
//
|
||||
// 使用指定数组替换到对应的索引元素值.
|
||||
func (a *IntArray) Replace(array []int) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
max := len(array)
|
||||
if max > len(a.array) {
|
||||
max = len(a.array)
|
||||
}
|
||||
for i := 0; i < max; i++ {
|
||||
a.array[i] = array[i]
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Calculate the sum of values in an array.
|
||||
//
|
||||
// 对数组中的元素项求和。
|
||||
func (a *IntArray) Sum() (sum int) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for _, v := range a.array {
|
||||
sum += v
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Sort the array in increasing order.
|
||||
// The param <reverse> controls whether sort
|
||||
// in increasing order(default) or decreasing order
|
||||
//
|
||||
// 将数组排序(默认从低到高).
|
||||
func (a *IntArray) Sort(reverse...bool) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if len(reverse) > 0 && reverse[0] {
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
if a.array[i] < a.array[j] {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
} else {
|
||||
sort.Ints(a.array)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Sort the array by custom function <less>.
|
||||
//
|
||||
// 使用自定义的排序函数将数组重新排序.
|
||||
func (a *IntArray) SortFunc(less func(v1, v2 int) bool) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return less(a.array[i], a.array[j])
|
||||
})
|
||||
return a
|
||||
}
|
||||
|
||||
// Insert the <value> to the front of <index>.
|
||||
//
|
||||
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界。
|
||||
func (a *IntArray) InsertBefore(index int, value int) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
rear := append([]int{}, a.array[index : ]...)
|
||||
a.array = append(a.array[0 : index], value)
|
||||
a.array = append(a.array, rear...)
|
||||
return a
|
||||
}
|
||||
|
||||
// Insert the <value> to the back of <index>.
|
||||
//
|
||||
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界。
|
||||
func (a *IntArray) InsertAfter(index int, value int) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
rear := append([]int{}, a.array[index + 1:]...)
|
||||
a.array = append(a.array[0 : index + 1], value)
|
||||
a.array = append(a.array, rear...)
|
||||
return a
|
||||
}
|
||||
|
||||
// Remove an item by index.
|
||||
//
|
||||
// 删除指定索引的数据项, 调用方注意判断数组边界。
|
||||
func (a *IntArray) Remove(index int) int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
// 边界删除判断,以提高删除效率
|
||||
if index == 0 {
|
||||
value := a.array[0]
|
||||
a.array = a.array[1 : ]
|
||||
return value
|
||||
} else if index == len(a.array) - 1 {
|
||||
value := a.array[index]
|
||||
a.array = a.array[: index]
|
||||
return value
|
||||
}
|
||||
// 如果非边界删除,会涉及到数组创建,那么删除的效率差一些
|
||||
value := a.array[index]
|
||||
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
|
||||
return value
|
||||
}
|
||||
|
||||
// Push new items to the beginning of array.
|
||||
//
|
||||
// 将数据项添加到数组的最左端(索引为0)。
|
||||
func (a *IntArray) PushLeft(value...int) *IntArray {
|
||||
a.mu.Lock()
|
||||
a.array = append(value, a.array...)
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// Push new items to the end of array.
|
||||
//
|
||||
// 将数据项添加到数组的最右端(索引为length - 1), 等于: Append。
|
||||
func (a *IntArray) PushRight(value...int) *IntArray {
|
||||
a.mu.Lock()
|
||||
a.array = append(a.array, value...)
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// Pop an item from the beginning of array.
|
||||
//
|
||||
// 将最左端(索引为0)的数据项移出数组,并返回该数据项。
|
||||
func (a *IntArray) PopLeft() int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
value := a.array[0]
|
||||
a.array = a.array[1 : ]
|
||||
return value
|
||||
}
|
||||
|
||||
// Pop an item from the end of array.
|
||||
//
|
||||
// 将最右端(索引为length - 1)的数据项移出数组,并返回该数据项。
|
||||
func (a *IntArray) PopRight() int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - 1
|
||||
value := a.array[index]
|
||||
a.array = a.array[: index]
|
||||
return value
|
||||
}
|
||||
|
||||
// Pop an random item from array.
|
||||
//
|
||||
// 随机将一个数据项移出数组,并返回该数据项。
|
||||
func (a *IntArray) PopRand() int {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// Pop <size> items from the beginning of array.
|
||||
//
|
||||
// 将最左端(首部)的size个数据项移出数组,并返回该数据项。
|
||||
func (a *IntArray) PopLefts(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
length := len(a.array)
|
||||
if size > length {
|
||||
size = length
|
||||
}
|
||||
value := a.array[0 : size]
|
||||
a.array = a.array[size : ]
|
||||
return value
|
||||
}
|
||||
|
||||
// Pop <size> items from the end of array.
|
||||
//
|
||||
// 将最右端(尾部)的size个数据项移出数组,并返回该数据项
|
||||
func (a *IntArray) PopRights(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - size
|
||||
if index < 0 {
|
||||
index = 0
|
||||
}
|
||||
value := a.array[index :]
|
||||
a.array = a.array[ : index]
|
||||
return value
|
||||
}
|
||||
|
||||
// Get items by range, returns array[start:end].
|
||||
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// 将最右端(尾部)的size个数据项移出数组,并返回该数据项
|
||||
func (a *IntArray) Range(start, end int) []int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
length := len(a.array)
|
||||
if start > length || start > end {
|
||||
return nil
|
||||
}
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
if end > length {
|
||||
end = length
|
||||
}
|
||||
array := ([]int)(nil)
|
||||
if a.mu.IsSafe() {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
array = make([]int, end - start)
|
||||
copy(array, a.array[start : end])
|
||||
} else {
|
||||
array = a.array[start : end]
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// See PushRight.
|
||||
//
|
||||
// 追加数据项, 等于: PushRight。
|
||||
func (a *IntArray) Append(value...int) *IntArray {
|
||||
a.mu.Lock()
|
||||
a.array = append(a.array, value...)
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// Get the length of array.
|
||||
//
|
||||
// 数组长度。
|
||||
func (a *IntArray) Len() int {
|
||||
a.mu.RLock()
|
||||
length := len(a.array)
|
||||
a.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// Get the underlying data of array.
|
||||
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// 返回原始数据数组.
|
||||
func (a *IntArray) Slice() []int {
|
||||
array := ([]int)(nil)
|
||||
if a.mu.IsSafe() {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
array = make([]int, len(a.array))
|
||||
copy(array, a.array)
|
||||
} else {
|
||||
array = a.array
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// Return a new array, which is a copy of current array.
|
||||
//
|
||||
// 克隆当前数组,返回当前数组的一个拷贝。
|
||||
func (a *IntArray) Clone() (newArray *IntArray) {
|
||||
a.mu.RLock()
|
||||
array := make([]int, len(a.array))
|
||||
copy(array, a.array)
|
||||
a.mu.RUnlock()
|
||||
return NewIntArrayFrom(array, !a.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Clear array.
|
||||
//
|
||||
// 清空数据数组。
|
||||
func (a *IntArray) Clear() *IntArray {
|
||||
a.mu.Lock()
|
||||
if len(a.array) > 0 {
|
||||
a.array = make([]int, 0)
|
||||
}
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// Check whether a value exists in the array.
|
||||
//
|
||||
// 查找指定数值是否存在。
|
||||
func (a *IntArray) Contains(value int) bool {
|
||||
return a.Search(value) != -1
|
||||
}
|
||||
|
||||
|
||||
// Search array by <value>, returns the index of <value>, returns -1 if not exists.
|
||||
//
|
||||
// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1。
|
||||
func (a *IntArray) Search(value int) int {
|
||||
if len(a.array) == 0 {
|
||||
return -1
|
||||
}
|
||||
a.mu.RLock()
|
||||
result := -1
|
||||
for index, v := range a.array {
|
||||
if v == value {
|
||||
result = index
|
||||
break
|
||||
}
|
||||
}
|
||||
a.mu.RUnlock()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Unique the array, clear repeated values.
|
||||
//
|
||||
// 清理数组中重复的元素项。
|
||||
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++ {
|
||||
if a.array[i] == a.array[j] {
|
||||
a.array = append(a.array[ : j], a.array[j + 1 : ]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// Lock writing by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁修改操作。
|
||||
func (a *IntArray) LockFunc(f func(array []int)) *IntArray {
|
||||
a.mu.Lock(true)
|
||||
defer a.mu.Unlock(true)
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// Lock reading by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁读取操作。
|
||||
func (a *IntArray) RLockFunc(f func(array []int)) *IntArray {
|
||||
a.mu.RLock(true)
|
||||
defer a.mu.RUnlock(true)
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// Merge two arrays.
|
||||
//
|
||||
// 合并两个数组.
|
||||
func (a *IntArray) Merge(array *IntArray) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if a != array {
|
||||
array.mu.RLock()
|
||||
defer array.mu.RUnlock()
|
||||
}
|
||||
a.array = append(a.array, array.array...)
|
||||
return a
|
||||
}
|
||||
|
||||
// Fills an array with num entries of the value of the value parameter,
|
||||
// keys starting at the startIndex parameter.
|
||||
//
|
||||
// 用value参数的值将数组填充num个条目,位置由startIndex参数指定的开始。
|
||||
func (a *IntArray) Fill(startIndex int, num int, value int) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if startIndex < 0 {
|
||||
startIndex = 0
|
||||
}
|
||||
for i := startIndex; i < startIndex + num; i++ {
|
||||
if i > len(a.array) - 1 {
|
||||
a.array = append(a.array, value)
|
||||
} else {
|
||||
a.array[i] = value
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Chunks an array into arrays with size elements.
|
||||
// The last chunk may contain less than size elements.
|
||||
//
|
||||
// 将一个数组分割成多个数组,其中每个数组的单元数目由size决定。最后一个数组的单元数目可能会少于size个。
|
||||
func (a *IntArray) Chunk(size int) [][]int {
|
||||
if size < 1 {
|
||||
return nil
|
||||
}
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
length := len(a.array)
|
||||
chunks := int(math.Ceil(float64(length) / float64(size)))
|
||||
var n [][]int
|
||||
for i, end := 0, 0; chunks > 0; chunks-- {
|
||||
end = (i + 1) * size
|
||||
if end > length {
|
||||
end = length
|
||||
}
|
||||
n = append(n, a.array[i*size : end])
|
||||
i++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Pad array to the specified length with a value.
|
||||
// If size is positive then the array is padded on the right, or negative on the left.
|
||||
// If the absolute value of size is less than or equal to the length of the array
|
||||
// then no padding takes place.
|
||||
//
|
||||
// 返回数组的一个拷贝,并用value将其填补到size指定的长度。
|
||||
// 如果size为正数,则填补到数组的右侧,如果为负数则从左侧开始填补。
|
||||
// 如果size的绝对值小于或等于数组的长度则没有任何填补。
|
||||
func (a *IntArray) Pad(size int, value int) *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size == 0 || (size > 0 && size < len(a.array)) || (size < 0 && size > -len(a.array)) {
|
||||
return a
|
||||
}
|
||||
n := size
|
||||
if size < 0 {
|
||||
n = -size
|
||||
}
|
||||
n -= len(a.array)
|
||||
tmp := make([]int, n)
|
||||
for i := 0; i < n; i++ {
|
||||
tmp[i] = value
|
||||
}
|
||||
if size > 0 {
|
||||
a.array = append(a.array, tmp...)
|
||||
} else {
|
||||
a.array = append(tmp, a.array...)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Extract a slice of the array(If in concurrent safe usage,
|
||||
// it returns a copy of the slice; else a pointer).
|
||||
// It returns the sequence of elements from the array array as specified
|
||||
// by the offset and length parameters.
|
||||
//
|
||||
// 返回根据offset和size参数所指定的数组中的一段序列。
|
||||
func (a *IntArray) SubSlice(offset, size int) []int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if offset > len(a.array) {
|
||||
return nil
|
||||
}
|
||||
if offset + size > len(a.array) {
|
||||
size = len(a.array) - offset
|
||||
}
|
||||
if a.mu.IsSafe() {
|
||||
s := make([]int, size)
|
||||
copy(s, a.array[offset:])
|
||||
return s
|
||||
} else {
|
||||
return a.array[offset:]
|
||||
}
|
||||
}
|
||||
|
||||
// Picks one or more random entries out of an array(a copy),
|
||||
// and returns the key (or keys) of the random entries.
|
||||
//
|
||||
// 从数组中随机取出size个元素项,构成slice返回。
|
||||
func (a *IntArray) Rand(size int) []int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if size > len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
n := make([]int, size)
|
||||
for i, v := range grand.Perm(len(a.array)) {
|
||||
n[i] = a.array[v]
|
||||
if i == size - 1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Randomly shuffles the array.
|
||||
//
|
||||
// 随机打乱当前数组。
|
||||
func (a *IntArray) Shuffle() *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i, v := range grand.Perm(len(a.array)) {
|
||||
a.array[i], a.array[v] = a.array[v], a.array[i]
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Make array with elements in reverse order.
|
||||
//
|
||||
// 将当前数组反转。
|
||||
func (a *IntArray) Reverse() *IntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i, j := 0, len(a.array) - 1; i < j; i, j = i + 1, j - 1 {
|
||||
a.array[i], a.array[j] = a.array[j], a.array[i]
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Join array elements with a string.
|
||||
//
|
||||
// 使用glue字符串串连当前数组的元素项,构造成新的字符串返回。
|
||||
func (a *IntArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return strings.Join(gconv.Strings(a.array), glue)
|
||||
}
|
||||
587
g/container/garray/garray_normal_interface.go
Normal file
587
g/container/garray/garray_normal_interface.go
Normal file
@ -0,0 +1,587 @@
|
||||
// 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
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/util/grand"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type Array struct {
|
||||
mu *rwmutex.RWMutex // 互斥锁
|
||||
array []interface{} // 底层数组
|
||||
}
|
||||
|
||||
// Create an empty array.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 创建一个空的数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
func New(unsafe...bool) *Array {
|
||||
return NewArraySize(0, 0, unsafe...)
|
||||
}
|
||||
|
||||
// See New.
|
||||
//
|
||||
// 同New方法。
|
||||
func NewArray(unsafe...bool) *Array {
|
||||
return NewArraySize(0, 0, unsafe...)
|
||||
}
|
||||
|
||||
// Create an array with given size and cap.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 创建一个指定大小的数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
func NewArraySize(size int, cap int, unsafe...bool) *Array {
|
||||
return &Array{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
array : make([]interface{}, size, cap),
|
||||
}
|
||||
}
|
||||
|
||||
// Create an array with given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 通过给定的slice变量创建数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
func NewArrayFrom(array []interface{}, unsafe...bool) *Array {
|
||||
return &Array{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
array : array,
|
||||
}
|
||||
}
|
||||
|
||||
// Get value by index.
|
||||
//
|
||||
// 获取指定索引的数据项, 调用方注意判断数组边界
|
||||
func (a *Array) Get(index int) interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
value := a.array[index]
|
||||
return value
|
||||
}
|
||||
|
||||
// Set value by index.
|
||||
//
|
||||
// 设置指定索引的数据项, 调用方注意判断数组边界
|
||||
func (a *Array) Set(index int, value interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
a.array[index] = value
|
||||
return a
|
||||
}
|
||||
|
||||
// Set the underlying slice array with the given <array> param.
|
||||
//
|
||||
// 设置底层数组变量.
|
||||
func (a *Array) SetArray(array []interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
a.array = array
|
||||
return a
|
||||
}
|
||||
|
||||
// Replace the array items by given <array> from the beginning of array.
|
||||
//
|
||||
// 使用指定数组替换到对应的索引元素值.
|
||||
func (a *Array) Replace(array []interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
max := len(array)
|
||||
if max > len(a.array) {
|
||||
max = len(a.array)
|
||||
}
|
||||
for i := 0; i < max; i++ {
|
||||
a.array[i] = array[i]
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Calculate the sum of values in an array.
|
||||
//
|
||||
// 对数组中的元素项求和(将元素值转换为int类型后叠加)。
|
||||
func (a *Array) Sum() (sum int) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for _, v := range a.array {
|
||||
sum += gconv.Int(v)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Sort the array by custom function <less>.
|
||||
//
|
||||
// 使用自定义的排序函数将数组重新排序.
|
||||
func (a *Array) SortFunc(less func(v1, v2 interface{}) bool) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return less(a.array[i], a.array[j])
|
||||
})
|
||||
return a
|
||||
}
|
||||
|
||||
// Insert the <value> to the front of <index>.
|
||||
//
|
||||
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界。
|
||||
func (a *Array) InsertBefore(index int, value interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
rear := append([]interface{}{}, a.array[index : ]...)
|
||||
a.array = append(a.array[0 : index], value)
|
||||
a.array = append(a.array, rear...)
|
||||
return a
|
||||
}
|
||||
|
||||
// Insert the <value> to the back of <index>.
|
||||
//
|
||||
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界。
|
||||
func (a *Array) InsertAfter(index int, value interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
rear := append([]interface{}{}, a.array[index + 1 : ]...)
|
||||
a.array = append(a.array[0 : index + 1], value)
|
||||
a.array = append(a.array, rear...)
|
||||
return a
|
||||
}
|
||||
|
||||
// Remove an item by index.
|
||||
//
|
||||
// 删除指定索引的数据项, 调用方注意判断数组边界。
|
||||
func (a *Array) Remove(index int) interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
// 边界删除判断,以提高删除效率
|
||||
if index == 0 {
|
||||
value := a.array[0]
|
||||
a.array = a.array[1 : ]
|
||||
return value
|
||||
} else if index == len(a.array) - 1 {
|
||||
value := a.array[index]
|
||||
a.array = a.array[: index]
|
||||
return value
|
||||
}
|
||||
// 如果非边界删除,会涉及到数组创建,那么删除的效率差一些
|
||||
value := a.array[index]
|
||||
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
|
||||
return value
|
||||
}
|
||||
|
||||
// Push new items to the beginning of array.
|
||||
//
|
||||
// 将数据项添加到数组的最左端(索引为0)。
|
||||
func (a *Array) PushLeft(value...interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
a.array = append(value, a.array...)
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// Push new items to the end of array.
|
||||
//
|
||||
// 将数据项添加到数组的最右端(索引为length - 1), 等于: Append。
|
||||
func (a *Array) PushRight(value...interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
a.array = append(a.array, value...)
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// Pop an random item from array.
|
||||
//
|
||||
// 随机将一个数据项移出数组,并返回该数据项。
|
||||
func (a *Array) PopRand() interface{} {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// Pop an item from the beginning of array.
|
||||
//
|
||||
// 将最左端(索引为0)的数据项移出数组,并返回该数据项。
|
||||
func (a *Array) PopLeft() interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
value := a.array[0]
|
||||
a.array = a.array[1 : ]
|
||||
return value
|
||||
}
|
||||
|
||||
// Pop an item from the end of array.
|
||||
//
|
||||
// 将最右端(索引为length - 1)的数据项移出数组,并返回该数据项。
|
||||
func (a *Array) PopRight() interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - 1
|
||||
value := a.array[index]
|
||||
a.array = a.array[: index]
|
||||
return value
|
||||
}
|
||||
|
||||
// Pop <size> items from the beginning of array.
|
||||
//
|
||||
// 将最左端(首部)的size个数据项移出数组,并返回该数据项
|
||||
func (a *Array) PopLefts(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
length := len(a.array)
|
||||
if size > length {
|
||||
size = length
|
||||
}
|
||||
value := a.array[0 : size]
|
||||
a.array = a.array[size : ]
|
||||
return value
|
||||
}
|
||||
|
||||
// Pop <size> items from the end of array.
|
||||
//
|
||||
// 将最右端(尾部)的size个数据项移出数组,并返回该数据项
|
||||
func (a *Array) PopRights(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - size
|
||||
if index < 0 {
|
||||
index = 0
|
||||
}
|
||||
value := a.array[index :]
|
||||
a.array = a.array[ : index]
|
||||
return value
|
||||
}
|
||||
|
||||
// Get items by range, returns array[start:end].
|
||||
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// 将最右端(尾部)的size个数据项移出数组,并返回该数据项
|
||||
func (a *Array) Range(start, end int) []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
length := len(a.array)
|
||||
if start > length || start > end {
|
||||
return nil
|
||||
}
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
if end > length {
|
||||
end = length
|
||||
}
|
||||
array := ([]interface{})(nil)
|
||||
if a.mu.IsSafe() {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
array = make([]interface{}, end - start)
|
||||
copy(array, a.array[start : end])
|
||||
} else {
|
||||
array = a.array[start : end]
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// See PushRight.
|
||||
//
|
||||
// 追加数据项, 等于: PushRight。
|
||||
func (a *Array) Append(value...interface{}) *Array {
|
||||
a.PushRight(value...)
|
||||
return a
|
||||
}
|
||||
|
||||
// Get the length of array.
|
||||
//
|
||||
// 数组长度。
|
||||
func (a *Array) Len() int {
|
||||
a.mu.RLock()
|
||||
length := len(a.array)
|
||||
a.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// Get the underlying data of array.
|
||||
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// 返回原始数据数组.
|
||||
func (a *Array) Slice() []interface{} {
|
||||
array := ([]interface{})(nil)
|
||||
if a.mu.IsSafe() {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
array = make([]interface{}, len(a.array))
|
||||
copy(array, a.array)
|
||||
} else {
|
||||
array = a.array
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// Return a new array, which is a copy of current array.
|
||||
//
|
||||
// 克隆当前数组,返回当前数组的一个拷贝。
|
||||
func (a *Array) Clone() (newArray *Array) {
|
||||
a.mu.RLock()
|
||||
array := make([]interface{}, len(a.array))
|
||||
copy(array, a.array)
|
||||
a.mu.RUnlock()
|
||||
return NewArrayFrom(array, !a.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Clear array.
|
||||
//
|
||||
// 清空数据数组
|
||||
func (a *Array) Clear() *Array {
|
||||
a.mu.Lock()
|
||||
if len(a.array) > 0 {
|
||||
a.array = make([]interface{}, 0)
|
||||
}
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// Check whether a value exists in the array.
|
||||
//
|
||||
// 查找指定数值是否存在
|
||||
func (a *Array) Contains(value interface{}) bool {
|
||||
return a.Search(value) != -1
|
||||
}
|
||||
|
||||
// Search array by <value>, returns the index of <value>, returns -1 if not exists.
|
||||
//
|
||||
// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1
|
||||
func (a *Array) Search(value interface{}) int {
|
||||
if len(a.array) == 0 {
|
||||
return -1
|
||||
}
|
||||
a.mu.RLock()
|
||||
result := -1
|
||||
for index, v := range a.array {
|
||||
if v == value {
|
||||
result = index
|
||||
break
|
||||
}
|
||||
}
|
||||
a.mu.RUnlock()
|
||||
|
||||
return result
|
||||
}
|
||||
|
||||
// Unique the array, clear repeated values.
|
||||
//
|
||||
// 清理数组中重复的元素项
|
||||
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++ {
|
||||
if a.array[i] == a.array[j] {
|
||||
a.array = append(a.array[ : j], a.array[j + 1 : ]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// Lock writing by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁修改操作
|
||||
func (a *Array) LockFunc(f func(array []interface{})) *Array {
|
||||
a.mu.Lock(true)
|
||||
defer a.mu.Unlock(true)
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// Lock reading by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁读取操作
|
||||
func (a *Array) RLockFunc(f func(array []interface{})) *Array {
|
||||
a.mu.RLock(true)
|
||||
defer a.mu.RUnlock(true)
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// Merge two arrays.
|
||||
//
|
||||
// 合并两个数组.
|
||||
func (a *Array) Merge(array *Array) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if a != array {
|
||||
array.mu.RLock()
|
||||
defer array.mu.RUnlock()
|
||||
}
|
||||
a.array = append(a.array, array.array...)
|
||||
return a
|
||||
}
|
||||
|
||||
// Fills an array with num entries of the value of the value parameter,
|
||||
// keys starting at the start_index parameter.
|
||||
//
|
||||
// 用value参数的值将数组填充num个条目,位置由startIndex参数指定的开始。
|
||||
func (a *Array) Fill(startIndex int, num int, value interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if startIndex < 0 {
|
||||
startIndex = 0
|
||||
}
|
||||
for i := startIndex; i < startIndex + num; i++ {
|
||||
if i > len(a.array) - 1 {
|
||||
a.array = append(a.array, value)
|
||||
} else {
|
||||
a.array[i] = value
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Chunks an array into arrays with size elements.
|
||||
// The last chunk may contain less than size elements.
|
||||
//
|
||||
// 将一个数组分割成多个数组,其中每个数组的单元数目由size决定。最后一个数组的单元数目可能会少于size个。
|
||||
func (a *Array) Chunk(size int) [][]interface{} {
|
||||
if size < 1 {
|
||||
return nil
|
||||
}
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
length := len(a.array)
|
||||
chunks := int(math.Ceil(float64(length) / float64(size)))
|
||||
var n [][]interface{}
|
||||
for i, end := 0, 0; chunks > 0; chunks-- {
|
||||
end = (i + 1) * size
|
||||
if end > length {
|
||||
end = length
|
||||
}
|
||||
n = append(n, a.array[i*size : end])
|
||||
i++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Pad array to the specified length with a value.
|
||||
// If size is positive then the array is padded on the right,
|
||||
// if it's negative then on the left.
|
||||
// If the absolute value of size is less than or equal to the length of the array
|
||||
// then no padding takes place.
|
||||
//
|
||||
// 返回数组的一个拷贝,并用value将其填补到size指定的长度。
|
||||
// 如果size为正数,则填补到数组的右侧,如果为负数则从左侧开始填补。
|
||||
// 如果size的绝对值小于或等于数组的长度则没有任何填补。
|
||||
func (a *Array) Pad(size int, val interface{}) *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size == 0 || (size > 0 && size < len(a.array)) || (size < 0 && size > -len(a.array)) {
|
||||
return a
|
||||
}
|
||||
n := size
|
||||
if size < 0 {
|
||||
n = -size
|
||||
}
|
||||
n -= len(a.array)
|
||||
tmp := make([]interface{}, n)
|
||||
for i := 0; i < n; i++ {
|
||||
tmp[i] = val
|
||||
}
|
||||
if size > 0 {
|
||||
a.array = append(a.array, tmp...)
|
||||
} else {
|
||||
a.array = append(tmp, a.array...)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Extract a slice of the array(If in concurrent safe usage, it returns a copy of the slice; else a pointer).
|
||||
// It returns the sequence of elements from the array array as specified by the offset and length parameters.
|
||||
//
|
||||
// 返回根据offset和size参数所指定的数组中的一段序列。
|
||||
func (a *Array) SubSlice(offset, size int) []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if offset > len(a.array) {
|
||||
return nil
|
||||
}
|
||||
if offset + size > len(a.array) {
|
||||
size = len(a.array) - offset
|
||||
}
|
||||
if a.mu.IsSafe() {
|
||||
s := make([]interface{}, size)
|
||||
copy(s, a.array[offset:])
|
||||
return s
|
||||
} else {
|
||||
return a.array[offset:]
|
||||
}
|
||||
}
|
||||
|
||||
// Picks one or more random entries out of an array(a copy),
|
||||
// and returns the key (or keys) of the random entries.
|
||||
//
|
||||
// 从数组中随机取出size个元素项,构成slice返回。
|
||||
func (a *Array) Rand(size int) []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if size > len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
n := make([]interface{}, size)
|
||||
for i, v := range grand.Perm(len(a.array)) {
|
||||
n[i] = a.array[v]
|
||||
if i == size - 1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Randomly shuffles the array.
|
||||
//
|
||||
// 随机打乱当前数组。
|
||||
func (a *Array) Shuffle() *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i, v := range grand.Perm(len(a.array)) {
|
||||
a.array[i], a.array[v] = a.array[v], a.array[i]
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Make array with elements in reverse order.
|
||||
//
|
||||
// 将当前数组反转。
|
||||
func (a *Array) Reverse() *Array {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i, j := 0, len(a.array) - 1; i < j; i, j = i + 1, j - 1 {
|
||||
a.array[i], a.array[j] = a.array[j], a.array[i]
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Join array elements with a string.
|
||||
//
|
||||
// 使用glue字符串串连当前数组的元素项,构造成新的字符串返回。
|
||||
func (a *Array) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return strings.Join(gconv.Strings(a.array), glue)
|
||||
}
|
||||
|
||||
// Counts all the values of an array.
|
||||
//
|
||||
// 统计数组中所有的值出现的次数.
|
||||
func (a *Array) CountValues() map[interface{}]int {
|
||||
m := make(map[interface{}]int)
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for _, v := range a.array {
|
||||
m[v]++
|
||||
}
|
||||
return m
|
||||
}
|
||||
592
g/container/garray/garray_normal_string.go
Normal file
592
g/container/garray/garray_normal_string.go
Normal file
@ -0,0 +1,592 @@
|
||||
// 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
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/util/grand"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type StringArray struct {
|
||||
mu *rwmutex.RWMutex // 互斥锁
|
||||
array []string // 底层数组
|
||||
}
|
||||
|
||||
// Create an empty array.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 创建一个空的数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
func NewStringArray(unsafe...bool) *StringArray {
|
||||
return NewStringArraySize(0, 0, unsafe...)
|
||||
}
|
||||
|
||||
// Create an array with given size and cap.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 创建一个指定大小的数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
func NewStringArraySize(size int, cap int, unsafe...bool) *StringArray {
|
||||
return &StringArray{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
array : make([]string, size, cap),
|
||||
}
|
||||
}
|
||||
|
||||
// Create an array with given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 通过给定的slice变量创建数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
func NewStringArrayFrom(array []string, unsafe...bool) *StringArray {
|
||||
return &StringArray {
|
||||
mu : rwmutex.New(unsafe...),
|
||||
array : array,
|
||||
}
|
||||
}
|
||||
|
||||
// Get value by index.
|
||||
//
|
||||
// 获取指定索引的数据项, 调用方注意判断数组边界。
|
||||
func (a *StringArray) Get(index int) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
value := a.array[index]
|
||||
return value
|
||||
}
|
||||
|
||||
// Set value by index.
|
||||
//
|
||||
// 设置指定索引的数据项, 调用方注意判断数组边界。
|
||||
func (a *StringArray) Set(index int, value string) *StringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
a.array[index] = value
|
||||
return a
|
||||
}
|
||||
|
||||
// Set the underlying slice array with the given <array> param.
|
||||
//
|
||||
// 设置底层数组变量.
|
||||
func (a *StringArray) SetArray(array []string) *StringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
a.array = array
|
||||
return a
|
||||
}
|
||||
|
||||
// Replace the array items by given <array> from the beginning of array.
|
||||
//
|
||||
// 使用指定数组替换到对应的索引元素值.
|
||||
func (a *StringArray) Replace(array []string) *StringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
max := len(array)
|
||||
if max > len(a.array) {
|
||||
max = len(a.array)
|
||||
}
|
||||
for i := 0; i < max; i++ {
|
||||
a.array[i] = array[i]
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Calculate the sum of values in an array.
|
||||
//
|
||||
// 对数组中的元素项求和(将元素值转换为int类型后叠加)。
|
||||
func (a *StringArray) Sum() (sum int) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for _, v := range a.array {
|
||||
sum += gconv.Int(v)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Sort the array in increasing order.
|
||||
// The param <reverse> controls whether sort
|
||||
// in increasing order(default) or decreasing order
|
||||
//
|
||||
// 将数组排序(默认从低到高).
|
||||
func (a *StringArray) Sort(reverse...bool) *StringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if len(reverse) > 0 && reverse[0] {
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
if strings.Compare(a.array[i], a.array[j]) < 0 {
|
||||
return false
|
||||
}
|
||||
return true
|
||||
})
|
||||
} else {
|
||||
sort.Strings(a.array)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Sort the array by custom function <less>.
|
||||
//
|
||||
// 使用自定义的排序函数将数组重新排序.
|
||||
func (a *StringArray) SortFunc(less func(v1, v2 string) bool) *StringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return less(a.array[i], a.array[j])
|
||||
})
|
||||
return a
|
||||
}
|
||||
|
||||
// Insert the <value> to the front of <index>.
|
||||
//
|
||||
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界。
|
||||
func (a *StringArray) InsertBefore(index int, value string) *StringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
rear := append([]string{}, a.array[index : ]...)
|
||||
a.array = append(a.array[0 : index], value)
|
||||
a.array = append(a.array, rear...)
|
||||
return a
|
||||
}
|
||||
|
||||
// Insert the <value> to the back of <index>.
|
||||
//
|
||||
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界。
|
||||
func (a *StringArray) InsertAfter(index int, value string) *StringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
rear := append([]string{}, a.array[index + 1:]...)
|
||||
a.array = append(a.array[ 0: index + 1], value)
|
||||
a.array = append(a.array, rear...)
|
||||
return a
|
||||
}
|
||||
|
||||
// Remove an item by index.
|
||||
//
|
||||
// 删除指定索引的数据项, 调用方注意判断数组边界。
|
||||
func (a *StringArray) Remove(index int) string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
// 边界删除判断,以提高删除效率
|
||||
if index == 0 {
|
||||
value := a.array[0]
|
||||
a.array = a.array[1 : ]
|
||||
return value
|
||||
} else if index == len(a.array) - 1 {
|
||||
value := a.array[index]
|
||||
a.array = a.array[: index]
|
||||
return value
|
||||
}
|
||||
// 如果非边界删除,会涉及到数组创建,那么删除的效率差一些
|
||||
value := a.array[index]
|
||||
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
|
||||
return value
|
||||
}
|
||||
|
||||
// Push new items to the beginning of array.
|
||||
//
|
||||
// 将数据项添加到数组的最左端(索引为0)。
|
||||
func (a *StringArray) PushLeft(value...string) *StringArray {
|
||||
a.mu.Lock()
|
||||
a.array = append(value, a.array...)
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// Push new items to the end of array.
|
||||
//
|
||||
// 将数据项添加到数组的最右端(索引为length - 1), 等于: Append。
|
||||
func (a *StringArray) PushRight(value...string) *StringArray {
|
||||
a.mu.Lock()
|
||||
a.array = append(a.array, value...)
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// Pop an item from the beginning of array.
|
||||
//
|
||||
// 将最左端(索引为0)的数据项移出数组,并返回该数据项。
|
||||
func (a *StringArray) PopLeft() string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
value := a.array[0]
|
||||
a.array = a.array[1 : ]
|
||||
return value
|
||||
}
|
||||
|
||||
// Pop an item from the end of array.
|
||||
//
|
||||
// 将最右端(索引为length - 1)的数据项移出数组,并返回该数据项。
|
||||
func (a *StringArray) PopRight() string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - 1
|
||||
value := a.array[index]
|
||||
a.array = a.array[: index]
|
||||
return value
|
||||
}
|
||||
|
||||
// Pop an random item from array.
|
||||
//
|
||||
// 随机将一个数据项移出数组,并返回该数据项。
|
||||
func (a *StringArray) PopRand() string {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// Pop <size> items from the beginning of array.
|
||||
//
|
||||
// 将最左端(首部)的size个数据项移出数组,并返回该数据项
|
||||
func (a *StringArray) PopLefts(size int) []string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
length := len(a.array)
|
||||
if size > length {
|
||||
size = length
|
||||
}
|
||||
value := a.array[0 : size]
|
||||
a.array = a.array[size : ]
|
||||
return value
|
||||
}
|
||||
|
||||
// Pop <size> items from the end of array.
|
||||
//
|
||||
// 将最右端(尾部)的size个数据项移出数组,并返回该数据项
|
||||
func (a *StringArray) PopRights(size int) []string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - size
|
||||
if index < 0 {
|
||||
index = 0
|
||||
}
|
||||
value := a.array[index :]
|
||||
a.array = a.array[ : index]
|
||||
return value
|
||||
}
|
||||
|
||||
// Get items by range, returns array[start:end].
|
||||
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// 将最右端(尾部)的size个数据项移出数组,并返回该数据项
|
||||
func (a *StringArray) Range(start, end int) []string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
length := len(a.array)
|
||||
if start > length || start > end {
|
||||
return nil
|
||||
}
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
if end > length {
|
||||
end = length
|
||||
}
|
||||
array := ([]string)(nil)
|
||||
if a.mu.IsSafe() {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
array = make([]string, end - start)
|
||||
copy(array, a.array[start : end])
|
||||
} else {
|
||||
array = a.array[start : end]
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// See PushRight.
|
||||
//
|
||||
// 追加数据项, 等于: PushRight。
|
||||
func (a *StringArray) Append(value...string) *StringArray {
|
||||
a.mu.Lock()
|
||||
a.array = append(a.array, value...)
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// Get the length of array.
|
||||
//
|
||||
// 数组长度。
|
||||
func (a *StringArray) Len() int {
|
||||
a.mu.RLock()
|
||||
length := len(a.array)
|
||||
a.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// Get the underlying data of array.
|
||||
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// 返回原始数据数组.
|
||||
func (a *StringArray) Slice() []string {
|
||||
array := ([]string)(nil)
|
||||
if a.mu.IsSafe() {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
array = make([]string, len(a.array))
|
||||
copy(array, a.array)
|
||||
} else {
|
||||
array = a.array
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// Return a new array, which is a copy of current array.
|
||||
//
|
||||
// 克隆当前数组,返回当前数组的一个拷贝。
|
||||
func (a *StringArray) Clone() (newArray *StringArray) {
|
||||
a.mu.RLock()
|
||||
array := make([]string, len(a.array))
|
||||
copy(array, a.array)
|
||||
a.mu.RUnlock()
|
||||
return NewStringArrayFrom(array, !a.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Clear array.
|
||||
//
|
||||
// 清空数据数组。
|
||||
func (a *StringArray) Clear() *StringArray {
|
||||
a.mu.Lock()
|
||||
if len(a.array) > 0 {
|
||||
a.array = make([]string, 0)
|
||||
}
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// Check whether a value exists in the array.
|
||||
//
|
||||
// 查找指定数值是否存在。
|
||||
func (a *StringArray) Contains(value string) bool {
|
||||
return a.Search(value) != -1
|
||||
}
|
||||
|
||||
// Search array by <value>, returns the index of <value>, returns -1 if not exists.
|
||||
//
|
||||
// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1。
|
||||
func (a *StringArray) Search(value string) int {
|
||||
if len(a.array) == 0 {
|
||||
return -1
|
||||
}
|
||||
a.mu.RLock()
|
||||
result := -1
|
||||
for index, v := range a.array {
|
||||
if strings.Compare(v, value) == 0 {
|
||||
result = index
|
||||
break
|
||||
}
|
||||
}
|
||||
a.mu.RUnlock()
|
||||
return result
|
||||
}
|
||||
|
||||
// Unique the array, clear repeated values.
|
||||
//
|
||||
// 清理数组中重复的元素项。
|
||||
func (a *StringArray) Unique() *StringArray {
|
||||
a.mu.Lock()
|
||||
for i := 0; i < len(a.array) - 1; i++ {
|
||||
for j := i + 1; j < len(a.array); j++ {
|
||||
if a.array[i] == a.array[j] {
|
||||
a.array = append(a.array[ : j], a.array[j + 1 : ]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// Lock writing by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁修改操作。
|
||||
func (a *StringArray) LockFunc(f func(array []string)) *StringArray {
|
||||
a.mu.Lock(true)
|
||||
defer a.mu.Unlock(true)
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// Lock reading by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁读取操作。
|
||||
func (a *StringArray) RLockFunc(f func(array []string)) *StringArray {
|
||||
a.mu.RLock(true)
|
||||
defer a.mu.RUnlock(true)
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// Merge two arrays.
|
||||
//
|
||||
// 合并两个数组.
|
||||
func (a *StringArray) Merge(array *StringArray) *StringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if a != array {
|
||||
array.mu.RLock()
|
||||
defer array.mu.RUnlock()
|
||||
}
|
||||
a.array = append(a.array, array.array...)
|
||||
return a
|
||||
}
|
||||
|
||||
// Fills an array with num entries of the value of the value parameter,
|
||||
// keys starting at the start_index parameter.
|
||||
//
|
||||
// 用value参数的值将数组填充num个条目,位置由startIndex参数指定的开始。
|
||||
func (a *StringArray) Fill(startIndex int, num int, value string) *StringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if startIndex < 0 {
|
||||
startIndex = 0
|
||||
}
|
||||
for i := startIndex; i < startIndex + num; i++ {
|
||||
if i > len(a.array) - 1 {
|
||||
a.array = append(a.array, value)
|
||||
} else {
|
||||
a.array[i] = value
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Chunks an array into arrays with size elements.
|
||||
// The last chunk may contain less than size elements.
|
||||
//
|
||||
// 将一个数组分割成多个数组,其中每个数组的单元数目由size决定。最后一个数组的单元数目可能会少于size个。
|
||||
func (a *StringArray) Chunk(size int) [][]string {
|
||||
if size < 1 {
|
||||
return nil
|
||||
}
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
length := len(a.array)
|
||||
chunks := int(math.Ceil(float64(length) / float64(size)))
|
||||
var n [][]string
|
||||
for i, end := 0, 0; chunks > 0; chunks-- {
|
||||
end = (i + 1) * size
|
||||
if end > length {
|
||||
end = length
|
||||
}
|
||||
n = append(n, a.array[i*size : end])
|
||||
i++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Pad array to the specified length with a value.
|
||||
// If size is positive then the array is padded on the right,
|
||||
// if it's negative then on the left.
|
||||
// If the absolute value of size is less than or equal to the length of the array
|
||||
// then no padding takes place.
|
||||
//
|
||||
// 返回数组的一个拷贝,并用value将其填补到size指定的长度。
|
||||
// 如果size为正数,则填补到数组的右侧,如果为负数则从左侧开始填补。
|
||||
// 如果size的绝对值小于或等于数组的长度则没有任何填补。
|
||||
func (a *StringArray) Pad(size int, value string) *StringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size == 0 || (size > 0 && size < len(a.array)) || (size < 0 && size > -len(a.array)) {
|
||||
return a
|
||||
}
|
||||
n := size
|
||||
if size < 0 {
|
||||
n = -size
|
||||
}
|
||||
n -= len(a.array)
|
||||
tmp := make([]string, n)
|
||||
for i := 0; i < n; i++ {
|
||||
tmp[i] = value
|
||||
}
|
||||
if size > 0 {
|
||||
a.array = append(a.array, tmp...)
|
||||
} else {
|
||||
a.array = append(tmp, a.array...)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Extract a slice of the array(If in concurrent safe usage,
|
||||
// it returns a copy of the slice; else a pointer).
|
||||
// It returns the sequence of elements from the array array as specified
|
||||
// by the offset and length parameters.
|
||||
//
|
||||
// 返回根据offset和size参数所指定的数组中的一段序列。
|
||||
func (a *StringArray) SubSlice(offset, size int) []string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if offset > len(a.array) {
|
||||
return nil
|
||||
}
|
||||
if offset + size > len(a.array) {
|
||||
size = len(a.array) - offset
|
||||
}
|
||||
if a.mu.IsSafe() {
|
||||
s := make([]string, size)
|
||||
copy(s, a.array[offset:])
|
||||
return s
|
||||
} else {
|
||||
return a.array[offset:]
|
||||
}
|
||||
}
|
||||
|
||||
// Picks one or more random entries out of an array(a copy),
|
||||
// and returns the key (or keys) of the random entries.
|
||||
//
|
||||
// 从数组中随机取出size个元素项,构成slice返回。
|
||||
func (a *StringArray) Rand(size int) []string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if size > len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
n := make([]string, size)
|
||||
for i, v := range grand.Perm(len(a.array)) {
|
||||
n[i] = a.array[v]
|
||||
if i == size - 1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Randomly shuffles the array.
|
||||
//
|
||||
// 随机打乱当前数组。
|
||||
func (a *StringArray) Shuffle() *StringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i, v := range grand.Perm(len(a.array)) {
|
||||
a.array[i], a.array[v] = a.array[v], a.array[i]
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Make array with elements in reverse order.
|
||||
//
|
||||
// 将当前数组反转。
|
||||
func (a *StringArray) Reverse() *StringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
for i, j := 0, len(a.array) - 1; i < j; i, j = i + 1, j - 1 {
|
||||
a.array[i], a.array[j] = a.array[j], a.array[i]
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// Join array elements with a string.
|
||||
//
|
||||
// 使用glue字符串串连当前数组的元素项,构造成新的字符串返回。
|
||||
func (a *StringArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return strings.Join(a.array, glue)
|
||||
}
|
||||
|
||||
@ -1,27 +1,44 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package garray
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/container/gtype"
|
||||
"gitee.com/johng/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/util/grand"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 默认按照从低到高进行排序
|
||||
// 默认按照从小到大进行排序
|
||||
type SortedIntArray struct {
|
||||
mu *rwmutex.RWMutex // 互斥锁
|
||||
cap int // 初始化设置的数组容量
|
||||
array []int // 底层数组
|
||||
unique *gtype.Bool // 是否要求不能重复(默认false)
|
||||
compareFunc func(v1, v2 int) int // 比较函数,返回值 -1: v1 < v2;0: v1 == v2;1: v1 > v2
|
||||
}
|
||||
|
||||
// 创建一个排序的int数组
|
||||
func NewSortedIntArray(cap int, unsafe...bool) *SortedIntArray {
|
||||
// Create an empty sorted array.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 创建一个空的排序数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
func NewSortedIntArray(unsafe...bool) *SortedIntArray {
|
||||
return NewSortedIntArraySize(0, unsafe...)
|
||||
}
|
||||
|
||||
// Create a sorted array with given size and cap.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 创建一个指定大小的排序数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
func NewSortedIntArraySize(cap int, unsafe...bool) *SortedIntArray {
|
||||
return &SortedIntArray {
|
||||
mu : rwmutex.New(unsafe...),
|
||||
array : make([]int, 0, cap),
|
||||
@ -38,10 +55,45 @@ func NewSortedIntArray(cap int, unsafe...bool) *SortedIntArray {
|
||||
}
|
||||
}
|
||||
|
||||
// 添加加数据项
|
||||
func (a *SortedIntArray) Add(values...int) {
|
||||
// Create an array with given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 通过给定的slice变量创建排序数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
func NewSortedIntArrayFrom(array []int, unsafe...bool) *SortedIntArray {
|
||||
a := NewSortedIntArraySize(0, unsafe...)
|
||||
a.array = array
|
||||
sort.Ints(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// Set the underlying slice array with the given <array> param.
|
||||
//
|
||||
// 设置底层数组变量.
|
||||
func (a *SortedIntArray) SetArray(array []int) *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
a.array = array
|
||||
sort.Ints(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// Sort the array in increasing order.
|
||||
//
|
||||
// 将数组排序(默认从低到高).
|
||||
func (a *SortedIntArray) Sort() *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
sort.Ints(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// And values to sorted array, the array always keeps sorted.
|
||||
//
|
||||
// 添加数据项.
|
||||
func (a *SortedIntArray) Add(values...int) *SortedIntArray {
|
||||
if len(values) == 0 {
|
||||
return
|
||||
return a
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -62,9 +114,12 @@ func (a *SortedIntArray) Add(values...int) {
|
||||
a.array = append(a.array[0 : index], value)
|
||||
a.array = append(a.array, rear...)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// 获取指定索引的数据项, 调用方注意判断数组边界
|
||||
// Get value by index.
|
||||
//
|
||||
// 获取指定索引的数据项, 调用方注意判断数组边界。
|
||||
func (a *SortedIntArray) Get(index int) int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -72,7 +127,9 @@ func (a *SortedIntArray) Get(index int) int {
|
||||
return value
|
||||
}
|
||||
|
||||
// 删除指定索引的数据项, 调用方注意判断数组边界
|
||||
// Remove an item by index.
|
||||
//
|
||||
// 删除指定索引的数据项, 调用方注意判断数组边界。
|
||||
func (a *SortedIntArray) Remove(index int) int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -92,7 +149,9 @@ func (a *SortedIntArray) Remove(index int) int {
|
||||
return value
|
||||
}
|
||||
|
||||
// 将最左端(索引为0)的数据项移出数组,并返回该数据项
|
||||
// Push new items to the beginning of array.
|
||||
//
|
||||
// 将数据项添加到数组的最左端(索引为0)。
|
||||
func (a *SortedIntArray) PopLeft() int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -101,7 +160,9 @@ func (a *SortedIntArray) PopLeft() int {
|
||||
return value
|
||||
}
|
||||
|
||||
// 将最右端(索引为length - 1)的数据项移出数组,并返回该数据项
|
||||
// Push new items to the end of array.
|
||||
//
|
||||
// 将数据项添加到数组的最右端(索引为length - 1)。
|
||||
func (a *SortedIntArray) PopRight() int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -111,7 +172,76 @@ func (a *SortedIntArray) PopRight() int {
|
||||
return value
|
||||
}
|
||||
|
||||
// 数组长度
|
||||
// Pop an random item from array.
|
||||
//
|
||||
// 随机将一个数据项移出数组,并返回该数据项。
|
||||
func (a *SortedIntArray) PopRand() int {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// Pop <size> items from the beginning of array.
|
||||
//
|
||||
// 将最左端(首部)的size个数据项移出数组,并返回该数据项
|
||||
func (a *SortedIntArray) PopLefts(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
length := len(a.array)
|
||||
if size > length {
|
||||
size = length
|
||||
}
|
||||
value := a.array[0 : size]
|
||||
a.array = a.array[size : ]
|
||||
return value
|
||||
}
|
||||
|
||||
// Pop <size> items from the end of array.
|
||||
//
|
||||
// 将最右端(尾部)的size个数据项移出数组,并返回该数据项
|
||||
func (a *SortedIntArray) PopRights(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - size
|
||||
if index < 0 {
|
||||
index = 0
|
||||
}
|
||||
value := a.array[index :]
|
||||
a.array = a.array[ : index]
|
||||
return value
|
||||
}
|
||||
|
||||
// Get items by range, returns array[start:end].
|
||||
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// 将最右端(尾部)的size个数据项移出数组,并返回该数据项
|
||||
func (a *SortedIntArray) Range(start, end int) []int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
length := len(a.array)
|
||||
if start > length || start > end {
|
||||
return nil
|
||||
}
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
if end > length {
|
||||
end = length
|
||||
}
|
||||
array := ([]int)(nil)
|
||||
if a.mu.IsSafe() {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
array = make([]int, end - start)
|
||||
copy(array, a.array[start : end])
|
||||
} else {
|
||||
array = a.array[start : end]
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// Get the length of array.
|
||||
//
|
||||
// 数组长度。
|
||||
func (a *SortedIntArray) Len() int {
|
||||
a.mu.RLock()
|
||||
length := len(a.array)
|
||||
@ -119,28 +249,54 @@ func (a *SortedIntArray) Len() int {
|
||||
return length
|
||||
}
|
||||
|
||||
// 返回原始数据数组
|
||||
// Calculate the sum of values in an array.
|
||||
//
|
||||
// 对数组中的元素项求和。
|
||||
func (a *SortedIntArray) Sum() (sum int) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for _, v := range a.array {
|
||||
sum += v
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Get the underlying data of array.
|
||||
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// 返回原始数据数组.
|
||||
func (a *SortedIntArray) Slice() []int {
|
||||
array := ([]int)(nil)
|
||||
if a.mu.IsSafe() {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
array = make([]int, len(a.array))
|
||||
for k, v := range a.array {
|
||||
array[k] = v
|
||||
}
|
||||
a.mu.RUnlock()
|
||||
copy(array, a.array)
|
||||
} else {
|
||||
array = a.array
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// 查找指定数值的索引位置,返回索引位置(具体匹配位置或者最后对比位置)及查找结果
|
||||
// 返回值: 最后比较位置, 比较结果
|
||||
func (a *SortedIntArray) Search(value int) (index int, result int) {
|
||||
return a.binSearch(value, true)
|
||||
// Check whether a value exists in the array.
|
||||
//
|
||||
// 查找指定数值是否存在。
|
||||
func (a *SortedIntArray) Contains(value int) bool {
|
||||
return a.Search(value) == 0
|
||||
}
|
||||
|
||||
// Search array by <value>, returns the index of <value>, returns -1 if not exists.
|
||||
//
|
||||
// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1。
|
||||
func (a *SortedIntArray) Search(value int) (index int) {
|
||||
index, _ = a.binSearch(value, true)
|
||||
return
|
||||
}
|
||||
|
||||
// Binary search.
|
||||
//
|
||||
// 二分查找.
|
||||
func (a *SortedIntArray) binSearch(value int, lock bool) (index int, result int) {
|
||||
if len(a.array) == 0 {
|
||||
return -1, -2
|
||||
@ -166,17 +322,24 @@ func (a *SortedIntArray) binSearch(value int, lock bool) (index int, result int)
|
||||
return mid, cmp
|
||||
}
|
||||
|
||||
// 设置是否允许数组唯一
|
||||
func (a *SortedIntArray) SetUnique(unique bool) {
|
||||
// Set unique mark to the array,
|
||||
// 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)
|
||||
if unique && oldUnique != unique {
|
||||
a.doUnique()
|
||||
a.Unique()
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// 清理数组中重复的元素项
|
||||
func (a *SortedIntArray) doUnique() {
|
||||
// Do unique check, remove all repeated items.
|
||||
//
|
||||
// 清理数组中重复的元素项.
|
||||
func (a *SortedIntArray) Unique() *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
i := 0
|
||||
for {
|
||||
@ -190,27 +353,140 @@ func (a *SortedIntArray) doUnique() {
|
||||
}
|
||||
}
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// 清空数据数组
|
||||
func (a *SortedIntArray) Clear() {
|
||||
// Return a new array, which is a copy of current array.
|
||||
//
|
||||
// 克隆当前数组,返回当前数组的一个拷贝。
|
||||
func (a *SortedIntArray) Clone() (newArray *SortedIntArray) {
|
||||
a.mu.RLock()
|
||||
array := make([]int, len(a.array))
|
||||
copy(array, a.array)
|
||||
a.mu.RUnlock()
|
||||
return NewSortedIntArrayFrom(array, !a.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Clear array.
|
||||
//
|
||||
// 清空数据数组。
|
||||
func (a *SortedIntArray) Clear() *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
if len(a.array) > 0 {
|
||||
a.array = make([]int, 0, a.cap)
|
||||
a.array = make([]int, 0)
|
||||
}
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// 使用自定义方法执行加锁修改操作
|
||||
func (a *SortedIntArray) LockFunc(f func(array []int)) {
|
||||
// Lock writing by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁修改操作。
|
||||
func (a *SortedIntArray) LockFunc(f func(array []int)) *SortedIntArray {
|
||||
a.mu.Lock(true)
|
||||
defer a.mu.Unlock(true)
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// 使用自定义方法执行加锁读取操作
|
||||
func (a *SortedIntArray) RLockFunc(f func(array []int)) {
|
||||
// Lock reading by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁读取操作。
|
||||
func (a *SortedIntArray) RLockFunc(f func(array []int)) *SortedIntArray {
|
||||
a.mu.RLock(true)
|
||||
defer a.mu.RUnlock(true)
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// Merge two arrays.
|
||||
//
|
||||
// 合并两个数组.
|
||||
func (a *SortedIntArray) Merge(array *SortedIntArray) *SortedIntArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if a != array {
|
||||
array.mu.RLock()
|
||||
defer array.mu.RUnlock()
|
||||
}
|
||||
a.array = append(a.array, array.array...)
|
||||
sort.Ints(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// Chunks an array into arrays with size elements.
|
||||
// The last chunk may contain less than size elements.
|
||||
//
|
||||
// 将一个数组分割成多个数组,其中每个数组的单元数目由size决定。最后一个数组的单元数目可能会少于size个。
|
||||
func (a *SortedIntArray) Chunk(size int) [][]int {
|
||||
if size < 1 {
|
||||
return nil
|
||||
}
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
length := len(a.array)
|
||||
chunks := int(math.Ceil(float64(length) / float64(size)))
|
||||
var n [][]int
|
||||
for i, end := 0, 0; chunks > 0; chunks-- {
|
||||
end = (i + 1) * size
|
||||
if end > length {
|
||||
end = length
|
||||
}
|
||||
n = append(n, a.array[i*size : end])
|
||||
i++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Extract a slice of the array(If in concurrent safe usage,
|
||||
// it returns a copy of the slice; else a pointer).
|
||||
// It returns the sequence of elements from the array array as specified
|
||||
// by the offset and length parameters.
|
||||
//
|
||||
// 返回根据offset和size参数所指定的数组中的一段序列。
|
||||
func (a *SortedIntArray) SubSlice(offset, size int) []int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if offset > len(a.array) {
|
||||
return nil
|
||||
}
|
||||
if offset + size > len(a.array) {
|
||||
size = len(a.array) - offset
|
||||
}
|
||||
if a.mu.IsSafe() {
|
||||
s := make([]int, size)
|
||||
copy(s, a.array[offset:])
|
||||
return s
|
||||
} else {
|
||||
return a.array[offset:]
|
||||
}
|
||||
}
|
||||
|
||||
// Picks one or more random entries out of an array(a copy),
|
||||
// and returns the key (or keys) of the random entries.
|
||||
//
|
||||
// 从数组中随机取出size个元素项,构成slice返回。
|
||||
func (a *SortedIntArray) Rand(size int) []int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if size > len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
n := make([]int, size)
|
||||
for i, v := range grand.Perm(len(a.array)) {
|
||||
n[i] = a.array[v]
|
||||
if i == size - 1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Join array elements with a string.
|
||||
//
|
||||
// 使用glue字符串串连当前数组的元素项,构造成新的字符串返回。
|
||||
func (a *SortedIntArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return strings.Join(gconv.Strings(a.array), glue)
|
||||
}
|
||||
@ -1,26 +1,52 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package garray
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/container/gtype"
|
||||
"gitee.com/johng/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/util/grand"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 默认按照从低到高进行排序
|
||||
// 默认按照从小到大进行排序
|
||||
type SortedArray struct {
|
||||
mu *rwmutex.RWMutex // 互斥锁
|
||||
cap int // 初始化设置的数组容量
|
||||
array []interface{} // 底层数组
|
||||
unique *gtype.Bool // 是否要求不能重复
|
||||
compareFunc func(v1, v2 interface{}) int // 比较函数,返回值 -1: v1 < v2;0: v1 == v2;1: v1 > v2
|
||||
}
|
||||
|
||||
func NewSortedArray(cap int, compareFunc func(v1, v2 interface{}) int, unsafe...bool) *SortedArray {
|
||||
// Create an empty sorted array.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
// The param <compareFunc> used to compare values to sort in array,
|
||||
// if it returns value < 0, means v1 < v2;
|
||||
// if it returns value = 0, means v1 = v2;
|
||||
// if it returns value > 0, means v1 > v2;
|
||||
//
|
||||
// 创建一个空的排序数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
// 参数compareFunc用于指定排序方法:
|
||||
// 如果返回值 < 0, 表示 v1 < v2;
|
||||
// 如果返回值 = 0, 表示 v1 = v2;
|
||||
// 如果返回值 > 0, 表示 v1 > v2;
|
||||
func NewSortedArray(compareFunc func(v1, v2 interface{}) int, unsafe...bool) *SortedArray {
|
||||
return NewSortedArraySize(0, compareFunc, unsafe...)
|
||||
}
|
||||
|
||||
// Create a sorted array with given size and cap.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 创建一个指定大小的排序数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
func NewSortedArraySize(cap int, compareFunc func(v1, v2 interface{}) int, unsafe...bool) *SortedArray {
|
||||
return &SortedArray{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
unique : gtype.NewBool(),
|
||||
@ -29,10 +55,51 @@ func NewSortedArray(cap int, compareFunc func(v1, v2 interface{}) int, unsafe...
|
||||
}
|
||||
}
|
||||
|
||||
// 添加加数据项
|
||||
func (a *SortedArray) Add(values...interface{}) {
|
||||
// Create an array with given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 通过给定的slice变量创建排序数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
func NewSortedArrayFrom(array []interface{}, compareFunc func(v1, v2 interface{}) int, unsafe...bool) *SortedArray {
|
||||
a := NewSortedArraySize(0, compareFunc, unsafe...)
|
||||
a.array = array
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.compareFunc(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
return a
|
||||
}
|
||||
|
||||
// Set the underlying slice array with the given <array> param.
|
||||
//
|
||||
// 设置底层数组变量.
|
||||
func (a *SortedArray) SetArray(array []interface{}) *SortedArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
a.array = array
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.compareFunc(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
return a
|
||||
}
|
||||
|
||||
// Sort the array by comparing function.
|
||||
//
|
||||
// 将数组按照比较方法进行排序.
|
||||
func (a *SortedArray) Sort() *SortedArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.compareFunc(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
return a
|
||||
}
|
||||
|
||||
// And values to sorted array, the array always keeps sorted.
|
||||
//
|
||||
// 添加数据项.
|
||||
func (a *SortedArray) Add(values...interface{}) *SortedArray {
|
||||
if len(values) == 0 {
|
||||
return
|
||||
return a
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -53,9 +120,12 @@ func (a *SortedArray) Add(values...interface{}) {
|
||||
a.array = append(a.array[0 : index], value)
|
||||
a.array = append(a.array, rear...)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// 获取指定索引的数据项, 调用方注意判断数组边界
|
||||
// Get value by index.
|
||||
//
|
||||
// 获取指定索引的数据项, 调用方注意判断数组边界。
|
||||
func (a *SortedArray) Get(index int) interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -63,7 +133,9 @@ func (a *SortedArray) Get(index int) interface{} {
|
||||
return value
|
||||
}
|
||||
|
||||
// 删除指定索引的数据项, 调用方注意判断数组边界
|
||||
// Remove an item by index.
|
||||
//
|
||||
// 删除指定索引的数据项, 调用方注意判断数组边界。
|
||||
func (a *SortedArray) Remove(index int) interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -83,7 +155,9 @@ func (a *SortedArray) Remove(index int) interface{} {
|
||||
return value
|
||||
}
|
||||
|
||||
// 将最左端(索引为0)的数据项移出数组,并返回该数据项
|
||||
// Push new items to the beginning of array.
|
||||
//
|
||||
// 将数据项添加到数组的最左端(索引为0)。
|
||||
func (a *SortedArray) PopLeft() interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -92,7 +166,9 @@ func (a *SortedArray) PopLeft() interface{} {
|
||||
return value
|
||||
}
|
||||
|
||||
// 将最右端(索引为length - 1)的数据项移出数组,并返回该数据项
|
||||
// Push new items to the end of array.
|
||||
//
|
||||
// 将数据项添加到数组的最右端(索引为length - 1)。
|
||||
func (a *SortedArray) PopRight() interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -102,7 +178,88 @@ func (a *SortedArray) PopRight() interface{} {
|
||||
return value
|
||||
}
|
||||
|
||||
// 数组长度
|
||||
// Pop an random item from array.
|
||||
//
|
||||
// 随机将一个数据项移出数组,并返回该数据项。
|
||||
func (a *SortedArray) PopRand() interface{} {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// Pop <size> items from the beginning of array.
|
||||
//
|
||||
// 将最左端(首部)的size个数据项移出数组,并返回该数据项
|
||||
func (a *SortedArray) PopLefts(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
length := len(a.array)
|
||||
if size > length {
|
||||
size = length
|
||||
}
|
||||
value := a.array[0 : size]
|
||||
a.array = a.array[size : ]
|
||||
return value
|
||||
}
|
||||
|
||||
// Pop <size> items from the end of array.
|
||||
//
|
||||
// 将最右端(尾部)的size个数据项移出数组,并返回该数据项
|
||||
func (a *SortedArray) PopRights(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - size
|
||||
if index < 0 {
|
||||
index = 0
|
||||
}
|
||||
value := a.array[index :]
|
||||
a.array = a.array[ : index]
|
||||
return value
|
||||
}
|
||||
|
||||
// Get items by range, returns array[start:end].
|
||||
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// 将最右端(尾部)的size个数据项移出数组,并返回该数据项
|
||||
func (a *SortedArray) Range(start, end int) []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
length := len(a.array)
|
||||
if start > length || start > end {
|
||||
return nil
|
||||
}
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
if end > length {
|
||||
end = length
|
||||
}
|
||||
array := ([]interface{})(nil)
|
||||
if a.mu.IsSafe() {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
array = make([]interface{}, end - start)
|
||||
copy(array, a.array[start : end])
|
||||
} else {
|
||||
array = a.array[start : end]
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// Calculate the sum of values in an array.
|
||||
//
|
||||
// 对数组中的元素项求和(将元素值转换为int类型后叠加)。
|
||||
func (a *SortedArray) Sum() (sum int) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for _, v := range a.array {
|
||||
sum += gconv.Int(v)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Get the length of array.
|
||||
//
|
||||
// 数组长度。
|
||||
func (a *SortedArray) Len() int {
|
||||
a.mu.RLock()
|
||||
length := len(a.array)
|
||||
@ -110,30 +267,43 @@ func (a *SortedArray) Len() int {
|
||||
return length
|
||||
}
|
||||
|
||||
// 返回原始数据数组
|
||||
// Get the underlying data of array.
|
||||
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// 返回原始数据数组.
|
||||
func (a *SortedArray) Slice() []interface{} {
|
||||
array := ([]interface{})(nil)
|
||||
if a.mu.IsSafe() {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
array = make([]interface{}, len(a.array))
|
||||
for k, v := range a.array {
|
||||
array[k] = v
|
||||
}
|
||||
a.mu.RUnlock()
|
||||
copy(array, a.array)
|
||||
} else {
|
||||
array = a.array
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// 查找指定数值的索引位置,返回索引位置(具体匹配位置或者最后对比位置)及查找结果
|
||||
// 返回值: 最后比较位置, 比较结果
|
||||
func (a *SortedArray) Search(value interface{}) (index int, result int) {
|
||||
return a.binSearch(value, true)
|
||||
// Check whether a value exists in the array.
|
||||
//
|
||||
// 查找指定数值是否存在。
|
||||
func (a *SortedArray) Contains(value interface{}) bool {
|
||||
return a.Search(value) == 0
|
||||
}
|
||||
|
||||
// 查找指定数值的索引位置,返回索引位置(具体匹配位置或者最后对比位置)及查找结果
|
||||
// 返回值: 最后比较位置, 比较结果
|
||||
// Search array by <value>, returns the index of <value>, returns -1 if not exists.
|
||||
//
|
||||
// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1。
|
||||
func (a *SortedArray) Search(value interface{}) (index int) {
|
||||
index, _ = a.binSearch(value, true)
|
||||
return
|
||||
}
|
||||
|
||||
// Binary search.
|
||||
//
|
||||
// 二分查找。查找指定数值的索引位置,返回索引位置(具体匹配位置或者最后对比位置)及查找结果
|
||||
// 返回值: 最后比较位置, 比较结果。
|
||||
func (a *SortedArray) binSearch(value interface{}, lock bool)(index int, result int) {
|
||||
if len(a.array) == 0 {
|
||||
return -1, -2
|
||||
@ -159,17 +329,24 @@ func (a *SortedArray) binSearch(value interface{}, lock bool)(index int, result
|
||||
return mid, cmp
|
||||
}
|
||||
|
||||
// 设置是否允许数组唯一
|
||||
func (a *SortedArray) SetUnique(unique bool) {
|
||||
// Set unique mark to the array,
|
||||
// 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)
|
||||
if unique && oldUnique != unique {
|
||||
a.doUnique()
|
||||
a.Unique()
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// 清理数组中重复的元素项
|
||||
func (a *SortedArray) doUnique() {
|
||||
// Do unique check, remove all repeated items.
|
||||
//
|
||||
// 清理数组中重复的元素项.
|
||||
func (a *SortedArray) Unique() *SortedArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
i := 0
|
||||
@ -183,27 +360,142 @@ func (a *SortedArray) doUnique() {
|
||||
i++
|
||||
}
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// 清空数据数组
|
||||
func (a *SortedArray) Clear() {
|
||||
// Return a new array, which is a copy of current array.
|
||||
//
|
||||
// 克隆当前数组,返回当前数组的一个拷贝。
|
||||
func (a *SortedArray) Clone() (newArray *SortedArray) {
|
||||
a.mu.RLock()
|
||||
array := make([]interface{}, len(a.array))
|
||||
copy(array, a.array)
|
||||
a.mu.RUnlock()
|
||||
return NewSortedArrayFrom(array, a.compareFunc, !a.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Clear array.
|
||||
//
|
||||
// 清空数据数组。
|
||||
func (a *SortedArray) Clear() *SortedArray {
|
||||
a.mu.Lock()
|
||||
if len(a.array) > 0 {
|
||||
a.array = make([]interface{}, 0, a.cap)
|
||||
a.array = make([]interface{}, 0)
|
||||
}
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// 使用自定义方法执行加锁修改操作
|
||||
func (a *SortedArray) LockFunc(f func(array []interface{})) {
|
||||
// Lock writing by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁修改操作。
|
||||
func (a *SortedArray) LockFunc(f func(array []interface{})) *SortedArray {
|
||||
a.mu.Lock(true)
|
||||
defer a.mu.Unlock(true)
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// 使用自定义方法执行加锁读取操作
|
||||
func (a *SortedArray) RLockFunc(f func(array []interface{})) {
|
||||
// Lock reading by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁读取操作。
|
||||
func (a *SortedArray) RLockFunc(f func(array []interface{})) *SortedArray {
|
||||
a.mu.RLock(true)
|
||||
defer a.mu.RUnlock(true)
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// Merge two arrays.
|
||||
//
|
||||
// 合并两个数组.
|
||||
func (a *SortedArray) Merge(array *SortedArray) *SortedArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if a != array {
|
||||
array.mu.RLock()
|
||||
defer array.mu.RUnlock()
|
||||
}
|
||||
a.array = append(a.array, array.array...)
|
||||
sort.Slice(a.array, func(i, j int) bool {
|
||||
return a.compareFunc(a.array[i], a.array[j]) < 0
|
||||
})
|
||||
return a
|
||||
}
|
||||
|
||||
// Chunks an array into arrays with size elements.
|
||||
// The last chunk may contain less than size elements.
|
||||
//
|
||||
// 将一个数组分割成多个数组,其中每个数组的单元数目由size决定。最后一个数组的单元数目可能会少于size个。
|
||||
func (a *SortedArray) Chunk(size int) [][]interface{} {
|
||||
if size < 1 {
|
||||
return nil
|
||||
}
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
length := len(a.array)
|
||||
chunks := int(math.Ceil(float64(length) / float64(size)))
|
||||
var n [][]interface{}
|
||||
for i, end := 0, 0; chunks > 0; chunks-- {
|
||||
end = (i + 1) * size
|
||||
if end > length {
|
||||
end = length
|
||||
}
|
||||
n = append(n, a.array[i*size : end])
|
||||
i++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Extract a slice of the array(If in concurrent safe usage,
|
||||
// it returns a copy of the slice; else a pointer).
|
||||
// It returns the sequence of elements from the array array as specified
|
||||
// by the offset and length parameters.
|
||||
//
|
||||
// 返回根据offset和size参数所指定的数组中的一段序列。
|
||||
func (a *SortedArray) SubSlice(offset, size int) []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if offset > len(a.array) {
|
||||
return nil
|
||||
}
|
||||
if offset + size > len(a.array) {
|
||||
size = len(a.array) - offset
|
||||
}
|
||||
if a.mu.IsSafe() {
|
||||
s := make([]interface{}, size)
|
||||
copy(s, a.array[offset:])
|
||||
return s
|
||||
} else {
|
||||
return a.array[offset:]
|
||||
}
|
||||
}
|
||||
|
||||
// Picks one or more random entries out of an array(a copy),
|
||||
// and returns the key (or keys) of the random entries.
|
||||
//
|
||||
// 从数组中随机取出size个元素项,构成slice返回。
|
||||
func (a *SortedArray) Rand(size int) []interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if size > len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
n := make([]interface{}, size)
|
||||
for i, v := range grand.Perm(len(a.array)) {
|
||||
n[i] = a.array[v]
|
||||
if i == size - 1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Join array elements with a string.
|
||||
//
|
||||
// 使用glue字符串串连当前数组的元素项,构造成新的字符串返回。
|
||||
func (a *SortedArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return strings.Join(gconv.Strings(a.array), glue)
|
||||
}
|
||||
@ -1,27 +1,44 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package garray
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/container/gtype"
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/util/grand"
|
||||
"math"
|
||||
"sort"
|
||||
"strings"
|
||||
"gitee.com/johng/gf/g/internal/rwmutex"
|
||||
)
|
||||
|
||||
// 默认按照从低到高进行排序
|
||||
// 默认按照从小到大进行排序
|
||||
type SortedStringArray struct {
|
||||
mu *rwmutex.RWMutex // 互斥锁
|
||||
cap int // 初始化设置的数组容量
|
||||
array []string // 底层数组
|
||||
unique *gtype.Bool // 是否要求不能重复
|
||||
compareFunc func(v1, v2 string) int // 比较函数,返回值 -1: v1 < v2;0: v1 == v2;1: v1 > v2
|
||||
}
|
||||
|
||||
func NewSortedStringArray(cap int, unsafe...bool) *SortedStringArray {
|
||||
// Create an empty sorted array.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 创建一个空的排序数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
func NewSortedStringArray(unsafe...bool) *SortedStringArray {
|
||||
return NewSortedStringArraySize(0, unsafe...)
|
||||
}
|
||||
|
||||
// Create a sorted array with given size and cap.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 创建一个指定大小的排序数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
func NewSortedStringArraySize(cap int, unsafe...bool) *SortedStringArray {
|
||||
return &SortedStringArray {
|
||||
mu : rwmutex.New(unsafe...),
|
||||
array : make([]string, 0, cap),
|
||||
@ -32,10 +49,45 @@ func NewSortedStringArray(cap int, unsafe...bool) *SortedStringArray {
|
||||
}
|
||||
}
|
||||
|
||||
// 添加加数据项
|
||||
func (a *SortedStringArray) Add(values...string) {
|
||||
// Create an array with given slice <array>.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 通过给定的slice变量创建排序数组对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
func NewSortedStringArrayFrom(array []string, unsafe...bool) *SortedStringArray {
|
||||
a := NewSortedStringArraySize(0, unsafe...)
|
||||
a.array = array
|
||||
sort.Strings(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// Set the underlying slice array with the given <array> param.
|
||||
//
|
||||
// 设置底层数组变量.
|
||||
func (a *SortedStringArray) SetArray(array []string) *SortedStringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
a.array = array
|
||||
sort.Strings(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// Sort the array in increasing order.
|
||||
//
|
||||
// 将数组排序(默认从低到高).
|
||||
func (a *SortedStringArray) Sort() *SortedStringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
sort.Strings(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// And values to sorted array, the array always keeps sorted.
|
||||
//
|
||||
// 添加数据项.
|
||||
func (a *SortedStringArray) Add(values...string) *SortedStringArray {
|
||||
if len(values) == 0 {
|
||||
return
|
||||
return a
|
||||
}
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -56,9 +108,12 @@ func (a *SortedStringArray) Add(values...string) {
|
||||
a.array = append(a.array[0 : index], value)
|
||||
a.array = append(a.array, rear...)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// 获取指定索引的数据项, 调用方注意判断数组边界
|
||||
// Get value by index.
|
||||
//
|
||||
// 获取指定索引的数据项, 调用方注意判断数组边界。
|
||||
func (a *SortedStringArray) Get(index int) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
@ -66,7 +121,9 @@ func (a *SortedStringArray) Get(index int) string {
|
||||
return value
|
||||
}
|
||||
|
||||
// 删除指定索引的数据项, 调用方注意判断数组边界
|
||||
// Remove an item by index.
|
||||
//
|
||||
// 删除指定索引的数据项, 调用方注意判断数组边界。
|
||||
func (a *SortedStringArray) Remove(index int) string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -86,7 +143,9 @@ func (a *SortedStringArray) Remove(index int) string {
|
||||
return value
|
||||
}
|
||||
|
||||
// 将最左端(索引为0)的数据项移出数组,并返回该数据项
|
||||
// Push new items to the beginning of array.
|
||||
//
|
||||
// 将数据项添加到数组的最左端(索引为0)。
|
||||
func (a *SortedStringArray) PopLeft() string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -95,7 +154,9 @@ func (a *SortedStringArray) PopLeft() string {
|
||||
return value
|
||||
}
|
||||
|
||||
// 将最右端(索引为length - 1)的数据项移出数组,并返回该数据项
|
||||
// Push new items to the end of array.
|
||||
//
|
||||
// 将数据项添加到数组的最右端(索引为length - 1)。
|
||||
func (a *SortedStringArray) PopRight() string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
@ -105,7 +166,88 @@ func (a *SortedStringArray) PopRight() string {
|
||||
return value
|
||||
}
|
||||
|
||||
// 数组长度
|
||||
// Pop an random item from array.
|
||||
//
|
||||
// 随机将一个数据项移出数组,并返回该数据项。
|
||||
func (a *SortedStringArray) PopRand() string {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// Pop <size> items from the beginning of array.
|
||||
//
|
||||
// 将最左端(首部)的size个数据项移出数组,并返回该数据项
|
||||
func (a *SortedStringArray) PopLefts(size int) []string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
length := len(a.array)
|
||||
if size > length {
|
||||
size = length
|
||||
}
|
||||
value := a.array[0 : size]
|
||||
a.array = a.array[size : ]
|
||||
return value
|
||||
}
|
||||
|
||||
// Pop <size> items from the end of array.
|
||||
//
|
||||
// 将最右端(尾部)的size个数据项移出数组,并返回该数据项
|
||||
func (a *SortedStringArray) PopRights(size int) []string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
index := len(a.array) - size
|
||||
if index < 0 {
|
||||
index = 0
|
||||
}
|
||||
value := a.array[index :]
|
||||
a.array = a.array[ : index]
|
||||
return value
|
||||
}
|
||||
|
||||
// Get items by range, returns array[start:end].
|
||||
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// 将最右端(尾部)的size个数据项移出数组,并返回该数据项
|
||||
func (a *SortedStringArray) Range(start, end int) []string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
length := len(a.array)
|
||||
if start > length || start > end {
|
||||
return nil
|
||||
}
|
||||
if start < 0 {
|
||||
start = 0
|
||||
}
|
||||
if end > length {
|
||||
end = length
|
||||
}
|
||||
array := ([]string)(nil)
|
||||
if a.mu.IsSafe() {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
array = make([]string, end - start)
|
||||
copy(array, a.array[start : end])
|
||||
} else {
|
||||
array = a.array[start : end]
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// Calculate the sum of values in an array.
|
||||
//
|
||||
// 对数组中的元素项求和(将元素值转换为int类型后叠加)。
|
||||
func (a *SortedStringArray) Sum() (sum int) {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
for _, v := range a.array {
|
||||
sum += gconv.Int(v)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Get the length of array.
|
||||
//
|
||||
// 数组长度。
|
||||
func (a *SortedStringArray) Len() int {
|
||||
a.mu.RLock()
|
||||
length := len(a.array)
|
||||
@ -113,28 +255,42 @@ func (a *SortedStringArray) Len() int {
|
||||
return length
|
||||
}
|
||||
|
||||
// 返回原始数据数组
|
||||
// Get the underlying data of array.
|
||||
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
|
||||
// else a pointer to the underlying data.
|
||||
//
|
||||
// 返回原始数据数组.
|
||||
func (a *SortedStringArray) Slice() []string {
|
||||
array := ([]string)(nil)
|
||||
if a.mu.IsSafe() {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
array = make([]string, len(a.array))
|
||||
for k, v := range a.array {
|
||||
array[k] = v
|
||||
}
|
||||
a.mu.RUnlock()
|
||||
copy(array, a.array)
|
||||
} else {
|
||||
array = a.array
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// 查找指定数值的索引位置,返回索引位置(具体匹配位置或者最后对比位置)及查找结果
|
||||
// 返回值: 最后比较位置, 比较结果
|
||||
func (a *SortedStringArray) Search(value string) (index int, result int) {
|
||||
return a.binSearch(value, true)
|
||||
// Check whether a value exists in the array.
|
||||
//
|
||||
// 查找指定数值是否存在。
|
||||
func (a *SortedStringArray) Contains(value string) bool {
|
||||
return a.Search(value) == 0
|
||||
}
|
||||
|
||||
// Search array by <value>, returns the index of <value>, returns -1 if not exists.
|
||||
//
|
||||
// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1。
|
||||
func (a *SortedStringArray) Search(value string) (index int) {
|
||||
index, _ = a.binSearch(value, true)
|
||||
return
|
||||
}
|
||||
|
||||
// Binary search.
|
||||
//
|
||||
// 二分查找.
|
||||
func (a *SortedStringArray) binSearch(value string, lock bool) (index int, result int) {
|
||||
if len(a.array) == 0 {
|
||||
return -1, -2
|
||||
@ -160,17 +316,24 @@ func (a *SortedStringArray) binSearch(value string, lock bool) (index int, resul
|
||||
return mid, cmp
|
||||
}
|
||||
|
||||
// 设置是否允许数组唯一
|
||||
func (a *SortedStringArray) SetUnique(unique bool) {
|
||||
// Set unique mark to the array,
|
||||
// which means it does not contain any repeated items.
|
||||
// It also do unique check, remove all repeated items.
|
||||
//
|
||||
// 设置是否允许数组唯一.
|
||||
func (a *SortedStringArray) SetUnique(unique bool) *SortedStringArray {
|
||||
oldUnique := a.unique.Val()
|
||||
a.unique.Set(unique)
|
||||
if unique && oldUnique != unique {
|
||||
a.doUnique()
|
||||
a.Unique()
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// 清理数组中重复的元素项
|
||||
func (a *SortedStringArray) doUnique() {
|
||||
// Do unique check, remove all repeated items.
|
||||
//
|
||||
// 清理数组中重复的元素项.
|
||||
func (a *SortedStringArray) Unique() *SortedStringArray {
|
||||
a.mu.Lock()
|
||||
i := 0
|
||||
for {
|
||||
@ -184,27 +347,140 @@ func (a *SortedStringArray) doUnique() {
|
||||
}
|
||||
}
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// 清空数据数组
|
||||
func (a *SortedStringArray) Clear() {
|
||||
// Return a new array, which is a copy of current array.
|
||||
//
|
||||
// 克隆当前数组,返回当前数组的一个拷贝。
|
||||
func (a *SortedStringArray) Clone() (newArray *SortedStringArray) {
|
||||
a.mu.RLock()
|
||||
array := make([]string, len(a.array))
|
||||
copy(array, a.array)
|
||||
a.mu.RUnlock()
|
||||
return NewSortedStringArrayFrom(array, !a.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Clear array.
|
||||
//
|
||||
// 清空数据数组。
|
||||
func (a *SortedStringArray) Clear() *SortedStringArray {
|
||||
a.mu.Lock()
|
||||
if len(a.array) > 0 {
|
||||
a.array = make([]string, 0, a.cap)
|
||||
a.array = make([]string, 0)
|
||||
}
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// 使用自定义方法执行加锁修改操作
|
||||
func (a *SortedStringArray) LockFunc(f func(array []string)) {
|
||||
// Lock writing by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁修改操作。
|
||||
func (a *SortedStringArray) LockFunc(f func(array []string)) *SortedStringArray {
|
||||
a.mu.Lock(true)
|
||||
defer a.mu.Unlock(true)
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// 使用自定义方法执行加锁读取操作
|
||||
func (a *SortedStringArray) RLockFunc(f func(array []string)) {
|
||||
// Lock reading by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁读取操作。
|
||||
func (a *SortedStringArray) RLockFunc(f func(array []string)) *SortedStringArray {
|
||||
a.mu.RLock(true)
|
||||
defer a.mu.RUnlock(true)
|
||||
f(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// Merge two arrays.
|
||||
//
|
||||
// 合并两个数组.
|
||||
func (a *SortedStringArray) Merge(array *SortedStringArray) *SortedStringArray {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if a != array {
|
||||
array.mu.RLock()
|
||||
defer array.mu.RUnlock()
|
||||
}
|
||||
a.array = append(a.array, array.array...)
|
||||
sort.Strings(a.array)
|
||||
return a
|
||||
}
|
||||
|
||||
// Chunks an array into arrays with size elements.
|
||||
// The last chunk may contain less than size elements.
|
||||
//
|
||||
// 将一个数组分割成多个数组,其中每个数组的单元数目由size决定。最后一个数组的单元数目可能会少于size个。
|
||||
func (a *SortedStringArray) Chunk(size int) [][]string {
|
||||
if size < 1 {
|
||||
return nil
|
||||
}
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
length := len(a.array)
|
||||
chunks := int(math.Ceil(float64(length) / float64(size)))
|
||||
var n [][]string
|
||||
for i, end := 0, 0; chunks > 0; chunks-- {
|
||||
end = (i + 1) * size
|
||||
if end > length {
|
||||
end = length
|
||||
}
|
||||
n = append(n, a.array[i*size : end])
|
||||
i++
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Extract a slice of the array(If in concurrent safe usage,
|
||||
// it returns a copy of the slice; else a pointer).
|
||||
// It returns the sequence of elements from the array array as specified
|
||||
// by the offset and length parameters.
|
||||
//
|
||||
// 返回根据offset和size参数所指定的数组中的一段序列。
|
||||
func (a *SortedStringArray) SubSlice(offset, size int) []string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if offset > len(a.array) {
|
||||
return nil
|
||||
}
|
||||
if offset + size > len(a.array) {
|
||||
size = len(a.array) - offset
|
||||
}
|
||||
if a.mu.IsSafe() {
|
||||
s := make([]string, size)
|
||||
copy(s, a.array[offset:])
|
||||
return s
|
||||
} else {
|
||||
return a.array[offset:]
|
||||
}
|
||||
}
|
||||
|
||||
// Picks one or more random entries out of an array(a copy),
|
||||
// and returns the key (or keys) of the random entries.
|
||||
//
|
||||
// 从数组中随机取出size个元素项,构成slice返回。
|
||||
func (a *SortedStringArray) Rand(size int) []string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
if size > len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
n := make([]string, size)
|
||||
for i, v := range grand.Perm(len(a.array)) {
|
||||
n[i] = a.array[v]
|
||||
if i == size - 1 {
|
||||
break
|
||||
}
|
||||
}
|
||||
return n
|
||||
}
|
||||
|
||||
// Join array elements with a string.
|
||||
//
|
||||
// 使用glue字符串串连当前数组的元素项,构造成新的字符串返回。
|
||||
func (a *SortedStringArray) Join(glue string) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return strings.Join(a.array, glue)
|
||||
}
|
||||
@ -1,175 +0,0 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf.
|
||||
|
||||
package garray
|
||||
|
||||
import (
|
||||
"strings"
|
||||
"gitee.com/johng/gf/g/internal/rwmutex"
|
||||
)
|
||||
|
||||
type StringArray struct {
|
||||
mu *rwmutex.RWMutex // 互斥锁
|
||||
cap int // 初始化设置的数组容量
|
||||
size int // 初始化设置的数组大小
|
||||
array []string // 底层数组
|
||||
}
|
||||
|
||||
func NewStringArray(size int, cap int, unsafe...bool) *StringArray {
|
||||
a := &StringArray{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
a.size = size
|
||||
if cap > 0 {
|
||||
a.cap = cap
|
||||
a.array = make([]string, size, cap)
|
||||
} else {
|
||||
a.array = make([]string, size)
|
||||
}
|
||||
return a
|
||||
}
|
||||
|
||||
// 获取指定索引的数据项, 调用方注意判断数组边界
|
||||
func (a *StringArray) Get(index int) string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
value := a.array[index]
|
||||
return value
|
||||
}
|
||||
|
||||
// 设置指定索引的数据项, 调用方注意判断数组边界
|
||||
func (a *StringArray) Set(index int, value string) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
a.array[index] = value
|
||||
}
|
||||
|
||||
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界
|
||||
func (a *StringArray) InsertBefore(index int, value string) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
rear := append([]string{}, a.array[index : ]...)
|
||||
a.array = append(a.array[0 : index], value)
|
||||
a.array = append(a.array, rear...)
|
||||
}
|
||||
|
||||
// 在当前索引位置后插入一个数据项, 调用方注意判断数组边界
|
||||
func (a *StringArray) InsertAfter(index int, value string) {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
rear := append([]string{}, a.array[index + 1:]...)
|
||||
a.array = append(a.array[ 0: index + 1], value)
|
||||
a.array = append(a.array, rear...)
|
||||
}
|
||||
|
||||
// 删除指定索引的数据项, 调用方注意判断数组边界
|
||||
func (a *StringArray) Remove(index int) string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
// 边界删除判断,以提高删除效率
|
||||
if index == 0 {
|
||||
value := a.array[0]
|
||||
a.array = a.array[1 : ]
|
||||
return value
|
||||
} else if index == len(a.array) - 1 {
|
||||
value := a.array[index]
|
||||
a.array = a.array[: index]
|
||||
return value
|
||||
}
|
||||
// 如果非边界删除,会涉及到数组创建,那么删除的效率差一些
|
||||
value := a.array[index]
|
||||
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
|
||||
return value
|
||||
}
|
||||
|
||||
// 追加数据项
|
||||
func (a *StringArray) Append(value...string) {
|
||||
a.mu.Lock()
|
||||
a.array = append(a.array, value...)
|
||||
a.mu.Unlock()
|
||||
}
|
||||
|
||||
// 数组长度
|
||||
func (a *StringArray) Len() int {
|
||||
a.mu.RLock()
|
||||
length := len(a.array)
|
||||
a.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// 返回原始数据数组
|
||||
func (a *StringArray) Slice() []string {
|
||||
array := ([]string)(nil)
|
||||
if a.mu.IsSafe() {
|
||||
a.mu.RLock()
|
||||
array = make([]string, len(a.array))
|
||||
for k, v := range a.array {
|
||||
array[k] = v
|
||||
}
|
||||
a.mu.RUnlock()
|
||||
} else {
|
||||
array = a.array
|
||||
}
|
||||
return array
|
||||
}
|
||||
|
||||
// 清空数据数组
|
||||
func (a *StringArray) Clear() {
|
||||
a.mu.Lock()
|
||||
if len(a.array) > 0 {
|
||||
if a.cap > 0 {
|
||||
a.array = make([]string, a.size, a.cap)
|
||||
} else {
|
||||
a.array = make([]string, a.size)
|
||||
}
|
||||
}
|
||||
a.mu.Unlock()
|
||||
}
|
||||
|
||||
// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1
|
||||
func (a *StringArray) Search(value string) int {
|
||||
if len(a.array) == 0 {
|
||||
return -1
|
||||
}
|
||||
a.mu.RLock()
|
||||
result := -1
|
||||
for index, v := range a.array {
|
||||
if strings.Compare(v, value) == 0 {
|
||||
result = index
|
||||
break
|
||||
}
|
||||
}
|
||||
a.mu.RUnlock()
|
||||
return result
|
||||
}
|
||||
|
||||
// 清理数组中重复的元素项
|
||||
func (a *StringArray) Unique() *StringArray {
|
||||
a.mu.Lock()
|
||||
for i := 0; i < len(a.array) - 1; i++ {
|
||||
for j := i + 1; j < len(a.array); j++ {
|
||||
if a.array[i] == a.array[j] {
|
||||
a.array = append(a.array[ : j], a.array[j + 1 : ]...)
|
||||
}
|
||||
}
|
||||
}
|
||||
a.mu.Unlock()
|
||||
return a
|
||||
}
|
||||
|
||||
// 使用自定义方法执行加锁修改操作
|
||||
func (a *StringArray) LockFunc(f func(array []string)) {
|
||||
a.mu.Lock(true)
|
||||
defer a.mu.Unlock(true)
|
||||
f(a.array)
|
||||
}
|
||||
|
||||
// 使用自定义方法执行加锁读取操作
|
||||
func (a *StringArray) RLockFunc(f func(array []string)) {
|
||||
a.mu.RLock(true)
|
||||
defer a.mu.RUnlock(true)
|
||||
f(a.array)
|
||||
}
|
||||
@ -1,20 +1,20 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// go test *.go -bench=".*" -benchmem
|
||||
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/container/garray"
|
||||
"github.com/gogf/gf/g/container/garray"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var (
|
||||
sortedIntArray = garray.NewSortedIntArray(0)
|
||||
sortedIntArray = garray.NewSortedIntArray()
|
||||
)
|
||||
|
||||
func BenchmarkSortedIntArray_Add(b *testing.B) {
|
||||
|
||||
@ -1,25 +1,24 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// go test *.go
|
||||
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/container/garray"
|
||||
"gitee.com/johng/gf/g/util/gconv"
|
||||
"gitee.com/johng/gf/g/util/gtest"
|
||||
"github.com/gogf/gf/g/container/garray"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"strings"
|
||||
"testing"
|
||||
)
|
||||
|
||||
|
||||
func Test_IntArray_Unique(t *testing.T) {
|
||||
expect := []int{1, 2, 3, 4, 5, 6}
|
||||
array := garray.NewIntArray(0, 0)
|
||||
array := garray.NewIntArray()
|
||||
array.Append(1, 1, 2, 3, 3, 4, 4, 5, 5, 6, 6)
|
||||
array.Unique()
|
||||
gtest.Assert(array.Slice(), expect)
|
||||
@ -27,7 +26,7 @@ func Test_IntArray_Unique(t *testing.T) {
|
||||
|
||||
func Test_SortedIntArray1(t *testing.T) {
|
||||
expect := []int{0,1,2,3,4,5,6,7,8,9,10}
|
||||
array := garray.NewSortedIntArray(0)
|
||||
array := garray.NewSortedIntArray()
|
||||
for i := 10; i > -1; i-- {
|
||||
array.Add(i)
|
||||
}
|
||||
@ -36,7 +35,7 @@ func Test_SortedIntArray1(t *testing.T) {
|
||||
|
||||
func Test_SortedIntArray2(t *testing.T) {
|
||||
expect := []int{0,1,2,3,4,5,6,7,8,9,10}
|
||||
array := garray.NewSortedIntArray(0)
|
||||
array := garray.NewSortedIntArray()
|
||||
for i := 0; i <= 10; i++ {
|
||||
array.Add(i)
|
||||
}
|
||||
@ -45,7 +44,7 @@ func Test_SortedIntArray2(t *testing.T) {
|
||||
|
||||
func Test_SortedStringArray1(t *testing.T) {
|
||||
expect := []string{"0","1","10","2","3","4","5","6","7","8","9"}
|
||||
array := garray.NewSortedStringArray(0)
|
||||
array := garray.NewSortedStringArray()
|
||||
for i := 10; i > -1; i-- {
|
||||
array.Add(gconv.String(i))
|
||||
}
|
||||
@ -54,7 +53,7 @@ func Test_SortedStringArray1(t *testing.T) {
|
||||
|
||||
func Test_SortedStringArray2(t *testing.T) {
|
||||
expect := []string{"0","1","10","2","3","4","5","6","7","8","9"}
|
||||
array := garray.NewSortedStringArray(0)
|
||||
array := garray.NewSortedStringArray()
|
||||
for i := 0; i <= 10; i++ {
|
||||
array.Add(gconv.String(i))
|
||||
}
|
||||
@ -63,7 +62,7 @@ func Test_SortedStringArray2(t *testing.T) {
|
||||
|
||||
func Test_SortedArray1(t *testing.T) {
|
||||
expect := []string{"0","1","10","2","3","4","5","6","7","8","9"}
|
||||
array := garray.NewSortedArray(0, func(v1, v2 interface{}) int {
|
||||
array := garray.NewSortedArray(func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
})
|
||||
for i := 10; i > -1; i-- {
|
||||
@ -74,7 +73,7 @@ func Test_SortedArray1(t *testing.T) {
|
||||
|
||||
func Test_SortedArray2(t *testing.T) {
|
||||
expect := []string{"0","1","10","2","3","4","5","6","7","8","9"}
|
||||
array := garray.NewSortedArray(0, func(v1, v2 interface{}) int {
|
||||
array := garray.NewSortedArray(func(v1, v2 interface{}) int {
|
||||
return strings.Compare(gconv.String(v1), gconv.String(v2))
|
||||
})
|
||||
for i := 0; i <= 10; i++ {
|
||||
192
g/container/garray/garray_z_unit_int_test.go
Normal file
192
g/container/garray/garray_z_unit_int_test.go
Normal file
@ -0,0 +1,192 @@
|
||||
// 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.
|
||||
|
||||
// go test *.go
|
||||
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/garray"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_IntArray_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
expect := []int{0, 1, 2, 3}
|
||||
array := garray.NewIntArrayFrom(expect)
|
||||
gtest.Assert(array.Slice(), expect)
|
||||
array.Set(0, 100)
|
||||
gtest.Assert(array.Get(0), 100)
|
||||
gtest.Assert(array.Get(1), 1)
|
||||
gtest.Assert(array.Search(100), 0)
|
||||
gtest.Assert(array.Contains(100), true)
|
||||
gtest.Assert(array.Remove(0), 100)
|
||||
gtest.Assert(array.Contains(100), false)
|
||||
array.Append(4)
|
||||
gtest.Assert(array.Len(), 4)
|
||||
array.InsertBefore(0, 100)
|
||||
array.InsertAfter(0, 200)
|
||||
gtest.Assert(array.Slice(), []int{100, 200, 1, 2, 3, 4})
|
||||
array.InsertBefore(5, 300)
|
||||
array.InsertAfter(6, 400)
|
||||
gtest.Assert(array.Slice(), []int{100, 200, 1, 2, 3, 300, 4, 400})
|
||||
gtest.Assert(array.Clear().Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Sort(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
expect1 := []int{0, 1, 2, 3}
|
||||
expect2 := []int{3, 2, 1, 0}
|
||||
array := garray.NewIntArray()
|
||||
for i := 3; i >= 0; i-- {
|
||||
array.Append(i)
|
||||
}
|
||||
array.Sort()
|
||||
gtest.Assert(array.Slice(), expect1)
|
||||
array.Sort(true)
|
||||
gtest.Assert(array.Slice(), expect2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Unique(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
expect := []int{1, 1, 2, 3}
|
||||
array := garray.NewIntArrayFrom(expect)
|
||||
gtest.Assert(array.Unique().Slice(), []int{1, 2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_PushAndPop(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
expect := []int{0, 1, 2, 3}
|
||||
array := garray.NewIntArrayFrom(expect)
|
||||
gtest.Assert(array.Slice(), expect)
|
||||
gtest.Assert(array.PopLeft(), 0)
|
||||
gtest.Assert(array.PopRight(), 3)
|
||||
gtest.AssertIN(array.PopRand(), []int{1, 2})
|
||||
gtest.AssertIN(array.PopRand(), []int{1, 2})
|
||||
gtest.Assert(array.Len(), 0)
|
||||
array.PushLeft(1).PushRight(2)
|
||||
gtest.Assert(array.Slice(), []int{1, 2})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_PopLeftsAndPopRights(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
value1 := []int{0,1,2,3,4,5,6}
|
||||
value2 := []int{0,1,2,3,4,5,6}
|
||||
array1 := garray.NewIntArrayFrom(value1)
|
||||
array2 := garray.NewIntArrayFrom(value2)
|
||||
gtest.Assert(array1.PopLefts(2), []int{0,1})
|
||||
gtest.Assert(array1.Slice(), []int{2,3,4,5,6})
|
||||
gtest.Assert(array1.PopRights(2), []int{5,6})
|
||||
gtest.Assert(array1.Slice(), []int{2,3,4})
|
||||
gtest.Assert(array1.PopRights(20), []int{2,3,4})
|
||||
gtest.Assert(array1.Slice(), []int{})
|
||||
gtest.Assert(array2.PopLefts(20), []int{0,1,2,3,4,5,6})
|
||||
gtest.Assert(array2.Slice(), []int{})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Range(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
value1 := []int{0,1,2,3,4,5,6}
|
||||
array1 := garray.NewIntArrayFrom(value1)
|
||||
gtest.Assert(array1.Range(0, 1), []int{0})
|
||||
gtest.Assert(array1.Range(1, 2), []int{1})
|
||||
gtest.Assert(array1.Range(0, 2), []int{0, 1})
|
||||
gtest.Assert(array1.Range(-1, 10), value1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Merge(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []int{0, 1, 2, 3}
|
||||
a2 := []int{4, 5, 6, 7}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
array2 := garray.NewIntArrayFrom(a2)
|
||||
gtest.Assert(array1.Merge(array2).Slice(), []int{0,1,2,3,4,5,6,7})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Fill(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []int{0}
|
||||
a2 := []int{0}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
array2 := garray.NewIntArrayFrom(a2)
|
||||
gtest.Assert(array1.Fill(1, 2, 100).Slice(), []int{0,100,100})
|
||||
gtest.Assert(array2.Fill(0, 2, 100).Slice(), []int{100,100})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Chunk(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []int{1,2,3,4,5}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
chunks := array1.Chunk(2)
|
||||
gtest.Assert(len(chunks), 3)
|
||||
gtest.Assert(chunks[0], []int{1,2})
|
||||
gtest.Assert(chunks[1], []int{3,4})
|
||||
gtest.Assert(chunks[2], []int{5})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Pad(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []int{0}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
gtest.Assert(array1.Pad(3, 1).Slice(), []int{0,1,1})
|
||||
gtest.Assert(array1.Pad(-4, 1).Slice(), []int{1,0,1,1})
|
||||
gtest.Assert(array1.Pad(3, 1).Slice(), []int{1,0,1,1})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_SubSlice(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []int{0,1,2,3,4,5,6}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
gtest.Assert(array1.SubSlice(0, 2), []int{0,1})
|
||||
gtest.Assert(array1.SubSlice(2, 2), []int{2,3})
|
||||
gtest.Assert(array1.SubSlice(5, 8), []int{5,6})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Rand(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []int{0,1,2,3,4,5,6}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
gtest.Assert(len(array1.Rand(2)), 2)
|
||||
gtest.Assert(len(array1.Rand(10)), 7)
|
||||
gtest.AssertIN(array1.Rand(1)[0], a1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Shuffle(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []int{0,1,2,3,4,5,6}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
gtest.Assert(array1.Shuffle().Len(), 7)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Reverse(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []int{0,1,2,3,4,5,6}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
gtest.Assert(array1.Reverse().Slice(), []int{6,5,4,3,2,1,0})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_Join(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []int{0,1,2,3,4,5,6}
|
||||
array1 := garray.NewIntArrayFrom(a1)
|
||||
gtest.Assert(array1.Join("."), "0.1.2.3.4.5.6")
|
||||
})
|
||||
}
|
||||
196
g/container/garray/garray_z_unit_interface_test.go
Normal file
196
g/container/garray/garray_z_unit_interface_test.go
Normal file
@ -0,0 +1,196 @@
|
||||
// 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.
|
||||
|
||||
// go test *.go
|
||||
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/garray"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_Array_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
expect := []interface{}{0, 1, 2, 3}
|
||||
array := garray.NewArrayFrom(expect)
|
||||
gtest.Assert(array.Slice(), expect)
|
||||
array.Set(0, 100)
|
||||
gtest.Assert(array.Get(0), 100)
|
||||
gtest.Assert(array.Get(1), 1)
|
||||
gtest.Assert(array.Search(100), 0)
|
||||
gtest.Assert(array.Contains(100), true)
|
||||
gtest.Assert(array.Remove(0), 100)
|
||||
gtest.Assert(array.Contains(100), false)
|
||||
array.Append(4)
|
||||
gtest.Assert(array.Len(), 4)
|
||||
array.InsertBefore(0, 100)
|
||||
array.InsertAfter(0, 200)
|
||||
gtest.Assert(array.Slice(), []interface{}{100, 200, 1, 2, 3, 4})
|
||||
array.InsertBefore(5, 300)
|
||||
array.InsertAfter(6, 400)
|
||||
gtest.Assert(array.Slice(), []interface{}{100, 200, 1, 2, 3, 300, 4, 400})
|
||||
gtest.Assert(array.Clear().Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Sort(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
expect1 := []interface{}{0, 1, 2, 3}
|
||||
expect2 := []interface{}{3, 2, 1, 0}
|
||||
array := garray.NewArray()
|
||||
for i := 3; i >= 0; i-- {
|
||||
array.Append(i)
|
||||
}
|
||||
array.SortFunc(func(v1, v2 interface{}) bool {
|
||||
return v1.(int) < v2.(int)
|
||||
})
|
||||
gtest.Assert(array.Slice(), expect1)
|
||||
array.SortFunc(func(v1, v2 interface{}) bool {
|
||||
return v1.(int) > v2.(int)
|
||||
})
|
||||
gtest.Assert(array.Slice(), expect2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Unique(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
expect := []interface{}{1, 1, 2, 3}
|
||||
array := garray.NewArrayFrom(expect)
|
||||
gtest.Assert(array.Unique().Slice(), []interface{}{1, 2, 3})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_PushAndPop(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
expect := []interface{}{0, 1, 2, 3}
|
||||
array := garray.NewArrayFrom(expect)
|
||||
gtest.Assert(array.Slice(), expect)
|
||||
gtest.Assert(array.PopLeft(), 0)
|
||||
gtest.Assert(array.PopRight(), 3)
|
||||
gtest.AssertIN(array.PopRand(), []interface{}{1, 2})
|
||||
gtest.AssertIN(array.PopRand(), []interface{}{1, 2})
|
||||
gtest.Assert(array.Len(), 0)
|
||||
array.PushLeft(1).PushRight(2)
|
||||
gtest.Assert(array.Slice(), []interface{}{1, 2})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_PopLeftsAndPopRights(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
value1 := []interface{}{0,1,2,3,4,5,6}
|
||||
value2 := []interface{}{0,1,2,3,4,5,6}
|
||||
array1 := garray.NewArrayFrom(value1)
|
||||
array2 := garray.NewArrayFrom(value2)
|
||||
gtest.Assert(array1.PopLefts(2), []interface{}{0,1})
|
||||
gtest.Assert(array1.Slice(), []interface{}{2,3,4,5,6})
|
||||
gtest.Assert(array1.PopRights(2), []interface{}{5,6})
|
||||
gtest.Assert(array1.Slice(), []interface{}{2,3,4})
|
||||
gtest.Assert(array1.PopRights(20), []interface{}{2,3,4})
|
||||
gtest.Assert(array1.Slice(), []interface{}{})
|
||||
gtest.Assert(array2.PopLefts(20), []interface{}{0,1,2,3,4,5,6})
|
||||
gtest.Assert(array2.Slice(), []interface{}{})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Range(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
value1 := []interface{}{0,1,2,3,4,5,6}
|
||||
array1 := garray.NewArrayFrom(value1)
|
||||
gtest.Assert(array1.Range(0, 1), []interface{}{0})
|
||||
gtest.Assert(array1.Range(1, 2), []interface{}{1})
|
||||
gtest.Assert(array1.Range(0, 2), []interface{}{0, 1})
|
||||
gtest.Assert(array1.Range(-1, 10), value1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Merge(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []interface{}{0, 1, 2, 3}
|
||||
a2 := []interface{}{4, 5, 6, 7}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
array2 := garray.NewArrayFrom(a2)
|
||||
gtest.Assert(array1.Merge(array2).Slice(), []interface{}{0,1,2,3,4,5,6,7})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Fill(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []interface{}{0}
|
||||
a2 := []interface{}{0}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
array2 := garray.NewArrayFrom(a2)
|
||||
gtest.Assert(array1.Fill(1, 2, 100).Slice(), []interface{}{0,100,100})
|
||||
gtest.Assert(array2.Fill(0, 2, 100).Slice(), []interface{}{100,100})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Chunk(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []interface{}{1,2,3,4,5}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
chunks := array1.Chunk(2)
|
||||
gtest.Assert(len(chunks), 3)
|
||||
gtest.Assert(chunks[0], []interface{}{1,2})
|
||||
gtest.Assert(chunks[1], []interface{}{3,4})
|
||||
gtest.Assert(chunks[2], []interface{}{5})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Pad(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []interface{}{0}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
gtest.Assert(array1.Pad(3, 1).Slice(), []interface{}{0,1,1})
|
||||
gtest.Assert(array1.Pad(-4, 1).Slice(), []interface{}{1,0,1,1})
|
||||
gtest.Assert(array1.Pad(3, 1).Slice(), []interface{}{1,0,1,1})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_SubSlice(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []interface{}{0,1,2,3,4,5,6}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
gtest.Assert(array1.SubSlice(0, 2), []interface{}{0,1})
|
||||
gtest.Assert(array1.SubSlice(2, 2), []interface{}{2,3})
|
||||
gtest.Assert(array1.SubSlice(5, 8), []interface{}{5,6})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Rand(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []interface{}{0,1,2,3,4,5,6}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
gtest.Assert(len(array1.Rand(2)), 2)
|
||||
gtest.Assert(len(array1.Rand(10)), 7)
|
||||
gtest.AssertIN(array1.Rand(1)[0], a1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Shuffle(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []interface{}{0,1,2,3,4,5,6}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
gtest.Assert(array1.Shuffle().Len(), 7)
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Reverse(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []interface{}{0,1,2,3,4,5,6}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
gtest.Assert(array1.Reverse().Slice(), []interface{}{6,5,4,3,2,1,0})
|
||||
})
|
||||
}
|
||||
|
||||
func TestArray_Join(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []interface{}{0,1,2,3,4,5,6}
|
||||
array1 := garray.NewArrayFrom(a1)
|
||||
gtest.Assert(array1.Join("."), "0.1.2.3.4.5.6")
|
||||
})
|
||||
}
|
||||
193
g/container/garray/garray_z_unit_string_test.go
Normal file
193
g/container/garray/garray_z_unit_string_test.go
Normal file
@ -0,0 +1,193 @@
|
||||
// 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.
|
||||
|
||||
// go test *.go
|
||||
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/garray"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_StringArray_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
expect := []string{"0", "1", "2", "3"}
|
||||
array := garray.NewStringArrayFrom(expect)
|
||||
gtest.Assert(array.Slice(), expect)
|
||||
array.Set(0, "100")
|
||||
gtest.Assert(array.Get(0), 100)
|
||||
gtest.Assert(array.Get(1), 1)
|
||||
gtest.Assert(array.Search("100"), 0)
|
||||
gtest.Assert(array.Contains("100"), true)
|
||||
gtest.Assert(array.Remove(0), 100)
|
||||
gtest.Assert(array.Contains("100"), false)
|
||||
array.Append("4")
|
||||
gtest.Assert(array.Len(), 4)
|
||||
array.InsertBefore(0, "100")
|
||||
array.InsertAfter(0, "200")
|
||||
gtest.Assert(array.Slice(), []string{"100", "200", "1", "2", "3", "4"})
|
||||
array.InsertBefore(5, "300")
|
||||
array.InsertAfter(6, "400")
|
||||
gtest.Assert(array.Slice(), []string{"100", "200", "1", "2", "3", "300", "4", "400"})
|
||||
gtest.Assert(array.Clear().Len(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStringArray_Sort(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
expect1 := []string{"0", "1", "2", "3"}
|
||||
expect2 := []string{"3", "2", "1", "0"}
|
||||
array := garray.NewStringArray()
|
||||
for i := 3; i >= 0; i-- {
|
||||
array.Append(gconv.String(i))
|
||||
}
|
||||
array.Sort()
|
||||
gtest.Assert(array.Slice(), expect1)
|
||||
array.Sort(true)
|
||||
gtest.Assert(array.Slice(), expect2)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStringArray_Unique(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
expect := []string{"1", "1", "2", "3"}
|
||||
array := garray.NewStringArrayFrom(expect)
|
||||
gtest.Assert(array.Unique().Slice(), []string{"1", "2", "3"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestStringArray_PushAndPop(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
expect := []string{"0", "1", "2", "3"}
|
||||
array := garray.NewStringArrayFrom(expect)
|
||||
gtest.Assert(array.Slice(), expect)
|
||||
gtest.Assert(array.PopLeft(), "0")
|
||||
gtest.Assert(array.PopRight(), "3")
|
||||
gtest.AssertIN(array.PopRand(), []string{"1", "2"})
|
||||
gtest.AssertIN(array.PopRand(), []string{"1", "2"})
|
||||
gtest.Assert(array.Len(), 0)
|
||||
array.PushLeft("1").PushRight("2")
|
||||
gtest.Assert(array.Slice(), []string{"1", "2"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestStringArray_PopLeftsAndPopRights(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
value1 := []string{"0","1","2","3","4","5","6"}
|
||||
value2 := []string{"0","1","2","3","4","5","6"}
|
||||
array1 := garray.NewStringArrayFrom(value1)
|
||||
array2 := garray.NewStringArrayFrom(value2)
|
||||
gtest.Assert(array1.PopLefts(2), []interface{}{"0","1"})
|
||||
gtest.Assert(array1.Slice(), []interface{}{"2","3","4","5","6"})
|
||||
gtest.Assert(array1.PopRights(2), []interface{}{"5","6"})
|
||||
gtest.Assert(array1.Slice(), []interface{}{"2","3","4"})
|
||||
gtest.Assert(array1.PopRights(20), []interface{}{"2","3","4"})
|
||||
gtest.Assert(array1.Slice(), []interface{}{})
|
||||
gtest.Assert(array2.PopLefts(20), []interface{}{"0","1","2","3","4","5","6"})
|
||||
gtest.Assert(array2.Slice(), []interface{}{})
|
||||
})
|
||||
}
|
||||
|
||||
func TestString_Range(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
value1 := []string{"0","1","2","3","4","5","6"}
|
||||
array1 := garray.NewStringArrayFrom(value1)
|
||||
gtest.Assert(array1.Range(0, 1), []interface{}{"0"})
|
||||
gtest.Assert(array1.Range(1, 2), []interface{}{"1"})
|
||||
gtest.Assert(array1.Range(0, 2), []interface{}{"0", "1"})
|
||||
gtest.Assert(array1.Range(-1, 10), value1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStringArray_Merge(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []string{"0", "1", "2", "3"}
|
||||
a2 := []string{"4", "5", "6", "7"}
|
||||
array1 := garray.NewStringArrayFrom(a1)
|
||||
array2 := garray.NewStringArrayFrom(a2)
|
||||
gtest.Assert(array1.Merge(array2).Slice(), []string{"0","1","2","3","4","5","6","7"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestStringArray_Fill(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []string{"0"}
|
||||
a2 := []string{"0"}
|
||||
array1 := garray.NewStringArrayFrom(a1)
|
||||
array2 := garray.NewStringArrayFrom(a2)
|
||||
gtest.Assert(array1.Fill(1, 2, "100").Slice(), []string{"0","100","100"})
|
||||
gtest.Assert(array2.Fill(0, 2, "100").Slice(), []string{"100","100"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestStringArray_Chunk(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []string{"1","2","3","4","5"}
|
||||
array1 := garray.NewStringArrayFrom(a1)
|
||||
chunks := array1.Chunk(2)
|
||||
gtest.Assert(len(chunks), 3)
|
||||
gtest.Assert(chunks[0], []string{"1","2"})
|
||||
gtest.Assert(chunks[1], []string{"3","4"})
|
||||
gtest.Assert(chunks[2], []string{"5"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestStringArray_Pad(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []string{"0"}
|
||||
array1 := garray.NewStringArrayFrom(a1)
|
||||
gtest.Assert(array1.Pad(3, "1").Slice(), []string{"0","1","1"})
|
||||
gtest.Assert(array1.Pad(-4, "1").Slice(), []string{"1","0","1","1"})
|
||||
gtest.Assert(array1.Pad(3, "1").Slice(), []string{"1","0","1","1"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestStringArray_SubSlice(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []string{"0","1","2","3","4","5","6"}
|
||||
array1 := garray.NewStringArrayFrom(a1)
|
||||
gtest.Assert(array1.SubSlice(0, 2), []string{"0","1"})
|
||||
gtest.Assert(array1.SubSlice(2, 2), []string{"2","3"})
|
||||
gtest.Assert(array1.SubSlice(5, 8), []string{"5","6"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestStringArray_Rand(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []string{"0","1","2","3","4","5","6"}
|
||||
array1 := garray.NewStringArrayFrom(a1)
|
||||
gtest.Assert(len(array1.Rand(2)), "2")
|
||||
gtest.Assert(len(array1.Rand(10)), "7")
|
||||
gtest.AssertIN(array1.Rand(1)[0], a1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStringArray_Shuffle(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []string{"0","1","2","3","4","5","6"}
|
||||
array1 := garray.NewStringArrayFrom(a1)
|
||||
gtest.Assert(array1.Shuffle().Len(), 7)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStringArray_Reverse(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []string{"0","1","2","3","4","5","6"}
|
||||
array1 := garray.NewStringArrayFrom(a1)
|
||||
gtest.Assert(array1.Reverse().Slice(), []string{"6","5","4","3","2","1","0"})
|
||||
})
|
||||
}
|
||||
|
||||
func TestStringArray_Join(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []string{"0","1","2","3","4","5","6"}
|
||||
array1 := garray.NewStringArrayFrom(a1)
|
||||
gtest.Assert(array1.Join("."), "0.1.2.3.4.5.6")
|
||||
})
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gchan provides graceful operations for channel.
|
||||
//
|
||||
@ -11,7 +11,7 @@ package gchan
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"gitee.com/johng/gf/g/container/gtype"
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
)
|
||||
|
||||
type Chan struct {
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// go test *.go -bench=".*"
|
||||
|
||||
@ -10,7 +10,7 @@ package gchan_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"gitee.com/johng/gf/g/container/gchan"
|
||||
"github.com/gogf/gf/g/container/gchan"
|
||||
)
|
||||
|
||||
var length = 10000000
|
||||
|
||||
@ -1,18 +1,18 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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 l file,
|
||||
// You can obtain one at https://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
// Package glist provides a concurrent-safe(alternative) doubly linked list.
|
||||
//
|
||||
// 并发安全的双向链表.
|
||||
// 并发安全双向链表.
|
||||
package glist
|
||||
|
||||
import (
|
||||
"container/list"
|
||||
"gitee.com/johng/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
)
|
||||
|
||||
// 变长双向链表
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// go test *.go -bench=".*" -benchmem
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2019 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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 this file,
|
||||
// You can obtain one at https://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package glist
|
||||
|
||||
|
||||
@ -1,21 +1,334 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// 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 kinds of concurrent-safe(alternative) maps.
|
||||
//
|
||||
// 并发安全的哈希MAP.
|
||||
// 并发安全MAP.
|
||||
package gmap
|
||||
|
||||
// 默认的Map对象其实就是InterfaceInterfaceMap的别名。
|
||||
// 注意
|
||||
import "github.com/gogf/gf/g/internal/rwmutex"
|
||||
|
||||
// 注意:
|
||||
// 1、这个Map是所有并发安全Map中效率最低的,如果对效率要求比较高的场合,请合理选择对应数据类型的Map;
|
||||
// 2、这个Map的优点是使用简便,由于键值都是interface{}类型,因此对键值的数据类型要求不高;
|
||||
// 3、底层实现比较类似于sync.Map;
|
||||
type Map = InterfaceInterfaceMap
|
||||
type Map struct {
|
||||
mu *rwmutex.RWMutex
|
||||
m map[interface{}]interface{}
|
||||
}
|
||||
|
||||
func New(safe...bool) *Map {
|
||||
return NewInterfaceInterfaceMap(safe...)
|
||||
// Create an empty hash map.
|
||||
// The param <unsafe> used to specify whether using map with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 创建一个空的哈希表,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
func New(unsafe...bool) *Map {
|
||||
return NewMap(unsafe...)
|
||||
}
|
||||
|
||||
// See New.
|
||||
//
|
||||
// 同New方法。
|
||||
func NewMap(unsafe...bool) *Map {
|
||||
return &Map{
|
||||
m : make(map[interface{}]interface{}),
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// Create a hash map from given map.
|
||||
// Be aware that, the param map is a type of pointer,
|
||||
// there might be some concurrent-safe issues when changing the map outside.
|
||||
//
|
||||
// 基于给定的map变量创建哈希表对象,注意由于map是指针类型,外部对map有操作时会有并发安全问题。
|
||||
func NewFrom(m map[interface{}]interface{}, unsafe...bool) *Map {
|
||||
return &Map{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// Create a hash map from given arrays.
|
||||
// The param <keys> given as the keys of the map,
|
||||
// and <values> as the corresponding values.
|
||||
//
|
||||
// 基于给定的数组变量创建哈希表对象,keys作为键名, values作为键值。
|
||||
// 当keys数组大小比values数组大时,多余的键名将会使用对应类型默认的键值。
|
||||
func NewFromArray(keys []interface{}, values []interface{}, unsafe...bool) *Map {
|
||||
m := make(map[interface{}]interface{})
|
||||
l := len(values)
|
||||
for i, k := range keys {
|
||||
if i < l {
|
||||
m[k] = values[i]
|
||||
} else {
|
||||
m[k] = interface{}(nil)
|
||||
}
|
||||
}
|
||||
return &Map{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate the hash map with custom callback function <f>.
|
||||
// If f returns true, then continue iterating; or false to stop.
|
||||
//
|
||||
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历
|
||||
func (gm *Map) Iterator(f func (k interface{}, v interface{}) bool) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
for k, v := range gm.m {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Clone current hash map with copy of underlying data,
|
||||
// return a new hash map.
|
||||
//
|
||||
// 哈希表克隆.
|
||||
func (gm *Map) Clone() *Map {
|
||||
return NewFrom(gm.Map(), !gm.mu.IsSafe())
|
||||
}
|
||||
|
||||
// Returns copy of the data of the hash map.
|
||||
//
|
||||
// 返回当前哈希表的数据Map.
|
||||
func (gm *Map) Map() map[interface{}]interface{} {
|
||||
m := make(map[interface{}]interface{})
|
||||
gm.mu.RLock()
|
||||
for k, v := range gm.m {
|
||||
m[k] = v
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return m
|
||||
}
|
||||
|
||||
// Set key-value to the hash map.
|
||||
//
|
||||
// 设置键值对
|
||||
func (gm *Map) Set(key interface{}, val interface{}) {
|
||||
gm.mu.Lock()
|
||||
gm.m[key] = val
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// Batch set key-values to the hash map.
|
||||
//
|
||||
// 批量设置键值对
|
||||
func (gm *Map) BatchSet(m map[interface{}]interface{}) {
|
||||
gm.mu.Lock()
|
||||
for k, v := range m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// Get value by key.
|
||||
//
|
||||
// 获取键值
|
||||
func (gm *Map) Get(key interface{}) interface{} {
|
||||
gm.mu.RLock()
|
||||
val, _ := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// 设置kv缓存键值对,内部会对键名的存在性使用写锁进行二次检索确认,如果存在则不再写入;返回键名对应的键值。
|
||||
// 在高并发下有用,防止数据写入的并发逻辑错误。
|
||||
func (gm *Map) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
return v
|
||||
}
|
||||
if f, ok := value.(func() interface {}); ok {
|
||||
value = f()
|
||||
}
|
||||
gm.m[key] = value
|
||||
return value
|
||||
}
|
||||
|
||||
// Get the value by key, or set it with given key-value if not exist.
|
||||
//
|
||||
// 当键名存在时返回其键值,否则写入指定的键值
|
||||
func (gm *Map) GetOrSet(key interface{}, value interface{}) interface{} {
|
||||
if v := gm.Get(key); v == nil {
|
||||
return gm.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// Get the value by key, or set the it with return of callback function <f> if not exist.
|
||||
//
|
||||
// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成
|
||||
func (gm *Map) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
|
||||
if v := gm.Get(key); v == nil {
|
||||
return gm.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// Get the value by key, or set the it with return of callback function <f> if not exist.
|
||||
// The difference with GetOrSetFunc is, it locks in executing callback function <f>.
|
||||
//
|
||||
// 与GetOrSetFunc不同的是,f是在写锁机制内执行
|
||||
func (gm *Map) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} {
|
||||
if v := gm.Get(key); v == nil {
|
||||
return gm.doSetWithLockCheck(key, f)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// Set key-value if the key does not exist, then return true; or else return false.
|
||||
//
|
||||
// 当键名不存在时写入,并返回true;否则返回false。
|
||||
func (gm *Map) SetIfNotExist(key interface{}, value interface{}) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Batch remove by keys.
|
||||
//
|
||||
// 批量删除键值对
|
||||
func (gm *Map) BatchRemove(keys []interface{}) {
|
||||
gm.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(gm.m, key)
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// Remove by given key.
|
||||
//
|
||||
// 返回对应的键值,并删除该键值
|
||||
func (gm *Map) Remove(key interface{}) interface{} {
|
||||
gm.mu.Lock()
|
||||
val, exists := gm.m[key]
|
||||
if exists {
|
||||
delete(gm.m, key)
|
||||
}
|
||||
gm.mu.Unlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// Return all the keys of hash map as a slice.
|
||||
//
|
||||
// 返回键列表.
|
||||
func (gm *Map) Keys() []interface{} {
|
||||
gm.mu.RLock()
|
||||
keys := make([]interface{}, 0)
|
||||
for key, _ := range gm.m {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// Return all the values of hash map as a slice.
|
||||
//
|
||||
// 返回值列表(注意是随机排序)
|
||||
func (gm *Map) Values() []interface{} {
|
||||
gm.mu.RLock()
|
||||
vals := make([]interface{}, 0)
|
||||
for _, val := range gm.m {
|
||||
vals = append(vals, val)
|
||||
}
|
||||
gm.mu.RUnlock()
|
||||
return vals
|
||||
}
|
||||
|
||||
// Check whether a key exist.
|
||||
//
|
||||
// 是否存在某个键
|
||||
func (gm *Map) Contains(key interface{}) bool {
|
||||
gm.mu.RLock()
|
||||
_, exists := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// Get the size of hash map.
|
||||
//
|
||||
// 哈希表大小
|
||||
func (gm *Map) Size() int {
|
||||
gm.mu.RLock()
|
||||
length := len(gm.m)
|
||||
gm.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// Check whether the hash map is empty.
|
||||
//
|
||||
// 哈希表是否为空
|
||||
func (gm *Map) IsEmpty() bool {
|
||||
gm.mu.RLock()
|
||||
empty := len(gm.m) == 0
|
||||
gm.mu.RUnlock()
|
||||
return empty
|
||||
}
|
||||
|
||||
// Clear the hash map, it will remake a new underlying map data map.
|
||||
//
|
||||
// 清空哈希表
|
||||
func (gm *Map) Clear() {
|
||||
gm.mu.Lock()
|
||||
gm.m = make(map[interface{}]interface{})
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// Lock writing with given callback <f>.
|
||||
//
|
||||
// 并发安全锁操作,使用自定义方法执行加锁修改操作
|
||||
func (gm *Map) LockFunc(f func(m map[interface{}]interface{})) {
|
||||
gm.mu.Lock(true)
|
||||
defer gm.mu.Unlock(true)
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// Lock reading with given callback <f>.
|
||||
//
|
||||
// 并发安全锁操作,使用自定义方法执行加锁读取操作
|
||||
func (gm *Map) RLockFunc(f func(m map[interface{}]interface{})) {
|
||||
gm.mu.RLock(true)
|
||||
defer gm.mu.RUnlock(true)
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// Exchange key-value in the hash map, it will change key-value to value-key.
|
||||
//
|
||||
// 交换Map中的键和值.
|
||||
func (gm *Map) Flip() {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
n := make(map[interface{}]interface{}, len(gm.m))
|
||||
for i, v := range gm.m {
|
||||
n[v] = i
|
||||
}
|
||||
gm.m = n
|
||||
}
|
||||
|
||||
// Merge two hash maps.
|
||||
//
|
||||
// 合并两个Map.
|
||||
func (gm *Map) Merge(m *Map) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if m != gm {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
}
|
||||
for k, v := range m.m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
}
|
||||
8
g/container/gmap/gmap_func.go
Normal file
8
g/container/gmap/gmap_func.go
Normal file
@ -0,0 +1,8 @@
|
||||
// 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
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// If a copy of the MIT was not distributed with gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
)
|
||||
|
||||
type IntBoolMap struct {
|
||||
@ -23,22 +23,50 @@ func NewIntBoolMap(unsafe...bool) *IntBoolMap {
|
||||
}
|
||||
}
|
||||
|
||||
// 哈希表克隆
|
||||
func (this *IntBoolMap) Clone() map[int]bool {
|
||||
func NewIntBoolMapFrom(m map[int]bool, unsafe...bool) *IntBoolMap {
|
||||
return &IntBoolMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
func NewIntBoolMapFromArray(keys []int, values []bool, unsafe...bool) *IntBoolMap {
|
||||
m := make(map[int]bool)
|
||||
l := len(values)
|
||||
for i, k := range keys {
|
||||
if i < l {
|
||||
m[k] = values[i]
|
||||
} else {
|
||||
m[k] = false
|
||||
}
|
||||
}
|
||||
return &IntBoolMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// 哈希表克隆.
|
||||
func (gm *IntBoolMap) Clone() *IntBoolMap {
|
||||
return NewIntBoolMapFrom(gm.Map(), !gm.mu.IsSafe())
|
||||
}
|
||||
|
||||
// 返回当前哈希表的数据Map.
|
||||
func (gm *IntBoolMap) Map() map[int]bool {
|
||||
m := make(map[int]bool)
|
||||
this.mu.RLock()
|
||||
for k, v := range this.m {
|
||||
gm.mu.RLock()
|
||||
for k, v := range gm.m {
|
||||
m[k] = v
|
||||
}
|
||||
this.mu.RUnlock()
|
||||
gm.mu.RUnlock()
|
||||
return m
|
||||
}
|
||||
|
||||
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历
|
||||
func (this *IntBoolMap) Iterator(f func (k int, v bool) bool) {
|
||||
this.mu.RLock()
|
||||
defer this.mu.RUnlock()
|
||||
for k, v := range this.m {
|
||||
func (gm *IntBoolMap) Iterator(f func (k int, v bool) bool) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
for k, v := range gm.m {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
@ -46,80 +74,80 @@ func (this *IntBoolMap) Iterator(f func (k int, v bool) bool) {
|
||||
}
|
||||
|
||||
// 设置键值对
|
||||
func (this *IntBoolMap) Set(key int, val bool) {
|
||||
this.mu.Lock()
|
||||
this.m[key] = val
|
||||
this.mu.Unlock()
|
||||
func (gm *IntBoolMap) Set(key int, val bool) {
|
||||
gm.mu.Lock()
|
||||
gm.m[key] = val
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 批量设置键值对
|
||||
func (this *IntBoolMap) BatchSet(m map[int]bool) {
|
||||
this.mu.Lock()
|
||||
func (gm *IntBoolMap) BatchSet(m map[int]bool) {
|
||||
gm.mu.Lock()
|
||||
for k, v := range m {
|
||||
this.m[k] = v
|
||||
gm.m[k] = v
|
||||
}
|
||||
this.mu.Unlock()
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 获取键值
|
||||
func (this *IntBoolMap) Get(key int) bool {
|
||||
this.mu.RLock()
|
||||
val, _ := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *IntBoolMap) Get(key int) bool {
|
||||
gm.mu.RLock()
|
||||
val, _ := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// 设置kv缓存键值对,内部会对键名的存在性使用写锁进行二次检索确认,如果存在则不再写入;返回键名对应的键值。
|
||||
// 在高并发下有用,防止数据写入的并发逻辑错误。
|
||||
func (this *IntBoolMap) doSetWithLockCheck(key int, value bool) bool {
|
||||
this.mu.Lock()
|
||||
if v, ok := this.m[key]; ok {
|
||||
this.mu.Unlock()
|
||||
func (gm *IntBoolMap) doSetWithLockCheck(key int, value bool) bool {
|
||||
gm.mu.Lock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
gm.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
this.m[key] = value
|
||||
this.mu.Unlock()
|
||||
gm.m[key] = value
|
||||
gm.mu.Unlock()
|
||||
return value
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值
|
||||
func (this *IntBoolMap) GetOrSet(key int, value bool) bool {
|
||||
this.mu.RLock()
|
||||
v, ok := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *IntBoolMap) GetOrSet(key int, value bool) bool {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return this.doSetWithLockCheck(key, value)
|
||||
return gm.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成
|
||||
func (this *IntBoolMap) GetOrSetFunc(key int, f func() bool) bool {
|
||||
this.mu.RLock()
|
||||
v, ok := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *IntBoolMap) GetOrSetFunc(key int, f func() bool) bool {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return this.doSetWithLockCheck(key, f())
|
||||
return gm.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// 与GetOrSetFunc不同的是,f是在写锁机制内执行
|
||||
func (this *IntBoolMap) GetOrSetFuncLock(key int, f func() bool) bool {
|
||||
this.mu.RLock()
|
||||
val, ok := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *IntBoolMap) GetOrSetFuncLock(key int, f func() bool) bool {
|
||||
gm.mu.RLock()
|
||||
val, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
this.mu.Lock()
|
||||
defer this.mu.Unlock()
|
||||
if v, ok := this.m[key]; ok {
|
||||
this.mu.Unlock()
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
gm.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
val = f()
|
||||
this.m[key] = val
|
||||
gm.m[key] = val
|
||||
return val
|
||||
} else {
|
||||
return val
|
||||
@ -127,97 +155,110 @@ func (this *IntBoolMap) GetOrSetFuncLock(key int, f func() bool) bool {
|
||||
}
|
||||
|
||||
// 当键名不存在时写入,并返回true;否则返回false。
|
||||
func (this *IntBoolMap) SetIfNotExist(key int, value bool) bool {
|
||||
if !this.Contains(key) {
|
||||
this.doSetWithLockCheck(key, value)
|
||||
func (gm *IntBoolMap) SetIfNotExist(key int, value bool) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 批量删除键值对
|
||||
func (this *IntBoolMap) BatchRemove(keys []int) {
|
||||
this.mu.Lock()
|
||||
func (gm *IntBoolMap) BatchRemove(keys []int) {
|
||||
gm.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(this.m, key)
|
||||
delete(gm.m, key)
|
||||
}
|
||||
this.mu.Unlock()
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 返回对应的键值,并删除该键值
|
||||
func (this *IntBoolMap) Remove(key int) bool {
|
||||
this.mu.Lock()
|
||||
val, exists := this.m[key]
|
||||
func (gm *IntBoolMap) Remove(key int) bool {
|
||||
gm.mu.Lock()
|
||||
val, exists := gm.m[key]
|
||||
if exists {
|
||||
delete(this.m, key)
|
||||
delete(gm.m, key)
|
||||
}
|
||||
this.mu.Unlock()
|
||||
gm.mu.Unlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// 返回键列表
|
||||
func (this *IntBoolMap) Keys() []int {
|
||||
this.mu.RLock()
|
||||
func (gm *IntBoolMap) Keys() []int {
|
||||
gm.mu.RLock()
|
||||
keys := make([]int, 0)
|
||||
for key, _ := range this.m {
|
||||
for key, _ := range gm.m {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
this.mu.RUnlock()
|
||||
gm.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// 返回值列表(注意是随机排序)
|
||||
//func (this *IntBoolMap) Values() []bool {
|
||||
// this.mu.RLock()
|
||||
//func (gm *IntBoolMap) Values() []bool {
|
||||
// gm.mu.RLock()
|
||||
// vals := make([]bool, 0)
|
||||
// for _, val := range this.m {
|
||||
// for _, val := range gm.m {
|
||||
// vals = append(vals, val)
|
||||
// }
|
||||
// this.mu.RUnlock()
|
||||
// gm.mu.RUnlock()
|
||||
// return vals
|
||||
//}
|
||||
|
||||
// 是否存在某个键
|
||||
func (this *IntBoolMap) Contains(key int) bool {
|
||||
this.mu.RLock()
|
||||
_, exists := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *IntBoolMap) Contains(key int) bool {
|
||||
gm.mu.RLock()
|
||||
_, exists := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// 哈希表大小
|
||||
func (this *IntBoolMap) Size() int {
|
||||
this.mu.RLock()
|
||||
length := len(this.m)
|
||||
this.mu.RUnlock()
|
||||
func (gm *IntBoolMap) Size() int {
|
||||
gm.mu.RLock()
|
||||
length := len(gm.m)
|
||||
gm.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// 哈希表是否为空
|
||||
func (this *IntBoolMap) IsEmpty() bool {
|
||||
this.mu.RLock()
|
||||
empty := len(this.m) == 0
|
||||
this.mu.RUnlock()
|
||||
func (gm *IntBoolMap) IsEmpty() bool {
|
||||
gm.mu.RLock()
|
||||
empty := len(gm.m) == 0
|
||||
gm.mu.RUnlock()
|
||||
return empty
|
||||
}
|
||||
|
||||
// 清空哈希表
|
||||
func (this *IntBoolMap) Clear() {
|
||||
this.mu.Lock()
|
||||
this.m = make(map[int]bool)
|
||||
this.mu.Unlock()
|
||||
func (gm *IntBoolMap) Clear() {
|
||||
gm.mu.Lock()
|
||||
gm.m = make(map[int]bool)
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 并发安全锁操作,使用自定义方法执行加锁修改操作
|
||||
func (this *IntBoolMap) LockFunc(f func(m map[int]bool)) {
|
||||
this.mu.Lock(true)
|
||||
defer this.mu.Unlock(true)
|
||||
f(this.m)
|
||||
func (gm *IntBoolMap) LockFunc(f func(m map[int]bool)) {
|
||||
gm.mu.Lock(true)
|
||||
defer gm.mu.Unlock(true)
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 并发安全锁操作,使用自定义方法执行加锁读取操作
|
||||
func (this *IntBoolMap) RLockFunc(f func(m map[int]bool)) {
|
||||
this.mu.RLock(true)
|
||||
defer this.mu.RUnlock(true)
|
||||
f(this.m)
|
||||
func (gm *IntBoolMap) RLockFunc(f func(m map[int]bool)) {
|
||||
gm.mu.RLock(true)
|
||||
defer gm.mu.RUnlock(true)
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 合并两个Map.
|
||||
func (gm *IntBoolMap) Merge(m *IntBoolMap) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if m != gm {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
}
|
||||
for k, v := range m.m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
}
|
||||
@ -1,14 +1,14 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// If a copy of the MIT was not distributed with gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
)
|
||||
|
||||
type IntIntMap struct {
|
||||
@ -23,103 +23,131 @@ func NewIntIntMap(unsafe...bool) *IntIntMap {
|
||||
}
|
||||
}
|
||||
|
||||
func NewIntIntMapFrom(m map[int]int, unsafe...bool) *IntIntMap {
|
||||
return &IntIntMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
func NewIntIntMapFromArray(keys []int, values []int, unsafe...bool) *IntIntMap {
|
||||
m := make(map[int]int)
|
||||
l := len(values)
|
||||
for i, k := range keys {
|
||||
if i < l {
|
||||
m[k] = values[i]
|
||||
} else {
|
||||
m[k] = 0
|
||||
}
|
||||
}
|
||||
return &IntIntMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历
|
||||
func (this *IntIntMap) Iterator(f func (k int, v int) bool) {
|
||||
this.mu.RLock()
|
||||
defer this.mu.RUnlock()
|
||||
for k, v := range this.m {
|
||||
func (gm *IntIntMap) Iterator(f func (k int, v int) bool) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
for k, v := range gm.m {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 哈希表克隆
|
||||
func (this *IntIntMap) Clone() map[int]int {
|
||||
// 哈希表克隆.
|
||||
func (gm *IntIntMap) Clone() *IntIntMap {
|
||||
return NewIntIntMapFrom(gm.Map(), !gm.mu.IsSafe())
|
||||
}
|
||||
|
||||
// 返回当前哈希表的数据Map.
|
||||
func (gm *IntIntMap) Map() map[int]int {
|
||||
m := make(map[int]int)
|
||||
this.mu.RLock()
|
||||
for k, v := range this.m {
|
||||
gm.mu.RLock()
|
||||
for k, v := range gm.m {
|
||||
m[k] = v
|
||||
}
|
||||
this.mu.RUnlock()
|
||||
gm.mu.RUnlock()
|
||||
return m
|
||||
}
|
||||
|
||||
// 设置键值对
|
||||
func (this *IntIntMap) Set(key int, val int) {
|
||||
this.mu.Lock()
|
||||
this.m[key] = val
|
||||
this.mu.Unlock()
|
||||
func (gm *IntIntMap) Set(key int, val int) {
|
||||
gm.mu.Lock()
|
||||
gm.m[key] = val
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 批量设置键值对
|
||||
func (this *IntIntMap) BatchSet(m map[int]int) {
|
||||
this.mu.Lock()
|
||||
func (gm *IntIntMap) BatchSet(m map[int]int) {
|
||||
gm.mu.Lock()
|
||||
for k, v := range m {
|
||||
this.m[k] = v
|
||||
gm.m[k] = v
|
||||
}
|
||||
this.mu.Unlock()
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 获取键值
|
||||
func (this *IntIntMap) Get(key int) (int) {
|
||||
this.mu.RLock()
|
||||
val, _ := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *IntIntMap) Get(key int) (int) {
|
||||
gm.mu.RLock()
|
||||
val, _ := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// 设置kv缓存键值对,内部会对键名的存在性使用写锁进行二次检索确认,如果存在则不再写入;返回键名对应的键值。
|
||||
// 在高并发下有用,防止数据写入的并发逻辑错误。
|
||||
func (this *IntIntMap) doSetWithLockCheck(key int, value int) int {
|
||||
this.mu.Lock()
|
||||
if v, ok := this.m[key]; ok {
|
||||
this.mu.Unlock()
|
||||
func (gm *IntIntMap) doSetWithLockCheck(key int, value int) int {
|
||||
gm.mu.Lock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
gm.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
this.m[key] = value
|
||||
this.mu.Unlock()
|
||||
gm.m[key] = value
|
||||
gm.mu.Unlock()
|
||||
return value
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值
|
||||
func (this *IntIntMap) GetOrSet(key int, value int) int {
|
||||
this.mu.RLock()
|
||||
v, ok := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *IntIntMap) GetOrSet(key int, value int) int {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return this.doSetWithLockCheck(key, value)
|
||||
return gm.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成
|
||||
func (this *IntIntMap) GetOrSetFunc(key int, f func() int) int {
|
||||
this.mu.RLock()
|
||||
v, ok := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *IntIntMap) GetOrSetFunc(key int, f func() int) int {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return this.doSetWithLockCheck(key, f())
|
||||
return gm.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// 与GetOrSetFunc不同的是,f是在写锁机制内执行
|
||||
func (this *IntIntMap) GetOrSetFuncLock(key int, f func() int) int {
|
||||
this.mu.RLock()
|
||||
val, ok := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *IntIntMap) GetOrSetFuncLock(key int, f func() int) int {
|
||||
gm.mu.RLock()
|
||||
val, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
this.mu.Lock()
|
||||
defer this.mu.Unlock()
|
||||
if v, ok := this.m[key]; ok {
|
||||
this.mu.Unlock()
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
gm.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
val = f()
|
||||
this.m[key] = val
|
||||
gm.m[key] = val
|
||||
return val
|
||||
} else {
|
||||
return val
|
||||
@ -127,97 +155,121 @@ func (this *IntIntMap) GetOrSetFuncLock(key int, f func() int) int {
|
||||
}
|
||||
|
||||
// 当键名不存在时写入,并返回true;否则返回false。
|
||||
func (this *IntIntMap) SetIfNotExist(key int, value int) bool {
|
||||
if !this.Contains(key) {
|
||||
this.doSetWithLockCheck(key, value)
|
||||
func (gm *IntIntMap) SetIfNotExist(key int, value int) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 批量删除键值对
|
||||
func (this *IntIntMap) BatchRemove(keys []int) {
|
||||
this.mu.Lock()
|
||||
func (gm *IntIntMap) BatchRemove(keys []int) {
|
||||
gm.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(this.m, key)
|
||||
delete(gm.m, key)
|
||||
}
|
||||
this.mu.Unlock()
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 返回对应的键值,并删除该键值
|
||||
func (this *IntIntMap) Remove(key int) int {
|
||||
this.mu.Lock()
|
||||
val, exists := this.m[key]
|
||||
func (gm *IntIntMap) Remove(key int) int {
|
||||
gm.mu.Lock()
|
||||
val, exists := gm.m[key]
|
||||
if exists {
|
||||
delete(this.m, key)
|
||||
delete(gm.m, key)
|
||||
}
|
||||
this.mu.Unlock()
|
||||
gm.mu.Unlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// 返回键列表
|
||||
func (this *IntIntMap) Keys() []int {
|
||||
this.mu.RLock()
|
||||
func (gm *IntIntMap) Keys() []int {
|
||||
gm.mu.RLock()
|
||||
keys := make([]int, 0)
|
||||
for key, _ := range this.m {
|
||||
for key, _ := range gm.m {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
this.mu.RUnlock()
|
||||
gm.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// 返回值列表(注意是随机排序)
|
||||
func (this *IntIntMap) Values() []int {
|
||||
this.mu.RLock()
|
||||
func (gm *IntIntMap) Values() []int {
|
||||
gm.mu.RLock()
|
||||
vals := make([]int, 0)
|
||||
for _, val := range this.m {
|
||||
for _, val := range gm.m {
|
||||
vals = append(vals, val)
|
||||
}
|
||||
this.mu.RUnlock()
|
||||
gm.mu.RUnlock()
|
||||
return vals
|
||||
}
|
||||
|
||||
// 是否存在某个键
|
||||
func (this *IntIntMap) Contains(key int) bool {
|
||||
this.mu.RLock()
|
||||
_, exists := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *IntIntMap) Contains(key int) bool {
|
||||
gm.mu.RLock()
|
||||
_, exists := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// 哈希表大小
|
||||
func (this *IntIntMap) Size() int {
|
||||
this.mu.RLock()
|
||||
length := len(this.m)
|
||||
this.mu.RUnlock()
|
||||
func (gm *IntIntMap) Size() int {
|
||||
gm.mu.RLock()
|
||||
length := len(gm.m)
|
||||
gm.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// 哈希表是否为空
|
||||
func (this *IntIntMap) IsEmpty() bool {
|
||||
this.mu.RLock()
|
||||
empty := len(this.m) == 0
|
||||
this.mu.RUnlock()
|
||||
func (gm *IntIntMap) IsEmpty() bool {
|
||||
gm.mu.RLock()
|
||||
empty := len(gm.m) == 0
|
||||
gm.mu.RUnlock()
|
||||
return empty
|
||||
}
|
||||
|
||||
// 清空哈希表
|
||||
func (this *IntIntMap) Clear() {
|
||||
this.mu.Lock()
|
||||
this.m = make(map[int]int)
|
||||
this.mu.Unlock()
|
||||
func (gm *IntIntMap) Clear() {
|
||||
gm.mu.Lock()
|
||||
gm.m = make(map[int]int)
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 并发安全锁操作,使用自定义方法执行加锁修改操作
|
||||
func (this *IntIntMap) LockFunc(f func(m map[int]int)) {
|
||||
this.mu.Lock(true)
|
||||
defer this.mu.Unlock(true)
|
||||
f(this.m)
|
||||
func (gm *IntIntMap) LockFunc(f func(m map[int]int)) {
|
||||
gm.mu.Lock(true)
|
||||
defer gm.mu.Unlock(true)
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 并发安全锁操作,使用自定义方法执行加锁读取操作
|
||||
func (this *IntIntMap) RLockFunc(f func(m map[int]int)) {
|
||||
this.mu.RLock(true)
|
||||
defer this.mu.RUnlock(true)
|
||||
f(this.m)
|
||||
func (gm *IntIntMap) RLockFunc(f func(m map[int]int)) {
|
||||
gm.mu.RLock(true)
|
||||
defer gm.mu.RUnlock(true)
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 交换Map中的键和值.
|
||||
func (gm *IntIntMap) Flip() {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
n := make(map[int]int, len(gm.m))
|
||||
for k, v := range gm.m {
|
||||
n[v] = k
|
||||
}
|
||||
gm.m = n
|
||||
}
|
||||
|
||||
// 合并两个Map.
|
||||
func (gm *IntIntMap) Merge(m *IntIntMap) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if m != gm {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
}
|
||||
for k, v := range m.m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,13 +1,16 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// If a copy of the MIT was not distributed with gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
package gmap
|
||||
|
||||
import "gitee.com/johng/gf/g/internal/rwmutex"
|
||||
import (
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
type IntInterfaceMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
@ -21,186 +24,238 @@ func NewIntInterfaceMap(unsafe...bool) *IntInterfaceMap {
|
||||
}
|
||||
}
|
||||
|
||||
func NewIntInterfaceMapFrom(m map[int]interface{}, unsafe...bool) *IntInterfaceMap {
|
||||
return &IntInterfaceMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
func NewIntInterfaceMapFromArray(keys []int, values []interface{}, unsafe...bool) *IntInterfaceMap {
|
||||
m := make(map[int]interface{})
|
||||
l := len(values)
|
||||
for i, k := range keys {
|
||||
if i < l {
|
||||
m[k] = values[i]
|
||||
} else {
|
||||
m[k] = interface{}(nil)
|
||||
}
|
||||
}
|
||||
return &IntInterfaceMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历
|
||||
func (this *IntInterfaceMap) Iterator(f func (k int, v interface{}) bool) {
|
||||
this.mu.RLock()
|
||||
defer this.mu.RUnlock()
|
||||
for k, v := range this.m {
|
||||
func (gm *IntInterfaceMap) Iterator(f func (k int, v interface{}) bool) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
for k, v := range gm.m {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 哈希表克隆
|
||||
func (this *IntInterfaceMap) Clone() map[int]interface{} {
|
||||
// 哈希表克隆.
|
||||
func (gm *IntInterfaceMap) Clone() *IntInterfaceMap {
|
||||
return NewIntInterfaceMapFrom(gm.Map(), !gm.mu.IsSafe())
|
||||
}
|
||||
|
||||
// 返回当前哈希表的数据Map.
|
||||
func (gm *IntInterfaceMap) Map() map[int]interface{} {
|
||||
m := make(map[int]interface{})
|
||||
this.mu.RLock()
|
||||
for k, v := range this.m {
|
||||
gm.mu.RLock()
|
||||
for k, v := range gm.m {
|
||||
m[k] = v
|
||||
}
|
||||
this.mu.RUnlock()
|
||||
gm.mu.RUnlock()
|
||||
return m
|
||||
}
|
||||
|
||||
// 设置键值对
|
||||
func (this *IntInterfaceMap) Set(key int, val interface{}) {
|
||||
this.mu.Lock()
|
||||
this.m[key] = val
|
||||
this.mu.Unlock()
|
||||
func (gm *IntInterfaceMap) Set(key int, val interface{}) {
|
||||
gm.mu.Lock()
|
||||
gm.m[key] = val
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 批量设置键值对
|
||||
func (this *IntInterfaceMap) BatchSet(m map[int]interface{}) {
|
||||
this.mu.Lock()
|
||||
func (gm *IntInterfaceMap) BatchSet(m map[int]interface{}) {
|
||||
gm.mu.Lock()
|
||||
for k, v := range m {
|
||||
this.m[k] = v
|
||||
gm.m[k] = v
|
||||
}
|
||||
this.mu.Unlock()
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 获取键值
|
||||
func (this *IntInterfaceMap) Get(key int) (interface{}) {
|
||||
this.mu.RLock()
|
||||
val, _ := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *IntInterfaceMap) Get(key int) (interface{}) {
|
||||
gm.mu.RLock()
|
||||
val, _ := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// 设置kv缓存键值对,内部会对键名的存在性使用写锁进行二次检索确认,如果存在则不再写入;返回键名对应的键值。
|
||||
// 在高并发下有用,防止数据写入的并发逻辑错误。
|
||||
func (this *IntInterfaceMap) doSetWithLockCheck(key int, value interface{}) interface{} {
|
||||
this.mu.Lock()
|
||||
defer this.mu.Unlock()
|
||||
if v, ok := this.m[key]; ok {
|
||||
func (gm *IntInterfaceMap) doSetWithLockCheck(key int, value interface{}) interface{} {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
return v
|
||||
}
|
||||
if f, ok := value.(func() interface {}); ok {
|
||||
value = f()
|
||||
}
|
||||
this.m[key] = value
|
||||
gm.m[key] = value
|
||||
return value
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值
|
||||
func (this *IntInterfaceMap) GetOrSet(key int, value interface{}) interface{} {
|
||||
if v := this.Get(key); v == nil {
|
||||
return this.doSetWithLockCheck(key, value)
|
||||
func (gm *IntInterfaceMap) GetOrSet(key int, value interface{}) interface{} {
|
||||
if v := gm.Get(key); v == nil {
|
||||
return gm.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成
|
||||
func (this *IntInterfaceMap) GetOrSetFunc(key int, f func() interface{}) interface{} {
|
||||
if v := this.Get(key); v == nil {
|
||||
return this.doSetWithLockCheck(key, f())
|
||||
func (gm *IntInterfaceMap) GetOrSetFunc(key int, f func() interface{}) interface{} {
|
||||
if v := gm.Get(key); v == nil {
|
||||
return gm.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// 与GetOrSetFunc不同的是,f是在写锁机制内执行
|
||||
func (this *IntInterfaceMap) GetOrSetFuncLock(key int, f func() interface{}) interface{} {
|
||||
if v := this.Get(key); v == nil {
|
||||
return this.doSetWithLockCheck(key, f)
|
||||
func (gm *IntInterfaceMap) GetOrSetFuncLock(key int, f func() interface{}) interface{} {
|
||||
if v := gm.Get(key); v == nil {
|
||||
return gm.doSetWithLockCheck(key, f)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// 当键名不存在时写入,并返回true;否则返回false。
|
||||
func (this *IntInterfaceMap) SetIfNotExist(key int, value interface{}) bool {
|
||||
if !this.Contains(key) {
|
||||
this.doSetWithLockCheck(key, value)
|
||||
func (gm *IntInterfaceMap) SetIfNotExist(key int, value interface{}) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 批量删除键值对
|
||||
func (this *IntInterfaceMap) BatchRemove(keys []int) {
|
||||
this.mu.Lock()
|
||||
func (gm *IntInterfaceMap) BatchRemove(keys []int) {
|
||||
gm.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(this.m, key)
|
||||
delete(gm.m, key)
|
||||
}
|
||||
this.mu.Unlock()
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 返回对应的键值,并删除该键值
|
||||
func (this *IntInterfaceMap) Remove(key int) interface{} {
|
||||
this.mu.Lock()
|
||||
val, exists := this.m[key]
|
||||
func (gm *IntInterfaceMap) Remove(key int) interface{} {
|
||||
gm.mu.Lock()
|
||||
val, exists := gm.m[key]
|
||||
if exists {
|
||||
delete(this.m, key)
|
||||
delete(gm.m, key)
|
||||
}
|
||||
this.mu.Unlock()
|
||||
gm.mu.Unlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// 返回键列表
|
||||
func (this *IntInterfaceMap) Keys() []int {
|
||||
this.mu.RLock()
|
||||
func (gm *IntInterfaceMap) Keys() []int {
|
||||
gm.mu.RLock()
|
||||
keys := make([]int, 0)
|
||||
for key, _ := range this.m {
|
||||
for key, _ := range gm.m {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
this.mu.RUnlock()
|
||||
gm.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// 返回值列表(注意是随机排序)
|
||||
func (this *IntInterfaceMap) Values() []interface{} {
|
||||
this.mu.RLock()
|
||||
func (gm *IntInterfaceMap) Values() []interface{} {
|
||||
gm.mu.RLock()
|
||||
vals := make([]interface{}, 0)
|
||||
for _, val := range this.m {
|
||||
for _, val := range gm.m {
|
||||
vals = append(vals, val)
|
||||
}
|
||||
this.mu.RUnlock()
|
||||
gm.mu.RUnlock()
|
||||
return vals
|
||||
}
|
||||
|
||||
// 是否存在某个键
|
||||
func (this *IntInterfaceMap) Contains(key int) bool {
|
||||
this.mu.RLock()
|
||||
_, exists := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *IntInterfaceMap) Contains(key int) bool {
|
||||
gm.mu.RLock()
|
||||
_, exists := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// 哈希表大小
|
||||
func (this *IntInterfaceMap) Size() int {
|
||||
this.mu.RLock()
|
||||
length := len(this.m)
|
||||
this.mu.RUnlock()
|
||||
func (gm *IntInterfaceMap) Size() int {
|
||||
gm.mu.RLock()
|
||||
length := len(gm.m)
|
||||
gm.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// 哈希表是否为空
|
||||
func (this *IntInterfaceMap) IsEmpty() bool {
|
||||
this.mu.RLock()
|
||||
empty := len(this.m) == 0
|
||||
this.mu.RUnlock()
|
||||
func (gm *IntInterfaceMap) IsEmpty() bool {
|
||||
gm.mu.RLock()
|
||||
empty := len(gm.m) == 0
|
||||
gm.mu.RUnlock()
|
||||
return empty
|
||||
}
|
||||
|
||||
// 清空哈希表
|
||||
func (this *IntInterfaceMap) Clear() {
|
||||
this.mu.Lock()
|
||||
this.m = make(map[int]interface{})
|
||||
this.mu.Unlock()
|
||||
func (gm *IntInterfaceMap) Clear() {
|
||||
gm.mu.Lock()
|
||||
gm.m = make(map[int]interface{})
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 并发安全锁操作,使用自定义方法执行加锁修改操作
|
||||
func (this *IntInterfaceMap) LockFunc(f func(m map[int]interface{})) {
|
||||
this.mu.Lock()
|
||||
defer this.mu.Unlock()
|
||||
f(this.m)
|
||||
func (gm *IntInterfaceMap) LockFunc(f func(m map[int]interface{})) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 并发安全锁操作,使用自定义方法执行加锁读取操作
|
||||
func (this *IntInterfaceMap) RLockFunc(f func(m map[int]interface{})) {
|
||||
this.mu.RLock(true)
|
||||
defer this.mu.RUnlock(true)
|
||||
f(this.m)
|
||||
func (gm *IntInterfaceMap) RLockFunc(f func(m map[int]interface{})) {
|
||||
gm.mu.RLock(true)
|
||||
defer gm.mu.RUnlock(true)
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 交换Map中的键和值.
|
||||
func (gm *IntInterfaceMap) Flip() {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
n := make(map[int]interface{}, len(gm.m))
|
||||
for k, v := range gm.m {
|
||||
n[gconv.Int(v)] = k
|
||||
}
|
||||
gm.m = n
|
||||
}
|
||||
|
||||
// 合并两个Map.
|
||||
func (gm *IntInterfaceMap) Merge(m *IntInterfaceMap) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if m != gm {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
}
|
||||
for k, v := range m.m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
}
|
||||
@ -1,14 +1,15 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// If a copy of the MIT was not distributed with gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
type IntStringMap struct {
|
||||
@ -23,103 +24,131 @@ func NewIntStringMap(unsafe...bool) *IntStringMap {
|
||||
}
|
||||
}
|
||||
|
||||
func NewIntStringMapFrom(m map[int]string, unsafe...bool) *IntStringMap {
|
||||
return &IntStringMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
func NewIntStringMapFromArray(keys []int, values []string, unsafe...bool) *IntStringMap {
|
||||
m := make(map[int]string)
|
||||
l := len(values)
|
||||
for i, k := range keys {
|
||||
if i < l {
|
||||
m[k] = values[i]
|
||||
} else {
|
||||
m[k] = ""
|
||||
}
|
||||
}
|
||||
return &IntStringMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历
|
||||
func (this *IntStringMap) Iterator(f func (k int, v string) bool) {
|
||||
this.mu.RLock()
|
||||
defer this.mu.RUnlock()
|
||||
for k, v := range this.m {
|
||||
func (gm *IntStringMap) Iterator(f func (k int, v string) bool) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
for k, v := range gm.m {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 哈希表克隆
|
||||
func (this *IntStringMap) Clone() map[int]string {
|
||||
// 哈希表克隆.
|
||||
func (gm *IntStringMap) Clone() *IntStringMap {
|
||||
return NewIntStringMapFrom(gm.Map(), !gm.mu.IsSafe())
|
||||
}
|
||||
|
||||
// 返回当前哈希表的数据Map.
|
||||
func (gm *IntStringMap) Map() map[int]string {
|
||||
m := make(map[int]string)
|
||||
this.mu.RLock()
|
||||
for k, v := range this.m {
|
||||
gm.mu.RLock()
|
||||
for k, v := range gm.m {
|
||||
m[k] = v
|
||||
}
|
||||
this.mu.RUnlock()
|
||||
gm.mu.RUnlock()
|
||||
return m
|
||||
}
|
||||
|
||||
// 设置键值对
|
||||
func (this *IntStringMap) Set(key int, val string) {
|
||||
this.mu.Lock()
|
||||
this.m[key] = val
|
||||
this.mu.Unlock()
|
||||
func (gm *IntStringMap) Set(key int, val string) {
|
||||
gm.mu.Lock()
|
||||
gm.m[key] = val
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 批量设置键值对
|
||||
func (this *IntStringMap) BatchSet(m map[int]string) {
|
||||
this.mu.Lock()
|
||||
func (gm *IntStringMap) BatchSet(m map[int]string) {
|
||||
gm.mu.Lock()
|
||||
for k, v := range m {
|
||||
this.m[k] = v
|
||||
gm.m[k] = v
|
||||
}
|
||||
this.mu.Unlock()
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 获取键值
|
||||
func (this *IntStringMap) Get(key int) string {
|
||||
this.mu.RLock()
|
||||
val, _ := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *IntStringMap) Get(key int) string {
|
||||
gm.mu.RLock()
|
||||
val, _ := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// 设置kv缓存键值对,内部会对键名的存在性使用写锁进行二次检索确认,如果存在则不再写入;返回键名对应的键值。
|
||||
// 在高并发下有用,防止数据写入的并发逻辑错误。
|
||||
func (this *IntStringMap) doSetWithLockCheck(key int, value string) string {
|
||||
this.mu.Lock()
|
||||
if v, ok := this.m[key]; ok {
|
||||
this.mu.Unlock()
|
||||
func (gm *IntStringMap) doSetWithLockCheck(key int, value string) string {
|
||||
gm.mu.Lock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
gm.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
this.m[key] = value
|
||||
this.mu.Unlock()
|
||||
gm.m[key] = value
|
||||
gm.mu.Unlock()
|
||||
return value
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值
|
||||
func (this *IntStringMap) GetOrSet(key int, value string) string {
|
||||
this.mu.RLock()
|
||||
v, ok := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *IntStringMap) GetOrSet(key int, value string) string {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return this.doSetWithLockCheck(key, value)
|
||||
return gm.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成
|
||||
func (this *IntStringMap) GetOrSetFunc(key int, f func() string) string {
|
||||
this.mu.RLock()
|
||||
v, ok := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *IntStringMap) GetOrSetFunc(key int, f func() string) string {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return this.doSetWithLockCheck(key, f())
|
||||
return gm.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// 与GetOrSetFunc不同的是,f是在写锁机制内执行
|
||||
func (this *IntStringMap) GetOrSetFuncLock(key int, f func() string) string {
|
||||
this.mu.RLock()
|
||||
val, ok := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *IntStringMap) GetOrSetFuncLock(key int, f func() string) string {
|
||||
gm.mu.RLock()
|
||||
val, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
this.mu.Lock()
|
||||
defer this.mu.Unlock()
|
||||
if v, ok := this.m[key]; ok {
|
||||
this.mu.Unlock()
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
gm.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
val = f()
|
||||
this.m[key] = val
|
||||
gm.m[key] = val
|
||||
return val
|
||||
} else {
|
||||
return val
|
||||
@ -127,97 +156,121 @@ func (this *IntStringMap) GetOrSetFuncLock(key int, f func() string) string {
|
||||
}
|
||||
|
||||
// 当键名不存在时写入,并返回true;否则返回false。
|
||||
func (this *IntStringMap) SetIfNotExist(key int, value string) bool {
|
||||
if !this.Contains(key) {
|
||||
this.doSetWithLockCheck(key, value)
|
||||
func (gm *IntStringMap) SetIfNotExist(key int, value string) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 批量删除键值对
|
||||
func (this *IntStringMap) BatchRemove(keys []int) {
|
||||
this.mu.Lock()
|
||||
func (gm *IntStringMap) BatchRemove(keys []int) {
|
||||
gm.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(this.m, key)
|
||||
delete(gm.m, key)
|
||||
}
|
||||
this.mu.Unlock()
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 返回对应的键值,并删除该键值
|
||||
func (this *IntStringMap) Remove(key int) string {
|
||||
this.mu.Lock()
|
||||
val, exists := this.m[key]
|
||||
func (gm *IntStringMap) Remove(key int) string {
|
||||
gm.mu.Lock()
|
||||
val, exists := gm.m[key]
|
||||
if exists {
|
||||
delete(this.m, key)
|
||||
delete(gm.m, key)
|
||||
}
|
||||
this.mu.Unlock()
|
||||
gm.mu.Unlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// 返回键列表
|
||||
func (this *IntStringMap) Keys() []int {
|
||||
this.mu.RLock()
|
||||
func (gm *IntStringMap) Keys() []int {
|
||||
gm.mu.RLock()
|
||||
keys := make([]int, 0)
|
||||
for key, _ := range this.m {
|
||||
for key, _ := range gm.m {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
this.mu.RUnlock()
|
||||
gm.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// 返回值列表(注意是随机排序)
|
||||
func (this *IntStringMap) Values() []string {
|
||||
this.mu.RLock()
|
||||
func (gm *IntStringMap) Values() []string {
|
||||
gm.mu.RLock()
|
||||
vals := make([]string, 0)
|
||||
for _, val := range this.m {
|
||||
for _, val := range gm.m {
|
||||
vals = append(vals, val)
|
||||
}
|
||||
this.mu.RUnlock()
|
||||
gm.mu.RUnlock()
|
||||
return vals
|
||||
}
|
||||
|
||||
// 是否存在某个键
|
||||
func (this *IntStringMap) Contains(key int) bool {
|
||||
this.mu.RLock()
|
||||
_, exists := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *IntStringMap) Contains(key int) bool {
|
||||
gm.mu.RLock()
|
||||
_, exists := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// 哈希表大小
|
||||
func (this *IntStringMap) Size() int {
|
||||
this.mu.RLock()
|
||||
length := len(this.m)
|
||||
this.mu.RUnlock()
|
||||
func (gm *IntStringMap) Size() int {
|
||||
gm.mu.RLock()
|
||||
length := len(gm.m)
|
||||
gm.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// 哈希表是否为空
|
||||
func (this *IntStringMap) IsEmpty() bool {
|
||||
this.mu.RLock()
|
||||
empty := len(this.m) == 0
|
||||
this.mu.RUnlock()
|
||||
func (gm *IntStringMap) IsEmpty() bool {
|
||||
gm.mu.RLock()
|
||||
empty := len(gm.m) == 0
|
||||
gm.mu.RUnlock()
|
||||
return empty
|
||||
}
|
||||
|
||||
// 清空哈希表
|
||||
func (this *IntStringMap) Clear() {
|
||||
this.mu.Lock()
|
||||
this.m = make(map[int]string)
|
||||
this.mu.Unlock()
|
||||
func (gm *IntStringMap) Clear() {
|
||||
gm.mu.Lock()
|
||||
gm.m = make(map[int]string)
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 并发安全锁操作,使用自定义方法执行加锁修改操作
|
||||
func (this *IntStringMap) LockFunc(f func(m map[int]string)) {
|
||||
this.mu.Lock(true)
|
||||
defer this.mu.Unlock(true)
|
||||
f(this.m)
|
||||
func (gm *IntStringMap) LockFunc(f func(m map[int]string)) {
|
||||
gm.mu.Lock(true)
|
||||
defer gm.mu.Unlock(true)
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 并发安全锁操作,使用自定义方法执行加锁读取操作
|
||||
func (this *IntStringMap) RLockFunc(f func(m map[int]string)) {
|
||||
this.mu.RLock(true)
|
||||
defer this.mu.RUnlock(true)
|
||||
f(this.m)
|
||||
func (gm *IntStringMap) RLockFunc(f func(m map[int]string)) {
|
||||
gm.mu.RLock(true)
|
||||
defer gm.mu.RUnlock(true)
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 交换Map中的键和值.
|
||||
func (gm *IntStringMap) Flip() {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
n := make(map[int]string, len(gm.m))
|
||||
for k, v := range gm.m {
|
||||
n[gconv.Int(v)] = gconv.String(k)
|
||||
}
|
||||
gm.m = n
|
||||
}
|
||||
|
||||
// 合并两个Map.
|
||||
func (gm *IntStringMap) Merge(m *IntStringMap) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if m != gm {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
}
|
||||
for k, v := range m.m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
}
|
||||
@ -1,208 +0,0 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf.
|
||||
//
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/internal/rwmutex"
|
||||
)
|
||||
|
||||
type InterfaceInterfaceMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
m map[interface{}]interface{}
|
||||
}
|
||||
|
||||
func NewInterfaceInterfaceMap(unsafe...bool) *InterfaceInterfaceMap {
|
||||
return &InterfaceInterfaceMap{
|
||||
m : make(map[interface{}]interface{}),
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历
|
||||
func (this *InterfaceInterfaceMap) Iterator(f func (k interface{}, v interface{}) bool) {
|
||||
this.mu.RLock()
|
||||
defer this.mu.RUnlock()
|
||||
for k, v := range this.m {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 哈希表克隆
|
||||
func (this *InterfaceInterfaceMap) Clone() map[interface{}]interface{} {
|
||||
m := make(map[interface{}]interface{})
|
||||
this.mu.RLock()
|
||||
for k, v := range this.m {
|
||||
m[k] = v
|
||||
}
|
||||
this.mu.RUnlock()
|
||||
return m
|
||||
}
|
||||
|
||||
// 设置键值对
|
||||
func (this *InterfaceInterfaceMap) Set(key interface{}, val interface{}) {
|
||||
this.mu.Lock()
|
||||
this.m[key] = val
|
||||
this.mu.Unlock()
|
||||
}
|
||||
|
||||
// 批量设置键值对
|
||||
func (this *InterfaceInterfaceMap) BatchSet(m map[interface{}]interface{}) {
|
||||
this.mu.Lock()
|
||||
for k, v := range m {
|
||||
this.m[k] = v
|
||||
}
|
||||
this.mu.Unlock()
|
||||
}
|
||||
|
||||
// 获取键值
|
||||
func (this *InterfaceInterfaceMap) Get(key interface{}) interface{} {
|
||||
this.mu.RLock()
|
||||
val, _ := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// 设置kv缓存键值对,内部会对键名的存在性使用写锁进行二次检索确认,如果存在则不再写入;返回键名对应的键值。
|
||||
// 在高并发下有用,防止数据写入的并发逻辑错误。
|
||||
func (this *InterfaceInterfaceMap) doSetWithLockCheck(key interface{}, value interface{}) interface{} {
|
||||
this.mu.Lock()
|
||||
defer this.mu.Unlock()
|
||||
if v, ok := this.m[key]; ok {
|
||||
return v
|
||||
}
|
||||
if f, ok := value.(func() interface {}); ok {
|
||||
value = f()
|
||||
}
|
||||
this.m[key] = value
|
||||
return value
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值
|
||||
func (this *InterfaceInterfaceMap) GetOrSet(key interface{}, value interface{}) interface{} {
|
||||
if v := this.Get(key); v == nil {
|
||||
return this.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成
|
||||
func (this *InterfaceInterfaceMap) GetOrSetFunc(key interface{}, f func() interface{}) interface{} {
|
||||
if v := this.Get(key); v == nil {
|
||||
return this.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// 与GetOrSetFunc不同的是,f是在写锁机制内执行
|
||||
func (this *InterfaceInterfaceMap) GetOrSetFuncLock(key interface{}, f func() interface{}) interface{} {
|
||||
if v := this.Get(key); v == nil {
|
||||
return this.doSetWithLockCheck(key, f)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// 当键名不存在时写入,并返回true;否则返回false。
|
||||
func (this *InterfaceInterfaceMap) SetIfNotExist(key interface{}, value interface{}) bool {
|
||||
if !this.Contains(key) {
|
||||
this.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 批量删除键值对
|
||||
func (this *InterfaceInterfaceMap) BatchRemove(keys []interface{}) {
|
||||
this.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(this.m, key)
|
||||
}
|
||||
this.mu.Unlock()
|
||||
}
|
||||
|
||||
// 返回对应的键值,并删除该键值
|
||||
func (this *InterfaceInterfaceMap) Remove(key interface{}) interface{} {
|
||||
this.mu.Lock()
|
||||
val, exists := this.m[key]
|
||||
if exists {
|
||||
delete(this.m, key)
|
||||
}
|
||||
this.mu.Unlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// 返回键列表
|
||||
func (this *InterfaceInterfaceMap) Keys() []interface{} {
|
||||
this.mu.RLock()
|
||||
keys := make([]interface{}, 0)
|
||||
for key, _ := range this.m {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
this.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// 返回值列表(注意是随机排序)
|
||||
func (this *InterfaceInterfaceMap) Values() []interface{} {
|
||||
this.mu.RLock()
|
||||
vals := make([]interface{}, 0)
|
||||
for _, val := range this.m {
|
||||
vals = append(vals, val)
|
||||
}
|
||||
this.mu.RUnlock()
|
||||
return vals
|
||||
}
|
||||
|
||||
// 是否存在某个键
|
||||
func (this *InterfaceInterfaceMap) Contains(key interface{}) bool {
|
||||
this.mu.RLock()
|
||||
_, exists := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// 哈希表大小
|
||||
func (this *InterfaceInterfaceMap) Size() int {
|
||||
this.mu.RLock()
|
||||
length := len(this.m)
|
||||
this.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// 哈希表是否为空
|
||||
func (this *InterfaceInterfaceMap) IsEmpty() bool {
|
||||
this.mu.RLock()
|
||||
empty := len(this.m) == 0
|
||||
this.mu.RUnlock()
|
||||
return empty
|
||||
}
|
||||
|
||||
// 清空哈希表
|
||||
func (this *InterfaceInterfaceMap) Clear() {
|
||||
this.mu.Lock()
|
||||
this.m = make(map[interface{}]interface{})
|
||||
this.mu.Unlock()
|
||||
}
|
||||
|
||||
// 并发安全锁操作,使用自定义方法执行加锁修改操作
|
||||
func (this *InterfaceInterfaceMap) LockFunc(f func(m map[interface{}]interface{})) {
|
||||
this.mu.Lock(true)
|
||||
defer this.mu.Unlock(true)
|
||||
f(this.m)
|
||||
}
|
||||
|
||||
// 并发安全锁操作,使用自定义方法执行加锁读取操作
|
||||
func (this *InterfaceInterfaceMap) RLockFunc(f func(m map[interface{}]interface{})) {
|
||||
this.mu.RLock(true)
|
||||
defer this.mu.RUnlock(true)
|
||||
f(this.m)
|
||||
}
|
||||
@ -1,14 +1,14 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// If a copy of the MIT was not distributed with gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
)
|
||||
|
||||
type StringBoolMap struct {
|
||||
@ -23,103 +23,131 @@ func NewStringBoolMap(unsafe...bool) *StringBoolMap {
|
||||
}
|
||||
}
|
||||
|
||||
func NewStringBoolMapFrom(m map[string]bool, unsafe...bool) *StringBoolMap {
|
||||
return &StringBoolMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
func NewStringBoolMapFromArray(keys []string, values []bool, unsafe...bool) *StringBoolMap {
|
||||
m := make(map[string]bool)
|
||||
l := len(values)
|
||||
for i, k := range keys {
|
||||
if i < l {
|
||||
m[k] = values[i]
|
||||
} else {
|
||||
m[k] = false
|
||||
}
|
||||
}
|
||||
return &StringBoolMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历
|
||||
func (this *StringBoolMap) Iterator(f func (k string, v bool) bool) {
|
||||
this.mu.RLock()
|
||||
defer this.mu.RUnlock()
|
||||
for k, v := range this.m {
|
||||
func (gm *StringBoolMap) Iterator(f func (k string, v bool) bool) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
for k, v := range gm.m {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 哈希表克隆
|
||||
func (this *StringBoolMap) Clone() map[string]bool {
|
||||
// 哈希表克隆.
|
||||
func (gm *StringBoolMap) Clone() *StringBoolMap {
|
||||
return NewStringBoolMapFrom(gm.Map(), !gm.mu.IsSafe())
|
||||
}
|
||||
|
||||
// 返回当前哈希表的数据Map.
|
||||
func (gm *StringBoolMap) Map() map[string]bool {
|
||||
m := make(map[string]bool)
|
||||
this.mu.RLock()
|
||||
for k, v := range this.m {
|
||||
gm.mu.RLock()
|
||||
for k, v := range gm.m {
|
||||
m[k] = v
|
||||
}
|
||||
this.mu.RUnlock()
|
||||
gm.mu.RUnlock()
|
||||
return m
|
||||
}
|
||||
|
||||
// 设置键值对
|
||||
func (this *StringBoolMap) Set(key string, val bool) {
|
||||
this.mu.Lock()
|
||||
this.m[key] = val
|
||||
this.mu.Unlock()
|
||||
func (gm *StringBoolMap) Set(key string, val bool) {
|
||||
gm.mu.Lock()
|
||||
gm.m[key] = val
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 批量设置键值对
|
||||
func (this *StringBoolMap) BatchSet(m map[string]bool) {
|
||||
this.mu.Lock()
|
||||
func (gm *StringBoolMap) BatchSet(m map[string]bool) {
|
||||
gm.mu.Lock()
|
||||
for k, v := range m {
|
||||
this.m[k] = v
|
||||
gm.m[k] = v
|
||||
}
|
||||
this.mu.Unlock()
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 获取键值
|
||||
func (this *StringBoolMap) Get(key string) bool {
|
||||
this.mu.RLock()
|
||||
val, _ := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *StringBoolMap) Get(key string) bool {
|
||||
gm.mu.RLock()
|
||||
val, _ := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// 设置kv缓存键值对,内部会对键名的存在性使用写锁进行二次检索确认,如果存在则不再写入;返回键名对应的键值。
|
||||
// 在高并发下有用,防止数据写入的并发逻辑错误。
|
||||
func (this *StringBoolMap) doSetWithLockCheck(key string, value bool) bool {
|
||||
this.mu.Lock()
|
||||
if v, ok := this.m[key]; ok {
|
||||
this.mu.Unlock()
|
||||
func (gm *StringBoolMap) doSetWithLockCheck(key string, value bool) bool {
|
||||
gm.mu.Lock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
gm.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
this.m[key] = value
|
||||
this.mu.Unlock()
|
||||
gm.m[key] = value
|
||||
gm.mu.Unlock()
|
||||
return value
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值
|
||||
func (this *StringBoolMap) GetOrSet(key string, value bool) bool {
|
||||
this.mu.RLock()
|
||||
v, ok := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *StringBoolMap) GetOrSet(key string, value bool) bool {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return this.doSetWithLockCheck(key, value)
|
||||
return gm.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成
|
||||
func (this *StringBoolMap) GetOrSetFunc(key string, f func() bool) bool {
|
||||
this.mu.RLock()
|
||||
v, ok := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *StringBoolMap) GetOrSetFunc(key string, f func() bool) bool {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return this.doSetWithLockCheck(key, f())
|
||||
return gm.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// 与GetOrSetFunc不同的是,f是在写锁机制内执行
|
||||
func (this *StringBoolMap) GetOrSetFuncLock(key string, f func() bool) bool {
|
||||
this.mu.RLock()
|
||||
val, ok := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *StringBoolMap) GetOrSetFuncLock(key string, f func() bool) bool {
|
||||
gm.mu.RLock()
|
||||
val, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
this.mu.Lock()
|
||||
defer this.mu.Unlock()
|
||||
if v, ok := this.m[key]; ok {
|
||||
this.mu.Unlock()
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
gm.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
val = f()
|
||||
this.m[key] = val
|
||||
gm.m[key] = val
|
||||
return val
|
||||
} else {
|
||||
return val
|
||||
@ -127,97 +155,110 @@ func (this *StringBoolMap) GetOrSetFuncLock(key string, f func() bool) bool {
|
||||
}
|
||||
|
||||
// 当键名不存在时写入,并返回true;否则返回false。
|
||||
func (this *StringBoolMap) SetIfNotExist(key string, value bool) bool {
|
||||
if !this.Contains(key) {
|
||||
this.doSetWithLockCheck(key, value)
|
||||
func (gm *StringBoolMap) SetIfNotExist(key string, value bool) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 批量删除键值对
|
||||
func (this *StringBoolMap) BatchRemove(keys []string) {
|
||||
this.mu.Lock()
|
||||
func (gm *StringBoolMap) BatchRemove(keys []string) {
|
||||
gm.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(this.m, key)
|
||||
delete(gm.m, key)
|
||||
}
|
||||
this.mu.Unlock()
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 返回对应的键值,并删除该键值
|
||||
func (this *StringBoolMap) Remove(key string) bool {
|
||||
this.mu.Lock()
|
||||
val, exists := this.m[key]
|
||||
func (gm *StringBoolMap) Remove(key string) bool {
|
||||
gm.mu.Lock()
|
||||
val, exists := gm.m[key]
|
||||
if exists {
|
||||
delete(this.m, key)
|
||||
delete(gm.m, key)
|
||||
}
|
||||
this.mu.Unlock()
|
||||
gm.mu.Unlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// 返回键列表
|
||||
func (this *StringBoolMap) Keys() []string {
|
||||
this.mu.RLock()
|
||||
func (gm *StringBoolMap) Keys() []string {
|
||||
gm.mu.RLock()
|
||||
keys := make([]string, 0)
|
||||
for key, _ := range this.m {
|
||||
for key, _ := range gm.m {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
this.mu.RUnlock()
|
||||
gm.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// 返回值列表(注意是随机排序)
|
||||
//func (this *StringBoolMap) Values() []bool {
|
||||
// this.mu.RLock()
|
||||
//func (gm *StringBoolMap) Values() []bool {
|
||||
// gm.mu.RLock()
|
||||
// vals := make([]bool, 0)
|
||||
// for _, val := range this.m {
|
||||
// for _, val := range gm.m {
|
||||
// vals = append(vals, val)
|
||||
// }
|
||||
// this.mu.RUnlock()
|
||||
// gm.mu.RUnlock()
|
||||
// return vals
|
||||
//}
|
||||
|
||||
// 是否存在某个键
|
||||
func (this *StringBoolMap) Contains(key string) bool {
|
||||
this.mu.RLock()
|
||||
_, exists := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *StringBoolMap) Contains(key string) bool {
|
||||
gm.mu.RLock()
|
||||
_, exists := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// 哈希表大小
|
||||
func (this *StringBoolMap) Size() int {
|
||||
this.mu.RLock()
|
||||
length := len(this.m)
|
||||
this.mu.RUnlock()
|
||||
func (gm *StringBoolMap) Size() int {
|
||||
gm.mu.RLock()
|
||||
length := len(gm.m)
|
||||
gm.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// 哈希表是否为空
|
||||
func (this *StringBoolMap) IsEmpty() bool {
|
||||
this.mu.RLock()
|
||||
empty := len(this.m) == 0
|
||||
this.mu.RUnlock()
|
||||
func (gm *StringBoolMap) IsEmpty() bool {
|
||||
gm.mu.RLock()
|
||||
empty := len(gm.m) == 0
|
||||
gm.mu.RUnlock()
|
||||
return empty
|
||||
}
|
||||
|
||||
// 清空哈希表
|
||||
func (this *StringBoolMap) Clear() {
|
||||
this.mu.Lock()
|
||||
this.m = make(map[string]bool)
|
||||
this.mu.Unlock()
|
||||
func (gm *StringBoolMap) Clear() {
|
||||
gm.mu.Lock()
|
||||
gm.m = make(map[string]bool)
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 并发安全锁操作,使用自定义方法执行加锁修改操作
|
||||
func (this *StringBoolMap) LockFunc(f func(m map[string]bool)) {
|
||||
this.mu.Lock(true)
|
||||
defer this.mu.Unlock(true)
|
||||
f(this.m)
|
||||
func (gm *StringBoolMap) LockFunc(f func(m map[string]bool)) {
|
||||
gm.mu.Lock(true)
|
||||
defer gm.mu.Unlock(true)
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 并发安全锁操作,使用自定义方法执行加锁读取操作
|
||||
func (this *StringBoolMap) RLockFunc(f func(m map[string]bool)) {
|
||||
this.mu.RLock(true)
|
||||
defer this.mu.RUnlock(true)
|
||||
f(this.m)
|
||||
func (gm *StringBoolMap) RLockFunc(f func(m map[string]bool)) {
|
||||
gm.mu.RLock(true)
|
||||
defer gm.mu.RUnlock(true)
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 合并两个Map.
|
||||
func (gm *StringBoolMap) Merge(m *StringBoolMap) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if m != gm {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
}
|
||||
for k, v := range m.m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,16 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// If a copy of the MIT was not distributed with gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
package gmap
|
||||
|
||||
import "gitee.com/johng/gf/g/internal/rwmutex"
|
||||
import (
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
type StringIntMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
@ -21,103 +24,131 @@ func NewStringIntMap(unsafe...bool) *StringIntMap {
|
||||
}
|
||||
}
|
||||
|
||||
func NewStringIntMapFrom(m map[string]int, unsafe...bool) *StringIntMap {
|
||||
return &StringIntMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
func NewStringIntMapFromArray(keys []string, values []int, unsafe...bool) *StringIntMap {
|
||||
m := make(map[string]int)
|
||||
l := len(values)
|
||||
for i, k := range keys {
|
||||
if i < l {
|
||||
m[k] = values[i]
|
||||
} else {
|
||||
m[k] = 0
|
||||
}
|
||||
}
|
||||
return &StringIntMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历
|
||||
func (this *StringIntMap) Iterator(f func (k string, v int) bool) {
|
||||
this.mu.RLock()
|
||||
defer this.mu.RUnlock()
|
||||
for k, v := range this.m {
|
||||
func (gm *StringIntMap) Iterator(f func (k string, v int) bool) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
for k, v := range gm.m {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 哈希表克隆
|
||||
func (this *StringIntMap) Clone() map[string]int {
|
||||
// 哈希表克隆.
|
||||
func (gm *StringIntMap) Clone() *StringIntMap {
|
||||
return NewStringIntMapFrom(gm.Map(), !gm.mu.IsSafe())
|
||||
}
|
||||
|
||||
// 返回当前哈希表的数据Map.
|
||||
func (gm *StringIntMap) Map() map[string]int {
|
||||
m := make(map[string]int)
|
||||
this.mu.RLock()
|
||||
for k, v := range this.m {
|
||||
gm.mu.RLock()
|
||||
for k, v := range gm.m {
|
||||
m[k] = v
|
||||
}
|
||||
this.mu.RUnlock()
|
||||
gm.mu.RUnlock()
|
||||
return m
|
||||
}
|
||||
|
||||
// 设置键值对
|
||||
func (this *StringIntMap) Set(key string, val int) {
|
||||
this.mu.Lock()
|
||||
this.m[key] = val
|
||||
this.mu.Unlock()
|
||||
func (gm *StringIntMap) Set(key string, val int) {
|
||||
gm.mu.Lock()
|
||||
gm.m[key] = val
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 批量设置键值对
|
||||
func (this *StringIntMap) BatchSet(m map[string]int) {
|
||||
this.mu.Lock()
|
||||
func (gm *StringIntMap) BatchSet(m map[string]int) {
|
||||
gm.mu.Lock()
|
||||
for k, v := range m {
|
||||
this.m[k] = v
|
||||
gm.m[k] = v
|
||||
}
|
||||
this.mu.Unlock()
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 获取键值
|
||||
func (this *StringIntMap) Get(key string) int {
|
||||
this.mu.RLock()
|
||||
val, _ := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *StringIntMap) Get(key string) int {
|
||||
gm.mu.RLock()
|
||||
val, _ := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// 设置kv缓存键值对,内部会对键名的存在性使用写锁进行二次检索确认,如果存在则不再写入;返回键名对应的键值。
|
||||
// 在高并发下有用,防止数据写入的并发逻辑错误。
|
||||
func (this *StringIntMap) doSetWithLockCheck(key string, value int) int {
|
||||
this.mu.Lock()
|
||||
if v, ok := this.m[key]; ok {
|
||||
this.mu.Unlock()
|
||||
func (gm *StringIntMap) doSetWithLockCheck(key string, value int) int {
|
||||
gm.mu.Lock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
gm.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
this.m[key] = value
|
||||
this.mu.Unlock()
|
||||
gm.m[key] = value
|
||||
gm.mu.Unlock()
|
||||
return value
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值
|
||||
func (this *StringIntMap) GetOrSet(key string, value int) int {
|
||||
this.mu.RLock()
|
||||
v, ok := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *StringIntMap) GetOrSet(key string, value int) int {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return this.doSetWithLockCheck(key, value)
|
||||
return gm.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成
|
||||
func (this *StringIntMap) GetOrSetFunc(key string, f func() int) int {
|
||||
this.mu.RLock()
|
||||
v, ok := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *StringIntMap) GetOrSetFunc(key string, f func() int) int {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return this.doSetWithLockCheck(key, f())
|
||||
return gm.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// 与GetOrSetFunc不同的是,f是在写锁机制内执行
|
||||
func (this *StringIntMap) GetOrSetFuncLock(key string, f func() int) int {
|
||||
this.mu.RLock()
|
||||
val, ok := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *StringIntMap) GetOrSetFuncLock(key string, f func() int) int {
|
||||
gm.mu.RLock()
|
||||
val, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
this.mu.Lock()
|
||||
defer this.mu.Unlock()
|
||||
if v, ok := this.m[key]; ok {
|
||||
this.mu.Unlock()
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
gm.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
val = f()
|
||||
this.m[key] = val
|
||||
gm.m[key] = val
|
||||
return val
|
||||
} else {
|
||||
return val
|
||||
@ -125,97 +156,121 @@ func (this *StringIntMap) GetOrSetFuncLock(key string, f func() int) int {
|
||||
}
|
||||
|
||||
// 当键名不存在时写入,并返回true;否则返回false。
|
||||
func (this *StringIntMap) SetIfNotExist(key string, value int) bool {
|
||||
if !this.Contains(key) {
|
||||
this.doSetWithLockCheck(key, value)
|
||||
func (gm *StringIntMap) SetIfNotExist(key string, value int) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 批量删除键值对
|
||||
func (this *StringIntMap) BatchRemove(keys []string) {
|
||||
this.mu.Lock()
|
||||
func (gm *StringIntMap) BatchRemove(keys []string) {
|
||||
gm.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(this.m, key)
|
||||
delete(gm.m, key)
|
||||
}
|
||||
this.mu.Unlock()
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 返回对应的键值,并删除该键值
|
||||
func (this *StringIntMap) Remove(key string) int {
|
||||
this.mu.Lock()
|
||||
val, exists := this.m[key]
|
||||
func (gm *StringIntMap) Remove(key string) int {
|
||||
gm.mu.Lock()
|
||||
val, exists := gm.m[key]
|
||||
if exists {
|
||||
delete(this.m, key)
|
||||
delete(gm.m, key)
|
||||
}
|
||||
this.mu.Unlock()
|
||||
gm.mu.Unlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// 返回键列表
|
||||
func (this *StringIntMap) Keys() []string {
|
||||
this.mu.RLock()
|
||||
func (gm *StringIntMap) Keys() []string {
|
||||
gm.mu.RLock()
|
||||
keys := make([]string, 0)
|
||||
for key, _ := range this.m {
|
||||
for key, _ := range gm.m {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
this.mu.RUnlock()
|
||||
gm.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// 返回值列表(注意是随机排序)
|
||||
func (this *StringIntMap) Values() []int {
|
||||
this.mu.RLock()
|
||||
func (gm *StringIntMap) Values() []int {
|
||||
gm.mu.RLock()
|
||||
vals := make([]int, 0)
|
||||
for _, val := range this.m {
|
||||
for _, val := range gm.m {
|
||||
vals = append(vals, val)
|
||||
}
|
||||
this.mu.RUnlock()
|
||||
gm.mu.RUnlock()
|
||||
return vals
|
||||
}
|
||||
|
||||
// 是否存在某个键
|
||||
func (this *StringIntMap) Contains(key string) bool {
|
||||
this.mu.RLock()
|
||||
_, exists := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *StringIntMap) Contains(key string) bool {
|
||||
gm.mu.RLock()
|
||||
_, exists := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// 哈希表大小
|
||||
func (this *StringIntMap) Size() int {
|
||||
this.mu.RLock()
|
||||
length := len(this.m)
|
||||
this.mu.RUnlock()
|
||||
func (gm *StringIntMap) Size() int {
|
||||
gm.mu.RLock()
|
||||
length := len(gm.m)
|
||||
gm.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// 哈希表是否为空
|
||||
func (this *StringIntMap) IsEmpty() bool {
|
||||
this.mu.RLock()
|
||||
empty := len(this.m) == 0
|
||||
this.mu.RUnlock()
|
||||
func (gm *StringIntMap) IsEmpty() bool {
|
||||
gm.mu.RLock()
|
||||
empty := len(gm.m) == 0
|
||||
gm.mu.RUnlock()
|
||||
return empty
|
||||
}
|
||||
|
||||
// 清空哈希表
|
||||
func (this *StringIntMap) Clear() {
|
||||
this.mu.Lock()
|
||||
this.m = make(map[string]int)
|
||||
this.mu.Unlock()
|
||||
func (gm *StringIntMap) Clear() {
|
||||
gm.mu.Lock()
|
||||
gm.m = make(map[string]int)
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 并发安全写锁操作,使用自定义方法执行加锁修改操作
|
||||
func (this *StringIntMap) LockFunc(f func(m map[string]int)) {
|
||||
this.mu.Lock(true)
|
||||
defer this.mu.Unlock(true)
|
||||
f(this.m)
|
||||
func (gm *StringIntMap) LockFunc(f func(m map[string]int)) {
|
||||
gm.mu.Lock(true)
|
||||
defer gm.mu.Unlock(true)
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 并发安全读锁操作,使用自定义方法执行加锁读取操作
|
||||
func (this *StringIntMap) RLockFunc(f func(m map[string]int)) {
|
||||
this.mu.RLock(true)
|
||||
defer this.mu.RUnlock(true)
|
||||
f(this.m)
|
||||
func (gm *StringIntMap) RLockFunc(f func(m map[string]int)) {
|
||||
gm.mu.RLock(true)
|
||||
defer gm.mu.RUnlock(true)
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 交换Map中的键和值.
|
||||
func (gm *StringIntMap) Flip() {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
n := make(map[string]int, len(gm.m))
|
||||
for k, v := range gm.m {
|
||||
n[gconv.String(v)] = gconv.Int(k)
|
||||
}
|
||||
gm.m = n
|
||||
}
|
||||
|
||||
// 合并两个Map.
|
||||
func (gm *StringIntMap) Merge(m *StringIntMap) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if m != gm {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
}
|
||||
for k, v := range m.m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
}
|
||||
@ -1,14 +1,15 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// If a copy of the MIT was not distributed with gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
package gmap
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
type StringInterfaceMap struct {
|
||||
@ -23,186 +24,238 @@ func NewStringInterfaceMap(unsafe...bool) *StringInterfaceMap {
|
||||
}
|
||||
}
|
||||
|
||||
func NewStringInterfaceMapFrom(m map[string]interface{}, unsafe...bool) *StringInterfaceMap {
|
||||
return &StringInterfaceMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
func NewStringInterfaceMapFromArray(keys []string, values []interface{}, unsafe...bool) *StringInterfaceMap {
|
||||
m := make(map[string]interface{})
|
||||
l := len(values)
|
||||
for i, k := range keys {
|
||||
if i < l {
|
||||
m[k] = values[i]
|
||||
} else {
|
||||
m[k] = interface{}(nil)
|
||||
}
|
||||
}
|
||||
return &StringInterfaceMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历
|
||||
func (this *StringInterfaceMap) Iterator(f func (k string, v interface{}) bool) {
|
||||
this.mu.RLock()
|
||||
defer this.mu.RUnlock()
|
||||
for k, v := range this.m {
|
||||
func (gm *StringInterfaceMap) Iterator(f func (k string, v interface{}) bool) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
for k, v := range gm.m {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 哈希表克隆
|
||||
func (this *StringInterfaceMap) Clone() map[string]interface{} {
|
||||
// 哈希表克隆.
|
||||
func (gm *StringInterfaceMap) Clone() *StringInterfaceMap {
|
||||
return NewStringInterfaceMapFrom(gm.Map(), !gm.mu.IsSafe())
|
||||
}
|
||||
|
||||
// 返回当前哈希表的数据Map.
|
||||
func (gm *StringInterfaceMap) Map() map[string]interface{} {
|
||||
m := make(map[string]interface{})
|
||||
this.mu.RLock()
|
||||
for k, v := range this.m {
|
||||
gm.mu.RLock()
|
||||
for k, v := range gm.m {
|
||||
m[k] = v
|
||||
}
|
||||
this.mu.RUnlock()
|
||||
gm.mu.RUnlock()
|
||||
return m
|
||||
}
|
||||
|
||||
// 设置键值对
|
||||
func (this *StringInterfaceMap) Set(key string, val interface{}) {
|
||||
this.mu.Lock()
|
||||
this.m[key] = val
|
||||
this.mu.Unlock()
|
||||
func (gm *StringInterfaceMap) Set(key string, val interface{}) {
|
||||
gm.mu.Lock()
|
||||
gm.m[key] = val
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 批量设置键值对
|
||||
func (this *StringInterfaceMap) BatchSet(m map[string]interface{}) {
|
||||
this.mu.Lock()
|
||||
func (gm *StringInterfaceMap) BatchSet(m map[string]interface{}) {
|
||||
gm.mu.Lock()
|
||||
for k, v := range m {
|
||||
this.m[k] = v
|
||||
gm.m[k] = v
|
||||
}
|
||||
this.mu.Unlock()
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 获取键值
|
||||
func (this *StringInterfaceMap) Get(key string) interface{} {
|
||||
this.mu.RLock()
|
||||
val, _ := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *StringInterfaceMap) Get(key string) interface{} {
|
||||
gm.mu.RLock()
|
||||
val, _ := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// 设置kv缓存键值对,内部会对键名的存在性使用写锁进行二次检索确认,如果存在则不再写入;返回键名对应的键值。
|
||||
// 在高并发下有用,防止数据写入的并发逻辑错误。
|
||||
func (this *StringInterfaceMap) doSetWithLockCheck(key string, value interface{}) interface{} {
|
||||
this.mu.Lock()
|
||||
defer this.mu.Unlock()
|
||||
if v, ok := this.m[key]; ok {
|
||||
func (gm *StringInterfaceMap) doSetWithLockCheck(key string, value interface{}) interface{} {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
return v
|
||||
}
|
||||
if f, ok := value.(func() interface {}); ok {
|
||||
value = f()
|
||||
}
|
||||
this.m[key] = value
|
||||
gm.m[key] = value
|
||||
return value
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值
|
||||
func (this *StringInterfaceMap) GetOrSet(key string, value interface{}) interface{} {
|
||||
if v := this.Get(key); v == nil {
|
||||
return this.doSetWithLockCheck(key, value)
|
||||
func (gm *StringInterfaceMap) GetOrSet(key string, value interface{}) interface{} {
|
||||
if v := gm.Get(key); v == nil {
|
||||
return gm.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成
|
||||
func (this *StringInterfaceMap) GetOrSetFunc(key string, f func() interface{}) interface{} {
|
||||
if v := this.Get(key); v == nil {
|
||||
return this.doSetWithLockCheck(key, f())
|
||||
func (gm *StringInterfaceMap) GetOrSetFunc(key string, f func() interface{}) interface{} {
|
||||
if v := gm.Get(key); v == nil {
|
||||
return gm.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// 与GetOrSetFunc不同的是,f是在写锁机制内执行
|
||||
func (this *StringInterfaceMap) GetOrSetFuncLock(key string, f func() interface{}) interface{} {
|
||||
if v := this.Get(key); v == nil {
|
||||
return this.doSetWithLockCheck(key, f)
|
||||
func (gm *StringInterfaceMap) GetOrSetFuncLock(key string, f func() interface{}) interface{} {
|
||||
if v := gm.Get(key); v == nil {
|
||||
return gm.doSetWithLockCheck(key, f)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// 当键名不存在时写入,并返回true;否则返回false。
|
||||
func (this *StringInterfaceMap) SetIfNotExist(key string, value interface{}) bool {
|
||||
if !this.Contains(key) {
|
||||
this.doSetWithLockCheck(key, value)
|
||||
func (gm *StringInterfaceMap) SetIfNotExist(key string, value interface{}) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 批量删除键值对
|
||||
func (this *StringInterfaceMap) BatchRemove(keys []string) {
|
||||
this.mu.Lock()
|
||||
func (gm *StringInterfaceMap) BatchRemove(keys []string) {
|
||||
gm.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(this.m, key)
|
||||
delete(gm.m, key)
|
||||
}
|
||||
this.mu.Unlock()
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 返回对应的键值,并删除该键值
|
||||
func (this *StringInterfaceMap) Remove(key string) interface{} {
|
||||
this.mu.Lock()
|
||||
val, exists := this.m[key]
|
||||
func (gm *StringInterfaceMap) Remove(key string) interface{} {
|
||||
gm.mu.Lock()
|
||||
val, exists := gm.m[key]
|
||||
if exists {
|
||||
delete(this.m, key)
|
||||
delete(gm.m, key)
|
||||
}
|
||||
this.mu.Unlock()
|
||||
gm.mu.Unlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// 返回键列表
|
||||
func (this *StringInterfaceMap) Keys() []string {
|
||||
this.mu.RLock()
|
||||
func (gm *StringInterfaceMap) Keys() []string {
|
||||
gm.mu.RLock()
|
||||
keys := make([]string, 0)
|
||||
for key, _ := range this.m {
|
||||
for key, _ := range gm.m {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
this.mu.RUnlock()
|
||||
gm.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// 返回值列表(注意是随机排序)
|
||||
func (this *StringInterfaceMap) Values() []interface{} {
|
||||
this.mu.RLock()
|
||||
func (gm *StringInterfaceMap) Values() []interface{} {
|
||||
gm.mu.RLock()
|
||||
vals := make([]interface{}, 0)
|
||||
for _, val := range this.m {
|
||||
for _, val := range gm.m {
|
||||
vals = append(vals, val)
|
||||
}
|
||||
this.mu.RUnlock()
|
||||
gm.mu.RUnlock()
|
||||
return vals
|
||||
}
|
||||
|
||||
// 是否存在某个键
|
||||
func (this *StringInterfaceMap) Contains(key string) bool {
|
||||
this.mu.RLock()
|
||||
_, exists := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *StringInterfaceMap) Contains(key string) bool {
|
||||
gm.mu.RLock()
|
||||
_, exists := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// 哈希表大小
|
||||
func (this *StringInterfaceMap) Size() int {
|
||||
this.mu.RLock()
|
||||
length := len(this.m)
|
||||
this.mu.RUnlock()
|
||||
func (gm *StringInterfaceMap) Size() int {
|
||||
gm.mu.RLock()
|
||||
length := len(gm.m)
|
||||
gm.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// 哈希表是否为空
|
||||
func (this *StringInterfaceMap) IsEmpty() bool {
|
||||
this.mu.RLock()
|
||||
empty := len(this.m) == 0
|
||||
this.mu.RUnlock()
|
||||
func (gm *StringInterfaceMap) IsEmpty() bool {
|
||||
gm.mu.RLock()
|
||||
empty := len(gm.m) == 0
|
||||
gm.mu.RUnlock()
|
||||
return empty
|
||||
}
|
||||
|
||||
// 清空哈希表
|
||||
func (this *StringInterfaceMap) Clear() {
|
||||
this.mu.Lock()
|
||||
this.m = make(map[string]interface{})
|
||||
this.mu.Unlock()
|
||||
func (gm *StringInterfaceMap) Clear() {
|
||||
gm.mu.Lock()
|
||||
gm.m = make(map[string]interface{})
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 并发安全写锁操作,使用自定义方法执行加锁修改操作
|
||||
func (this *StringInterfaceMap) LockFunc(f func(m map[string]interface{})) {
|
||||
this.mu.Lock(true)
|
||||
defer this.mu.Unlock(true)
|
||||
f(this.m)
|
||||
func (gm *StringInterfaceMap) LockFunc(f func(m map[string]interface{})) {
|
||||
gm.mu.Lock(true)
|
||||
defer gm.mu.Unlock(true)
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 并发安全读锁操作,使用自定义方法执行加锁读取操作
|
||||
func (this *StringInterfaceMap) RLockFunc(f func(m map[string]interface{})) {
|
||||
this.mu.RLock(true)
|
||||
defer this.mu.RUnlock(true)
|
||||
f(this.m)
|
||||
func (gm *StringInterfaceMap) RLockFunc(f func(m map[string]interface{})) {
|
||||
gm.mu.RLock(true)
|
||||
defer gm.mu.RUnlock(true)
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 交换Map中的键和值.
|
||||
func (gm *StringInterfaceMap) Flip() {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
n := make(map[string]interface{}, len(gm.m))
|
||||
for k, v := range gm.m {
|
||||
n[gconv.String(v)] = k
|
||||
}
|
||||
gm.m = n
|
||||
}
|
||||
|
||||
// 合并两个Map.
|
||||
func (gm *StringInterfaceMap) Merge(m *StringInterfaceMap) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if m != gm {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
}
|
||||
for k, v := range m.m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
}
|
||||
@ -1,13 +1,15 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// If a copy of the MIT was not distributed with gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
package gmap
|
||||
|
||||
import "gitee.com/johng/gf/g/internal/rwmutex"
|
||||
import (
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
)
|
||||
|
||||
type StringStringMap struct {
|
||||
mu *rwmutex.RWMutex
|
||||
@ -21,103 +23,131 @@ func NewStringStringMap(unsafe...bool) *StringStringMap {
|
||||
}
|
||||
}
|
||||
|
||||
func NewStringStringMapFrom(m map[string]string, unsafe...bool) *StringStringMap {
|
||||
return &StringStringMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
func NewStringStringMapFromArray(keys []string, values []string, unsafe...bool) *StringStringMap {
|
||||
m := make(map[string]string)
|
||||
l := len(values)
|
||||
for i, k := range keys {
|
||||
if i < l {
|
||||
m[k] = values[i]
|
||||
} else {
|
||||
m[k] = ""
|
||||
}
|
||||
}
|
||||
return &StringStringMap{
|
||||
m : m,
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历
|
||||
func (this *StringStringMap) Iterator(f func (k string, v string) bool) {
|
||||
this.mu.RLock()
|
||||
defer this.mu.RUnlock()
|
||||
for k, v := range this.m {
|
||||
func (gm *StringStringMap) Iterator(f func (k string, v string) bool) {
|
||||
gm.mu.RLock()
|
||||
defer gm.mu.RUnlock()
|
||||
for k, v := range gm.m {
|
||||
if !f(k, v) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 哈希表克隆
|
||||
func (this *StringStringMap) Clone() map[string]string {
|
||||
// 哈希表克隆.
|
||||
func (gm *StringStringMap) Clone() *StringStringMap {
|
||||
return NewStringStringMapFrom(gm.Map(), !gm.mu.IsSafe())
|
||||
}
|
||||
|
||||
// 返回当前哈希表的数据Map.
|
||||
func (gm *StringStringMap) Map() map[string]string {
|
||||
m := make(map[string]string)
|
||||
this.mu.RLock()
|
||||
for k, v := range this.m {
|
||||
gm.mu.RLock()
|
||||
for k, v := range gm.m {
|
||||
m[k] = v
|
||||
}
|
||||
this.mu.RUnlock()
|
||||
gm.mu.RUnlock()
|
||||
return m
|
||||
}
|
||||
|
||||
// 设置键值对
|
||||
func (this *StringStringMap) Set(key string, val string) {
|
||||
this.mu.Lock()
|
||||
this.m[key] = val
|
||||
this.mu.Unlock()
|
||||
func (gm *StringStringMap) Set(key string, val string) {
|
||||
gm.mu.Lock()
|
||||
gm.m[key] = val
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 批量设置键值对
|
||||
func (this *StringStringMap) BatchSet(m map[string]string) {
|
||||
this.mu.Lock()
|
||||
func (gm *StringStringMap) BatchSet(m map[string]string) {
|
||||
gm.mu.Lock()
|
||||
for k, v := range m {
|
||||
this.m[k] = v
|
||||
gm.m[k] = v
|
||||
}
|
||||
this.mu.Unlock()
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 获取键值
|
||||
func (this *StringStringMap) Get(key string) string {
|
||||
this.mu.RLock()
|
||||
val, _ := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *StringStringMap) Get(key string) string {
|
||||
gm.mu.RLock()
|
||||
val, _ := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// 设置kv缓存键值对,内部会对键名的存在性使用写锁进行二次检索确认,如果存在则不再写入;返回键名对应的键值。
|
||||
// 在高并发下有用,防止数据写入的并发逻辑错误。
|
||||
func (this *StringStringMap) doSetWithLockCheck(key string, value string) string {
|
||||
this.mu.Lock()
|
||||
if v, ok := this.m[key]; ok {
|
||||
this.mu.Unlock()
|
||||
func (gm *StringStringMap) doSetWithLockCheck(key string, value string) string {
|
||||
gm.mu.Lock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
gm.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
this.m[key] = value
|
||||
this.mu.Unlock()
|
||||
gm.m[key] = value
|
||||
gm.mu.Unlock()
|
||||
return value
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值
|
||||
func (this *StringStringMap) GetOrSet(key string, value string) string {
|
||||
this.mu.RLock()
|
||||
v, ok := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *StringStringMap) GetOrSet(key string, value string) string {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return this.doSetWithLockCheck(key, value)
|
||||
return gm.doSetWithLockCheck(key, value)
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// 当键名存在时返回其键值,否则写入指定的键值,键值由指定的函数生成
|
||||
func (this *StringStringMap) GetOrSetFunc(key string, f func() string) string {
|
||||
this.mu.RLock()
|
||||
v, ok := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *StringStringMap) GetOrSetFunc(key string, f func() string) string {
|
||||
gm.mu.RLock()
|
||||
v, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
return this.doSetWithLockCheck(key, f())
|
||||
return gm.doSetWithLockCheck(key, f())
|
||||
} else {
|
||||
return v
|
||||
}
|
||||
}
|
||||
|
||||
// 与GetOrSetFunc不同的是,f是在写锁机制内执行
|
||||
func (this *StringStringMap) GetOrSetFuncLock(key string, f func() string) string {
|
||||
this.mu.RLock()
|
||||
val, ok := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *StringStringMap) GetOrSetFuncLock(key string, f func() string) string {
|
||||
gm.mu.RLock()
|
||||
val, ok := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
if !ok {
|
||||
this.mu.Lock()
|
||||
defer this.mu.Unlock()
|
||||
if v, ok := this.m[key]; ok {
|
||||
this.mu.Unlock()
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if v, ok := gm.m[key]; ok {
|
||||
gm.mu.Unlock()
|
||||
return v
|
||||
}
|
||||
val = f()
|
||||
this.m[key] = val
|
||||
gm.m[key] = val
|
||||
return val
|
||||
} else {
|
||||
return val
|
||||
@ -125,97 +155,121 @@ func (this *StringStringMap) GetOrSetFuncLock(key string, f func() string) strin
|
||||
}
|
||||
|
||||
// 当键名不存在时写入,并返回true;否则返回false。
|
||||
func (this *StringStringMap) SetIfNotExist(key string, value string) bool {
|
||||
if !this.Contains(key) {
|
||||
this.doSetWithLockCheck(key, value)
|
||||
func (gm *StringStringMap) SetIfNotExist(key string, value string) bool {
|
||||
if !gm.Contains(key) {
|
||||
gm.doSetWithLockCheck(key, value)
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// 批量删除键值对
|
||||
func (this *StringStringMap) BatchRemove(keys []string) {
|
||||
this.mu.Lock()
|
||||
func (gm *StringStringMap) BatchRemove(keys []string) {
|
||||
gm.mu.Lock()
|
||||
for _, key := range keys {
|
||||
delete(this.m, key)
|
||||
delete(gm.m, key)
|
||||
}
|
||||
this.mu.Unlock()
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 返回对应的键值,并删除该键值
|
||||
func (this *StringStringMap) Remove(key string) string {
|
||||
this.mu.Lock()
|
||||
val, exists := this.m[key]
|
||||
func (gm *StringStringMap) Remove(key string) string {
|
||||
gm.mu.Lock()
|
||||
val, exists := gm.m[key]
|
||||
if exists {
|
||||
delete(this.m, key)
|
||||
delete(gm.m, key)
|
||||
}
|
||||
this.mu.Unlock()
|
||||
gm.mu.Unlock()
|
||||
return val
|
||||
}
|
||||
|
||||
// 返回键列表
|
||||
func (this *StringStringMap) Keys() []string {
|
||||
this.mu.RLock()
|
||||
func (gm *StringStringMap) Keys() []string {
|
||||
gm.mu.RLock()
|
||||
keys := make([]string, 0)
|
||||
for key, _ := range this.m {
|
||||
for key, _ := range gm.m {
|
||||
keys = append(keys, key)
|
||||
}
|
||||
this.mu.RUnlock()
|
||||
gm.mu.RUnlock()
|
||||
return keys
|
||||
}
|
||||
|
||||
// 返回值列表(注意是随机排序)
|
||||
func (this *StringStringMap) Values() []string {
|
||||
this.mu.RLock()
|
||||
func (gm *StringStringMap) Values() []string {
|
||||
gm.mu.RLock()
|
||||
vals := make([]string, 0)
|
||||
for _, val := range this.m {
|
||||
for _, val := range gm.m {
|
||||
vals = append(vals, val)
|
||||
}
|
||||
this.mu.RUnlock()
|
||||
gm.mu.RUnlock()
|
||||
return vals
|
||||
}
|
||||
|
||||
// 是否存在某个键
|
||||
func (this *StringStringMap) Contains(key string) bool {
|
||||
this.mu.RLock()
|
||||
_, exists := this.m[key]
|
||||
this.mu.RUnlock()
|
||||
func (gm *StringStringMap) Contains(key string) bool {
|
||||
gm.mu.RLock()
|
||||
_, exists := gm.m[key]
|
||||
gm.mu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// 哈希表大小
|
||||
func (this *StringStringMap) Size() int {
|
||||
this.mu.RLock()
|
||||
length := len(this.m)
|
||||
this.mu.RUnlock()
|
||||
func (gm *StringStringMap) Size() int {
|
||||
gm.mu.RLock()
|
||||
length := len(gm.m)
|
||||
gm.mu.RUnlock()
|
||||
return length
|
||||
}
|
||||
|
||||
// 哈希表是否为空
|
||||
func (this *StringStringMap) IsEmpty() bool {
|
||||
this.mu.RLock()
|
||||
empty := len(this.m) == 0
|
||||
this.mu.RUnlock()
|
||||
func (gm *StringStringMap) IsEmpty() bool {
|
||||
gm.mu.RLock()
|
||||
empty := len(gm.m) == 0
|
||||
gm.mu.RUnlock()
|
||||
return empty
|
||||
}
|
||||
|
||||
// 清空哈希表
|
||||
func (this *StringStringMap) Clear() {
|
||||
this.mu.Lock()
|
||||
this.m = make(map[string]string)
|
||||
this.mu.Unlock()
|
||||
func (gm *StringStringMap) Clear() {
|
||||
gm.mu.Lock()
|
||||
gm.m = make(map[string]string)
|
||||
gm.mu.Unlock()
|
||||
}
|
||||
|
||||
// 并发安全写锁操作,使用自定义方法执行加锁修改操作
|
||||
func (this *StringStringMap) LockFunc(f func(m map[string]string)) {
|
||||
this.mu.Lock(true)
|
||||
defer this.mu.Unlock(true)
|
||||
f(this.m)
|
||||
func (gm *StringStringMap) LockFunc(f func(m map[string]string)) {
|
||||
gm.mu.Lock(true)
|
||||
defer gm.mu.Unlock(true)
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 并发安全读锁操作,使用自定义方法执行加锁读取操作
|
||||
func (this *StringStringMap) RLockFunc(f func(m map[string]string)) {
|
||||
this.mu.RLock(true)
|
||||
defer this.mu.RUnlock(true)
|
||||
f(this.m)
|
||||
func (gm *StringStringMap) RLockFunc(f func(m map[string]string)) {
|
||||
gm.mu.RLock(true)
|
||||
defer gm.mu.RUnlock(true)
|
||||
f(gm.m)
|
||||
}
|
||||
|
||||
// 交换Map中的键和值.
|
||||
func (gm *StringStringMap) Flip() {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
n := make(map[string]string, len(gm.m))
|
||||
for k, v := range gm.m {
|
||||
n[v] = k
|
||||
}
|
||||
gm.m = n
|
||||
}
|
||||
|
||||
// 合并两个Map.
|
||||
func (gm *StringStringMap) Merge(m *StringStringMap) {
|
||||
gm.mu.Lock()
|
||||
defer gm.mu.Unlock()
|
||||
if m != gm {
|
||||
m.mu.RLock()
|
||||
defer m.mu.RUnlock()
|
||||
}
|
||||
for k, v := range m.m {
|
||||
gm.m[k] = v
|
||||
}
|
||||
}
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// If a copy of the MIT was not distributed with gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// go test *.go -bench=".*" -benchmem
|
||||
|
||||
@ -18,7 +18,7 @@ var ibm = NewIntBoolMap()
|
||||
var iim = NewIntIntMap()
|
||||
var iifm = NewIntInterfaceMap()
|
||||
var ism = NewIntStringMap()
|
||||
var ififm = NewInterfaceInterfaceMap()
|
||||
var ififm = NewMap()
|
||||
var sbm = NewStringBoolMap()
|
||||
var sim = NewStringIntMap()
|
||||
var sifm = NewStringInterfaceMap()
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// If a copy of the MIT was not distributed with gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// go test *.go -bench=".*" -benchmem
|
||||
|
||||
@ -10,7 +10,7 @@ package gmap_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"gitee.com/johng/gf/g/container/gmap"
|
||||
"github.com/gogf/gf/g/container/gmap"
|
||||
"sync"
|
||||
)
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// If a copy of the MIT was not distributed with gm file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// go test *.go -bench=".*" -benchmem
|
||||
|
||||
@ -14,15 +14,15 @@ import (
|
||||
)
|
||||
|
||||
|
||||
var ibmUnsafe = NewIntBoolMap(false)
|
||||
var iimUnsafe = NewIntIntMap(false)
|
||||
var iifmUnsafe = NewIntInterfaceMap(false)
|
||||
var ismUnsafe = NewIntStringMap(false)
|
||||
var ififmUnsafe = NewInterfaceInterfaceMap(false)
|
||||
var sbmUnsafe = NewStringBoolMap(false)
|
||||
var simUnsafe = NewStringIntMap(false)
|
||||
var sifmUnsafe = NewStringInterfaceMap(false)
|
||||
var ssmUnsafe = NewStringStringMap(false)
|
||||
var ibmUnsafe = NewIntBoolMap(true)
|
||||
var iimUnsafe = NewIntIntMap(true)
|
||||
var iifmUnsafe = NewIntInterfaceMap(true)
|
||||
var ismUnsafe = NewIntStringMap(true)
|
||||
var ififmUnsafe = NewMap(true)
|
||||
var sbmUnsafe = NewStringBoolMap(true)
|
||||
var simUnsafe = NewStringIntMap(true)
|
||||
var sifmUnsafe = NewStringInterfaceMap(true)
|
||||
var ssmUnsafe = NewStringStringMap(true)
|
||||
|
||||
// 写入性能测试
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gpool provides a object-reusable concurrent-safe pool.
|
||||
//
|
||||
@ -11,10 +11,10 @@ package gpool
|
||||
|
||||
import (
|
||||
"errors"
|
||||
"gitee.com/johng/gf/g/container/glist"
|
||||
"gitee.com/johng/gf/g/container/gtype"
|
||||
"gitee.com/johng/gf/g/os/gtime"
|
||||
"gitee.com/johng/gf/g/os/gtimer"
|
||||
"github.com/gogf/gf/g/container/glist"
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"github.com/gogf/gf/g/os/gtimer"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// go test *.go -bench=".*"
|
||||
|
||||
|
||||
@ -1,12 +1,12 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gqueue provides a dynamic/static concurrent-safe(alternative) queue.
|
||||
//
|
||||
// 并发安全的动态队列.
|
||||
// 并发安全动态队列.
|
||||
//
|
||||
// 特点:
|
||||
// 1. 动态队列初始化速度快;
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// go test *.go -bench=".*" -benchmem
|
||||
|
||||
@ -10,7 +10,7 @@ package gqueue_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"gitee.com/johng/gf/g/container/gqueue"
|
||||
"github.com/gogf/gf/g/container/gqueue"
|
||||
)
|
||||
|
||||
var bn = 20000000
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gring provides a concurrent-safe(alternative) ring(circular lists).
|
||||
//
|
||||
@ -11,8 +11,8 @@ package gring
|
||||
|
||||
import (
|
||||
"container/ring"
|
||||
"gitee.com/johng/gf/g/container/gtype"
|
||||
"gitee.com/johng/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
)
|
||||
|
||||
type Ring struct {
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// go test *.go -bench=".*"
|
||||
|
||||
|
||||
@ -1,17 +1,293 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gset provides kinds of concurrent-safe(alternative) sets.
|
||||
//
|
||||
// 并发安全集合.
|
||||
package gset
|
||||
|
||||
type Set = InterfaceSet
|
||||
import (
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 默认Set类型
|
||||
type Set struct {
|
||||
mu *rwmutex.RWMutex
|
||||
m map[interface{}]struct{}
|
||||
}
|
||||
|
||||
// Create a set, which contains un-repeated items.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 创建一个空的集合对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
func New(unsafe...bool) *Set {
|
||||
return NewInterfaceSet(unsafe...)
|
||||
return NewSet(unsafe...)
|
||||
}
|
||||
|
||||
// See New.
|
||||
//
|
||||
// 同New.
|
||||
func NewSet(unsafe...bool) *Set {
|
||||
return &Set{
|
||||
m : make(map[interface{}]struct{}),
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// Iterate the set by given callback <f>,
|
||||
// if <f> returns true then continue iterating; or false to stop.
|
||||
//
|
||||
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历。
|
||||
func (set *Set) Iterator(f func (v interface{}) bool) *Set {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for k, _ := range set.m {
|
||||
if !f(k) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// Add one or multiple items to the set.
|
||||
//
|
||||
// 添加元素项到集合中(支持多个).
|
||||
func (set *Set) Add(item...interface{}) *Set {
|
||||
set.mu.Lock()
|
||||
for _, v := range item {
|
||||
set.m[v] = struct{}{}
|
||||
}
|
||||
set.mu.Unlock()
|
||||
return set
|
||||
}
|
||||
|
||||
// Check whether the set contains <item>.
|
||||
//
|
||||
// 键是否存在.
|
||||
func (set *Set) Contains(item interface{}) bool {
|
||||
set.mu.RLock()
|
||||
_, exists := set.m[item]
|
||||
set.mu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// Remove <item> from set.
|
||||
//
|
||||
// 删除元素项。
|
||||
func (set *Set) Remove(item interface{}) *Set {
|
||||
set.mu.Lock()
|
||||
delete(set.m, item)
|
||||
set.mu.Unlock()
|
||||
return set
|
||||
}
|
||||
|
||||
// Get size of the set.
|
||||
//
|
||||
// 获得集合大小。
|
||||
func (set *Set) Size() int {
|
||||
set.mu.RLock()
|
||||
l := len(set.m)
|
||||
set.mu.RUnlock()
|
||||
return l
|
||||
}
|
||||
|
||||
// Clear the set.
|
||||
//
|
||||
// 清空集合。
|
||||
func (set *Set) Clear() *Set {
|
||||
set.mu.Lock()
|
||||
set.m = make(map[interface{}]struct{})
|
||||
set.mu.Unlock()
|
||||
return set
|
||||
}
|
||||
|
||||
// Get the copy of items from set as slice.
|
||||
//
|
||||
// 获得集合元素项列表.
|
||||
func (set *Set) Slice() []interface{} {
|
||||
set.mu.RLock()
|
||||
i := 0
|
||||
ret := make([]interface{}, len(set.m))
|
||||
for item := range set.m {
|
||||
ret[i] = item
|
||||
i++
|
||||
}
|
||||
set.mu.RUnlock()
|
||||
return ret
|
||||
}
|
||||
|
||||
// Join set items with a string.
|
||||
//
|
||||
// 使用glue字符串串连当前集合的元素项,构造成新的字符串返回。
|
||||
func (set *Set) Join(glue string) string {
|
||||
return strings.Join(gconv.Strings(set.Slice()), ",")
|
||||
}
|
||||
|
||||
// Return set items as a string, which are joined by char ','.
|
||||
//
|
||||
// 使用glue字符串串连当前集合的元素项,构造成新的字符串返回。
|
||||
func (set *Set) String() string {
|
||||
return set.Join(",")
|
||||
}
|
||||
|
||||
// Lock writing by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁修改操作。
|
||||
func (set *Set) LockFunc(f func(m map[interface{}]struct{})) *Set {
|
||||
set.mu.Lock(true)
|
||||
defer set.mu.Unlock(true)
|
||||
f(set.m)
|
||||
return set
|
||||
}
|
||||
|
||||
// Lock reading by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁读取操作。
|
||||
func (set *Set) RLockFunc(f func(m map[interface{}]struct{})) *Set {
|
||||
set.mu.RLock(true)
|
||||
defer set.mu.RUnlock(true)
|
||||
f(set.m)
|
||||
return set
|
||||
}
|
||||
|
||||
// Check whether the two sets equal.
|
||||
//
|
||||
// 判断两个集合是否相等.
|
||||
func (set *Set) Equal(other *Set) bool {
|
||||
if set == other {
|
||||
return true
|
||||
}
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
if len(set.m) != len(other.m) {
|
||||
return false
|
||||
}
|
||||
for key := range set.m {
|
||||
if _, ok := other.m[key]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Check whether the current set is sub-set of <other>.
|
||||
//
|
||||
// 判断当前集合是否为other集合的子集.
|
||||
func (set *Set) IsSubsetOf(other *Set) bool {
|
||||
if set == other {
|
||||
return true
|
||||
}
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
for key := range set.m {
|
||||
if _, ok := other.m[key]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Returns a new set which is the union of <set> and <other>.
|
||||
// Which means, all the items in <newSet> is in <set> or in <other>.
|
||||
//
|
||||
// 并集, 返回新的集合:属于set或属于others的元素为元素的集合.
|
||||
func (set *Set) Union(others ... *Set) (newSet *Set) {
|
||||
newSet = NewSet(true)
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for _, other := range others {
|
||||
if set != other {
|
||||
other.mu.RLock()
|
||||
}
|
||||
for k, v := range set.m {
|
||||
newSet.m[k] = v
|
||||
}
|
||||
if set != other {
|
||||
for k, v := range other.m {
|
||||
newSet.m[k] = v
|
||||
}
|
||||
}
|
||||
if set != other {
|
||||
other.mu.RUnlock()
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Returns a new set which is the difference set from <set> to <other>.
|
||||
// Which means, all the items in <newSet> is in <set> and not in <other>.
|
||||
//
|
||||
// 差集, 返回新的集合: 属于set且不属于others的元素为元素的集合.
|
||||
func (set *Set) Diff(others...*Set) (newSet *Set) {
|
||||
newSet = NewSet(true)
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for _, other := range others {
|
||||
if set == other {
|
||||
continue
|
||||
}
|
||||
other.mu.RLock()
|
||||
for k, v := range set.m {
|
||||
if _, ok := other.m[k]; !ok {
|
||||
newSet.m[k] = v
|
||||
}
|
||||
}
|
||||
other.mu.RUnlock()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Returns a new set which is the intersection from <set> to <other>.
|
||||
// Which means, all the items in <newSet> is in <set> and also in <other>.
|
||||
//
|
||||
// 交集, 返回新的集合: 属于set且属于others的元素为元素的集合.
|
||||
func (set *Set) Intersect(others...*Set) (newSet *Set) {
|
||||
newSet = NewSet(true)
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for _, other := range others {
|
||||
if set != other {
|
||||
other.mu.RLock()
|
||||
}
|
||||
for k, v := range set.m {
|
||||
if _, ok := other.m[k]; ok {
|
||||
newSet.m[k] = v
|
||||
}
|
||||
}
|
||||
if set != other {
|
||||
other.mu.RUnlock()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Returns a new set which is the complement from <set> to <full>.
|
||||
// Which means, all the items in <newSet> is in <full> and not in <set>.
|
||||
//
|
||||
// 补集, 返回新的集合: (前提: set应当为full的子集)属于全集full不属于集合set的元素组成的集合.
|
||||
// 如果给定的full集合不是set的全集时,返回full与set的差集.
|
||||
func (set *Set) Complement(full *Set) (newSet *Set) {
|
||||
newSet = NewSet(true)
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
if set != full {
|
||||
full.mu.RLock()
|
||||
defer full.mu.RUnlock()
|
||||
}
|
||||
for k, v := range full.m {
|
||||
if _, ok := set.m[k]; !ok {
|
||||
newSet.m[k] = v
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
@ -1,16 +1,16 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
//
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
package gset
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gitee.com/johng/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type IntSet struct {
|
||||
@ -18,6 +18,11 @@ type IntSet struct {
|
||||
m map[int]struct{}
|
||||
}
|
||||
|
||||
// Create a set, which contains un-repeated items.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 创建一个空的集合对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
func NewIntSet(unsafe...bool) *IntSet {
|
||||
return &IntSet{
|
||||
m : make(map[int]struct{}),
|
||||
@ -25,8 +30,11 @@ func NewIntSet(unsafe...bool) *IntSet {
|
||||
}
|
||||
}
|
||||
|
||||
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历
|
||||
func (set *IntSet) Iterator(f func (v int) bool) {
|
||||
// Iterate the set by given callback <f>,
|
||||
// if <f> returns true then continue iterating; or false to stop.
|
||||
//
|
||||
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历。
|
||||
func (set *IntSet) Iterator(f func (v int) bool) *IntSet {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for k, _ := range set.m {
|
||||
@ -34,27 +42,24 @@ func (set *IntSet) Iterator(f func (v int) bool) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// 设置键
|
||||
func (set *IntSet) Add(item int) *IntSet {
|
||||
// Add one or multiple items to the set.
|
||||
//
|
||||
// 添加元素项到集合中(支持多个).
|
||||
func (set *IntSet) Add(item...int) *IntSet {
|
||||
set.mu.Lock()
|
||||
set.m[item] = struct{}{}
|
||||
for _, v := range item {
|
||||
set.m[v] = struct{}{}
|
||||
}
|
||||
set.mu.Unlock()
|
||||
return set
|
||||
}
|
||||
|
||||
// 批量添加设置键
|
||||
func (set *IntSet) BatchAdd(items []int) *IntSet {
|
||||
set.mu.Lock()
|
||||
for _, item := range items {
|
||||
set.m[item] = struct{}{}
|
||||
}
|
||||
set.mu.Unlock()
|
||||
return set
|
||||
}
|
||||
|
||||
// 键是否存在
|
||||
// Check whether the set contains <item>.
|
||||
//
|
||||
// 键是否存在.
|
||||
func (set *IntSet) Contains(item int) bool {
|
||||
set.mu.RLock()
|
||||
_, exists := set.m[item]
|
||||
@ -62,14 +67,19 @@ func (set *IntSet) Contains(item int) bool {
|
||||
return exists
|
||||
}
|
||||
|
||||
// 删除键值对
|
||||
func (set *IntSet) Remove(key int) {
|
||||
// Remove <item> from set.
|
||||
//
|
||||
// 删除元素项。
|
||||
func (set *IntSet) Remove(item int) *IntSet {
|
||||
set.mu.Lock()
|
||||
delete(set.m, key)
|
||||
delete(set.m, item)
|
||||
set.mu.Unlock()
|
||||
return set
|
||||
}
|
||||
|
||||
// 大小
|
||||
// Get size of the set.
|
||||
//
|
||||
// 获得集合大小。
|
||||
func (set *IntSet) Size() int {
|
||||
set.mu.RLock()
|
||||
l := len(set.m)
|
||||
@ -77,40 +87,198 @@ func (set *IntSet) Size() int {
|
||||
return l
|
||||
}
|
||||
|
||||
// 清空set
|
||||
func (set *IntSet) Clear() {
|
||||
// Clear the set.
|
||||
//
|
||||
// 清空集合。
|
||||
func (set *IntSet) Clear() *IntSet {
|
||||
set.mu.Lock()
|
||||
set.m = make(map[int]struct{})
|
||||
set.mu.Unlock()
|
||||
return set
|
||||
}
|
||||
|
||||
// 转换为数组
|
||||
// Get the copy of items from set as slice.
|
||||
//
|
||||
// 获得集合元素项列表.
|
||||
func (set *IntSet) Slice() []int {
|
||||
set.mu.RLock()
|
||||
ret := make([]int, len(set.m))
|
||||
i := 0
|
||||
for item := range set.m {
|
||||
ret[i] = item
|
||||
for k, _ := range set.m {
|
||||
ret[i] = k
|
||||
i++
|
||||
}
|
||||
|
||||
set.mu.RUnlock()
|
||||
return ret
|
||||
}
|
||||
|
||||
// 转换为字符串
|
||||
func (set *IntSet) String() string {
|
||||
return fmt.Sprint(set.Slice())
|
||||
// Join set items with a string.
|
||||
//
|
||||
// 使用glue字符串串连当前集合的元素项,构造成新的字符串返回。
|
||||
func (set *IntSet) Join(glue string) string {
|
||||
return strings.Join(gconv.Strings(set.Slice()), ",")
|
||||
}
|
||||
|
||||
func (set *IntSet) LockFunc(f func(m map[int]struct{})) {
|
||||
// Return set items as a string, which are joined by char ','.
|
||||
//
|
||||
// 使用glue字符串串连当前集合的元素项,构造成新的字符串返回。
|
||||
func (set *IntSet) String() string {
|
||||
return set.Join(",")
|
||||
}
|
||||
|
||||
// Lock writing by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁修改操作。
|
||||
func (set *IntSet) LockFunc(f func(m map[int]struct{})) *IntSet {
|
||||
set.mu.Lock(true)
|
||||
defer set.mu.Unlock(true)
|
||||
f(set.m)
|
||||
return set
|
||||
}
|
||||
|
||||
func (set *IntSet) RLockFunc(f func(m map[int]struct{})) {
|
||||
// Lock reading by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁读取操作。
|
||||
func (set *IntSet) RLockFunc(f func(m map[int]struct{})) *IntSet {
|
||||
set.mu.RLock(true)
|
||||
defer set.mu.RUnlock(true)
|
||||
f(set.m)
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// Check whether the two sets equal.
|
||||
//
|
||||
// 判断两个集合是否相等.
|
||||
func (set *IntSet) Equal(other *IntSet) bool {
|
||||
if set == other {
|
||||
return true
|
||||
}
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
if len(set.m) != len(other.m) {
|
||||
return false
|
||||
}
|
||||
for key := range set.m {
|
||||
if _, ok := other.m[key]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Check whether the current set is sub-set of <other>.
|
||||
//
|
||||
// 判断当前集合是否为other集合的子集.
|
||||
func (set *IntSet) IsSubsetOf(other *IntSet) bool {
|
||||
if set == other {
|
||||
return true
|
||||
}
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
for key := range set.m {
|
||||
if _, ok := other.m[key]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Returns a new set which is the union of <set> and <other>.
|
||||
// Which means, all the items in <newSet> is in <set> or in <other>.
|
||||
//
|
||||
// 并集, 返回新的集合:属于set或属于others的元素为元素的集合.
|
||||
func (set *IntSet) Union(others ... *IntSet) (newSet *IntSet) {
|
||||
newSet = NewIntSet(true)
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for _, other := range others {
|
||||
if set != other {
|
||||
other.mu.RLock()
|
||||
}
|
||||
for k, v := range set.m {
|
||||
newSet.m[k] = v
|
||||
}
|
||||
if set != other {
|
||||
for k, v := range other.m {
|
||||
newSet.m[k] = v
|
||||
}
|
||||
}
|
||||
if set != other {
|
||||
other.mu.RUnlock()
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Returns a new set which is the difference set from <set> to <other>.
|
||||
// Which means, all the items in <newSet> is in <set> and not in <other>.
|
||||
//
|
||||
// 差集, 返回新的集合: 属于set且不属于others的元素为元素的集合.
|
||||
func (set *IntSet) Diff(others...*IntSet) (newSet *IntSet) {
|
||||
newSet = NewIntSet(true)
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for _, other := range others {
|
||||
if set == other {
|
||||
continue
|
||||
}
|
||||
other.mu.RLock()
|
||||
for k, v := range set.m {
|
||||
if _, ok := other.m[k]; !ok {
|
||||
newSet.m[k] = v
|
||||
}
|
||||
}
|
||||
other.mu.RUnlock()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Returns a new set which is the intersection from <set> to <other>.
|
||||
// Which means, all the items in <newSet> is in <set> and also in <other>.
|
||||
//
|
||||
// 交集, 返回新的集合: 属于set且属于others的元素为元素的集合.
|
||||
func (set *IntSet) Intersect(others...*IntSet) (newSet *IntSet) {
|
||||
newSet = NewIntSet(true)
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for _, other := range others {
|
||||
if set != other {
|
||||
other.mu.RLock()
|
||||
}
|
||||
for k, v := range set.m {
|
||||
if _, ok := other.m[k]; ok {
|
||||
newSet.m[k] = v
|
||||
}
|
||||
}
|
||||
if set != other {
|
||||
other.mu.RUnlock()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Returns a new set which is the complement from <set> to <full>.
|
||||
// Which means, all the items in <newSet> is in <full> and not in <set>.
|
||||
//
|
||||
// 补集, 返回新的集合: (前提: set应当为full的子集)属于全集full不属于集合set的元素组成的集合.
|
||||
// 如果给定的full集合不是set的全集时,返回full与set的差集.
|
||||
func (set *IntSet) Complement(full *IntSet) (newSet *IntSet) {
|
||||
newSet = NewIntSet(true)
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
if set != full {
|
||||
full.mu.RLock()
|
||||
defer full.mu.RUnlock()
|
||||
}
|
||||
for k, v := range full.m {
|
||||
if _, ok := set.m[k]; !ok {
|
||||
newSet.m[k] = v
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -1,114 +0,0 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf.
|
||||
//
|
||||
|
||||
package gset
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gitee.com/johng/gf/g/internal/rwmutex"
|
||||
)
|
||||
|
||||
type InterfaceSet struct {
|
||||
mu *rwmutex.RWMutex
|
||||
m map[interface{}]struct{}
|
||||
}
|
||||
|
||||
func NewInterfaceSet(unsafe...bool) *InterfaceSet {
|
||||
return &InterfaceSet{
|
||||
m : make(map[interface{}]struct{}),
|
||||
mu : rwmutex.New(unsafe...),
|
||||
}
|
||||
}
|
||||
|
||||
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历
|
||||
func (set *InterfaceSet) Iterator(f func (v interface{}) bool) {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for k, _ := range set.m {
|
||||
if !f(k) {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// 添加
|
||||
func (set *InterfaceSet) Add(item interface{}) *InterfaceSet {
|
||||
set.mu.Lock()
|
||||
set.m[item] = struct{}{}
|
||||
set.mu.Unlock()
|
||||
return set
|
||||
}
|
||||
|
||||
// 批量添加
|
||||
func (set *InterfaceSet) BatchAdd(items []interface{}) *InterfaceSet {
|
||||
set.mu.Lock()
|
||||
for _, item := range items {
|
||||
set.m[item] = struct{}{}
|
||||
}
|
||||
set.mu.Unlock()
|
||||
return set
|
||||
}
|
||||
|
||||
// 键是否存在
|
||||
func (set *InterfaceSet) Contains(item interface{}) bool {
|
||||
set.mu.RLock()
|
||||
_, exists := set.m[item]
|
||||
set.mu.RUnlock()
|
||||
return exists
|
||||
}
|
||||
|
||||
// 删除键值对
|
||||
func (set *InterfaceSet) Remove(key interface{}) {
|
||||
set.mu.Lock()
|
||||
delete(set.m, key)
|
||||
set.mu.Unlock()
|
||||
}
|
||||
|
||||
// 大小
|
||||
func (set *InterfaceSet) Size() int {
|
||||
set.mu.RLock()
|
||||
l := len(set.m)
|
||||
set.mu.RUnlock()
|
||||
return l
|
||||
}
|
||||
|
||||
// 清空set
|
||||
func (set *InterfaceSet) Clear() {
|
||||
set.mu.Lock()
|
||||
set.m = make(map[interface{}]struct{})
|
||||
set.mu.Unlock()
|
||||
}
|
||||
|
||||
// 转换为数组
|
||||
func (set *InterfaceSet) Slice() []interface{} {
|
||||
set.mu.RLock()
|
||||
i := 0
|
||||
ret := make([]interface{}, len(set.m))
|
||||
for item := range set.m {
|
||||
ret[i] = item
|
||||
i++
|
||||
}
|
||||
set.mu.RUnlock()
|
||||
return ret
|
||||
}
|
||||
|
||||
// 转换为字符串
|
||||
func (set *InterfaceSet) String() string {
|
||||
return fmt.Sprint(set.Slice())
|
||||
}
|
||||
|
||||
func (set *InterfaceSet) LockFunc(f func(m map[interface{}]struct{})) {
|
||||
set.mu.Lock(true)
|
||||
defer set.mu.Unlock(true)
|
||||
f(set.m)
|
||||
}
|
||||
|
||||
func (set *InterfaceSet) RLockFunc(f func(m map[interface{}]struct{})) {
|
||||
set.mu.RLock(true)
|
||||
defer set.mu.RUnlock(true)
|
||||
f(set.m)
|
||||
}
|
||||
@ -1,15 +1,15 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
package gset
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gitee.com/johng/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"strings"
|
||||
)
|
||||
|
||||
type StringSet struct {
|
||||
@ -17,6 +17,11 @@ type StringSet struct {
|
||||
m map[string]struct{}
|
||||
}
|
||||
|
||||
// Create a set, which contains un-repeated items.
|
||||
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
|
||||
// which is false in default, means concurrent-safe in default.
|
||||
//
|
||||
// 创建一个空的集合对象,参数unsafe用于指定是否用于非并发安全场景,默认为false,表示并发安全。
|
||||
func NewStringSet(unsafe...bool) *StringSet {
|
||||
return &StringSet {
|
||||
m : make(map[string]struct{}),
|
||||
@ -24,8 +29,11 @@ func NewStringSet(unsafe...bool) *StringSet {
|
||||
}
|
||||
}
|
||||
|
||||
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历
|
||||
func (set *StringSet) Iterator(f func (v string) bool) {
|
||||
// Iterate the set by given callback <f>,
|
||||
// if <f> returns true then continue iterating; or false to stop.
|
||||
//
|
||||
// 给定回调函数对原始内容进行遍历,回调函数返回true表示继续遍历,否则停止遍历。
|
||||
func (set *StringSet) Iterator(f func (v string) bool) *StringSet {
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for k, _ := range set.m {
|
||||
@ -33,27 +41,24 @@ func (set *StringSet) Iterator(f func (v string) bool) {
|
||||
break
|
||||
}
|
||||
}
|
||||
return set
|
||||
}
|
||||
|
||||
// 设置键
|
||||
func (set *StringSet) Add(item string) *StringSet {
|
||||
// Add one or multiple items to the set.
|
||||
//
|
||||
// 添加元素项到集合中(支持多个).
|
||||
func (set *StringSet) Add(item...string) *StringSet {
|
||||
set.mu.Lock()
|
||||
set.m[item] = struct{}{}
|
||||
for _, v := range item {
|
||||
set.m[v] = struct{}{}
|
||||
}
|
||||
set.mu.Unlock()
|
||||
return set
|
||||
}
|
||||
|
||||
// 批量添加设置键
|
||||
func (set *StringSet) BatchAdd(items []string) *StringSet {
|
||||
set.mu.Lock()
|
||||
for _, item := range items {
|
||||
set.m[item] = struct{}{}
|
||||
}
|
||||
set.mu.Unlock()
|
||||
return set
|
||||
}
|
||||
|
||||
// 键是否存在
|
||||
// Check whether the set contains <item>.
|
||||
//
|
||||
// 键是否存在.
|
||||
func (set *StringSet) Contains(item string) bool {
|
||||
set.mu.RLock()
|
||||
_, exists := set.m[item]
|
||||
@ -61,14 +66,19 @@ func (set *StringSet) Contains(item string) bool {
|
||||
return exists
|
||||
}
|
||||
|
||||
// 删除键值对
|
||||
func (set *StringSet) Remove(key string) {
|
||||
// Remove <item> from set.
|
||||
//
|
||||
// 删除元素项。
|
||||
func (set *StringSet) Remove(item string) *StringSet {
|
||||
set.mu.Lock()
|
||||
delete(set.m, key)
|
||||
delete(set.m, item)
|
||||
set.mu.Unlock()
|
||||
return set
|
||||
}
|
||||
|
||||
// 大小
|
||||
// Get size of the set.
|
||||
//
|
||||
// 获得集合大小。
|
||||
func (set *StringSet) Size() int {
|
||||
set.mu.RLock()
|
||||
l := len(set.m)
|
||||
@ -76,14 +86,19 @@ func (set *StringSet) Size() int {
|
||||
return l
|
||||
}
|
||||
|
||||
// 清空set
|
||||
func (set *StringSet) Clear() {
|
||||
// Clear the set.
|
||||
//
|
||||
// 清空集合。
|
||||
func (set *StringSet) Clear() *StringSet {
|
||||
set.mu.Lock()
|
||||
set.m = make(map[string]struct{})
|
||||
set.mu.Unlock()
|
||||
return set
|
||||
}
|
||||
|
||||
// 转换为数组
|
||||
// Get the copy of items from set as slice.
|
||||
//
|
||||
// 获得集合元素项列表.
|
||||
func (set *StringSet) Slice() []string {
|
||||
set.mu.RLock()
|
||||
ret := make([]string, len(set.m))
|
||||
@ -97,19 +112,173 @@ func (set *StringSet) Slice() []string {
|
||||
return ret
|
||||
}
|
||||
|
||||
// 转换为字符串
|
||||
func (set *StringSet) String() string {
|
||||
return fmt.Sprint(set.Slice())
|
||||
// Join set items with a string.
|
||||
//
|
||||
// 使用glue字符串串连当前集合的元素项,构造成新的字符串返回。
|
||||
func (set *StringSet) Join(glue string) string {
|
||||
return strings.Join(set.Slice(), ",")
|
||||
}
|
||||
|
||||
func (set *StringSet) LockFunc(f func(m map[string]struct{})) {
|
||||
// Return set items as a string, which are joined by char ','.
|
||||
//
|
||||
// 使用glue字符串串连当前集合的元素项,构造成新的字符串返回。
|
||||
func (set *StringSet) String() string {
|
||||
return set.Join(",")
|
||||
}
|
||||
|
||||
// Lock writing by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁修改操作。
|
||||
func (set *StringSet) LockFunc(f func(m map[string]struct{})) *StringSet {
|
||||
set.mu.Lock(true)
|
||||
defer set.mu.Unlock(true)
|
||||
f(set.m)
|
||||
return set
|
||||
}
|
||||
|
||||
func (set *StringSet) RLockFunc(f func(m map[string]struct{})) {
|
||||
// Lock reading by callback function f.
|
||||
//
|
||||
// 使用自定义方法执行加锁读取操作。
|
||||
func (set *StringSet) RLockFunc(f func(m map[string]struct{})) *StringSet {
|
||||
set.mu.RLock(true)
|
||||
defer set.mu.RUnlock(true)
|
||||
f(set.m)
|
||||
return set
|
||||
}
|
||||
|
||||
// Check whether the two sets equal.
|
||||
//
|
||||
// 判断两个集合是否相等.
|
||||
func (set *StringSet) Equal(other *StringSet) bool {
|
||||
if set == other {
|
||||
return true
|
||||
}
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
if len(set.m) != len(other.m) {
|
||||
return false
|
||||
}
|
||||
for key := range set.m {
|
||||
if _, ok := other.m[key]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Check whether the current set is sub-set of <other>.
|
||||
//
|
||||
// 判断当前集合是否为other集合的子集.
|
||||
func (set *StringSet) IsSubsetOf(other *StringSet) bool {
|
||||
if set == other {
|
||||
return true
|
||||
}
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
other.mu.RLock()
|
||||
defer other.mu.RUnlock()
|
||||
for key := range set.m {
|
||||
if _, ok := other.m[key]; !ok {
|
||||
return false
|
||||
}
|
||||
}
|
||||
return true
|
||||
}
|
||||
|
||||
// Returns a new set which is the union of <set> and <other>.
|
||||
// Which means, all the items in <newSet> is in <set> or in <other>.
|
||||
//
|
||||
// 并集, 返回新的集合:属于set或属于others的元素为元素的集合.
|
||||
func (set *StringSet) Union(others ... *StringSet) (newSet *StringSet) {
|
||||
newSet = NewStringSet(true)
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for _, other := range others {
|
||||
if set != other {
|
||||
other.mu.RLock()
|
||||
}
|
||||
for k, v := range set.m {
|
||||
newSet.m[k] = v
|
||||
}
|
||||
if set != other {
|
||||
for k, v := range other.m {
|
||||
newSet.m[k] = v
|
||||
}
|
||||
}
|
||||
if set != other {
|
||||
other.mu.RUnlock()
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// Returns a new set which is the difference set from <set> to <other>.
|
||||
// Which means, all the items in <newSet> is in <set> and not in <other>.
|
||||
//
|
||||
// 差集, 返回新的集合: 属于set且不属于others的元素为元素的集合.
|
||||
func (set *StringSet) Diff(others...*StringSet) (newSet *StringSet) {
|
||||
newSet = NewStringSet(true)
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for _, other := range others {
|
||||
if set == other {
|
||||
continue
|
||||
}
|
||||
other.mu.RLock()
|
||||
for k, v := range set.m {
|
||||
if _, ok := other.m[k]; !ok {
|
||||
newSet.m[k] = v
|
||||
}
|
||||
}
|
||||
other.mu.RUnlock()
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Returns a new set which is the intersection from <set> to <other>.
|
||||
// Which means, all the items in <newSet> is in <set> and also in <other>.
|
||||
//
|
||||
// 交集, 返回新的集合: 属于set且属于others的元素为元素的集合.
|
||||
func (set *StringSet) Intersect(others...*StringSet) (newSet *StringSet) {
|
||||
newSet = NewStringSet(true)
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
for _, other := range others {
|
||||
if set != other {
|
||||
other.mu.RLock()
|
||||
}
|
||||
for k, v := range set.m {
|
||||
if _, ok := other.m[k]; ok {
|
||||
newSet.m[k] = v
|
||||
}
|
||||
}
|
||||
if set != other {
|
||||
other.mu.RUnlock()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
// Returns a new set which is the complement from <set> to <full>.
|
||||
// Which means, all the items in <newSet> is in <full> and not in <set>.
|
||||
//
|
||||
// 补集, 返回新的集合: (前提: set应当为full的子集)属于全集full不属于集合set的元素组成的集合.
|
||||
// 如果给定的full集合不是set的全集时,返回full与set的差集.
|
||||
func (set *StringSet) Complement(full *StringSet) (newSet *StringSet) {
|
||||
newSet = NewStringSet(true)
|
||||
set.mu.RLock()
|
||||
defer set.mu.RUnlock()
|
||||
if set != full {
|
||||
full.mu.RLock()
|
||||
defer full.mu.RUnlock()
|
||||
}
|
||||
for k, v := range full.m {
|
||||
if _, ok := set.m[k]; !ok {
|
||||
newSet.m[k] = v
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@ -1,73 +0,0 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf.
|
||||
|
||||
// go test *.go -bench=".*"
|
||||
|
||||
package gset_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"strconv"
|
||||
"gitee.com/johng/gf/g/container/gset"
|
||||
)
|
||||
|
||||
var ints = gset.NewIntSet()
|
||||
var itfs = gset.NewInterfaceSet()
|
||||
var strs = gset.NewStringSet()
|
||||
|
||||
func Benchmark_IntSet_Add(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ints.Add(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_IntSet_Contains(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ints.Contains(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_IntSet_Remove(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ints.Remove(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_InterfaceSet_Add(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
itfs.Add(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_InterfaceSet_Contains(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
itfs.Contains(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_InterfaceSet_Remove(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
itfs.Remove(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_StringSet_Add(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
strs.Add(strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_StringSet_Contains(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
strs.Contains(strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_StringSet_Remove(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
strs.Remove(strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
@ -1,73 +0,0 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/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://gitee.com/johng/gf.
|
||||
|
||||
// go test *.go -bench=".*"
|
||||
|
||||
package gset_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"strconv"
|
||||
"gitee.com/johng/gf/g/container/gset"
|
||||
)
|
||||
|
||||
var intsUnsafe = gset.NewIntSet(true)
|
||||
var itfsUnsafe = gset.NewInterfaceSet(true)
|
||||
var strsUnsafe = gset.NewStringSet(true)
|
||||
|
||||
func Benchmark_Unsafe_IntSet_Add(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
intsUnsafe.Add(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_IntSet_Contains(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
intsUnsafe.Contains(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_IntSet_Remove(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
intsUnsafe.Remove(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_InterfaceSet_Add(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
itfsUnsafe.Add(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_InterfaceSet_Contains(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
itfsUnsafe.Contains(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_InterfaceSet_Remove(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
itfsUnsafe.Remove(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_StringSet_Add(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
strsUnsafe.Add(strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_StringSet_Contains(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
strsUnsafe.Contains(strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_StringSet_Remove(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
strsUnsafe.Remove(strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
130
g/container/gset/gset_z_bench_test.go
Normal file
130
g/container/gset/gset_z_bench_test.go
Normal file
@ -0,0 +1,130 @@
|
||||
// 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.
|
||||
|
||||
// go test *.go -bench=".*"
|
||||
|
||||
package gset_test
|
||||
|
||||
import (
|
||||
"testing"
|
||||
"strconv"
|
||||
"github.com/gogf/gf/g/container/gset"
|
||||
)
|
||||
|
||||
var ints = gset.NewIntSet()
|
||||
var itfs = gset.NewSet()
|
||||
var strs = gset.NewStringSet()
|
||||
var intsUnsafe = gset.NewIntSet(true)
|
||||
var itfsUnsafe = gset.NewSet(true)
|
||||
var strsUnsafe = gset.NewStringSet(true)
|
||||
|
||||
func Benchmark_IntSet_Add(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ints.Add(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_IntSet_Contains(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ints.Contains(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_IntSet_Remove(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
ints.Remove(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Set_Add(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
itfs.Add(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Set_Contains(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
itfs.Contains(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Set_Remove(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
itfs.Remove(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_StringSet_Add(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
strs.Add(strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_StringSet_Contains(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
strs.Contains(strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_StringSet_Remove(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
strs.Remove(strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_IntSet_Add(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
intsUnsafe.Add(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_IntSet_Contains(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
intsUnsafe.Contains(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_IntSet_Remove(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
intsUnsafe.Remove(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_Set_Add(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
itfsUnsafe.Add(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_Set_Contains(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
itfsUnsafe.Contains(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_Set_Remove(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
itfsUnsafe.Remove(i)
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_StringSet_Add(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
strsUnsafe.Add(strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_StringSet_Contains(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
strsUnsafe.Contains(strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
|
||||
func Benchmark_Unsafe_StringSet_Remove(b *testing.B) {
|
||||
for i := 0; i < b.N; i++ {
|
||||
strsUnsafe.Remove(strconv.Itoa(i))
|
||||
}
|
||||
}
|
||||
160
g/container/gset/gset_z_unit_int_test.go
Normal file
160
g/container/gset/gset_z_unit_int_test.go
Normal file
@ -0,0 +1,160 @@
|
||||
// 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.
|
||||
|
||||
// go test *.go
|
||||
|
||||
package gset_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/garray"
|
||||
"github.com/gogf/gf/g/container/gset"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestIntSet_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s := gset.NewIntSet()
|
||||
s.Add(1).Add(1).Add(2)
|
||||
s.Add([]int{3,4}...)
|
||||
gtest.Assert(s.Size(), 4)
|
||||
gtest.AssertIN(1, s.Slice())
|
||||
gtest.AssertIN(2, s.Slice())
|
||||
gtest.AssertIN(3, s.Slice())
|
||||
gtest.AssertIN(4, s.Slice())
|
||||
gtest.AssertNI(0, s.Slice())
|
||||
gtest.Assert(s.Contains(4), true)
|
||||
gtest.Assert(s.Contains(5), false)
|
||||
s.Remove(1)
|
||||
gtest.Assert(s.Size(), 3)
|
||||
s.Clear()
|
||||
gtest.Assert(s.Size(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntSet_Iterator(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s := gset.NewIntSet()
|
||||
s.Add(1).Add(2).Add(3)
|
||||
gtest.Assert(s.Size(), 3)
|
||||
|
||||
a1 := garray.New()
|
||||
a2 := garray.New()
|
||||
s.Iterator(func(v int) bool {
|
||||
a1.Append(1)
|
||||
return false
|
||||
})
|
||||
s.Iterator(func(v int) bool {
|
||||
a2.Append(1)
|
||||
return true
|
||||
})
|
||||
gtest.Assert(a1.Len(), 1)
|
||||
gtest.Assert(a2.Len(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntSet_LockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s := gset.NewIntSet()
|
||||
s.Add(1).Add(2).Add(3)
|
||||
gtest.Assert(s.Size(), 3)
|
||||
s.LockFunc(func(m map[int]struct{}) {
|
||||
delete(m, 1)
|
||||
})
|
||||
gtest.Assert(s.Size(), 2)
|
||||
s.RLockFunc(func(m map[int]struct{}) {
|
||||
gtest.Assert(m, map[int]struct{}{
|
||||
3 : struct{}{},
|
||||
2 : struct{}{},
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntSet_Equal(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
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)
|
||||
gtest.Assert(s1.Equal(s2), true)
|
||||
gtest.Assert(s1.Equal(s3), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntSet_IsSubsetOf(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
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)
|
||||
gtest.Assert(s1.IsSubsetOf(s2), true)
|
||||
gtest.Assert(s2.IsSubsetOf(s3), true)
|
||||
gtest.Assert(s1.IsSubsetOf(s3), true)
|
||||
gtest.Assert(s2.IsSubsetOf(s1), false)
|
||||
gtest.Assert(s3.IsSubsetOf(s2), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntSet_Union(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.NewIntSet()
|
||||
s2 := gset.NewIntSet()
|
||||
s1.Add(1).Add(2)
|
||||
s2.Add(3).Add(4)
|
||||
s3 := s1.Union(s2)
|
||||
gtest.Assert(s3.Contains(1), true)
|
||||
gtest.Assert(s3.Contains(2), true)
|
||||
gtest.Assert(s3.Contains(3), true)
|
||||
gtest.Assert(s3.Contains(4), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntSet_Diff(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.NewIntSet()
|
||||
s2 := gset.NewIntSet()
|
||||
s1.Add(1).Add(2).Add(3)
|
||||
s2.Add(3).Add(4).Add(5)
|
||||
s3 := s1.Diff(s2)
|
||||
gtest.Assert(s3.Contains(1), true)
|
||||
gtest.Assert(s3.Contains(2), true)
|
||||
gtest.Assert(s3.Contains(3), false)
|
||||
gtest.Assert(s3.Contains(4), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntSet_Intersect(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.NewIntSet()
|
||||
s2 := gset.NewIntSet()
|
||||
s1.Add(1).Add(2).Add(3)
|
||||
s2.Add(3).Add(4).Add(5)
|
||||
s3 := s1.Intersect(s2)
|
||||
gtest.Assert(s3.Contains(1), false)
|
||||
gtest.Assert(s3.Contains(2), false)
|
||||
gtest.Assert(s3.Contains(3), true)
|
||||
gtest.Assert(s3.Contains(4), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntSet_Complement(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.NewIntSet()
|
||||
s2 := gset.NewIntSet()
|
||||
s1.Add(1).Add(2).Add(3)
|
||||
s2.Add(3).Add(4).Add(5)
|
||||
s3 := s1.Complement(s2)
|
||||
gtest.Assert(s3.Contains(1), false)
|
||||
gtest.Assert(s3.Contains(2), false)
|
||||
gtest.Assert(s3.Contains(4), true)
|
||||
gtest.Assert(s3.Contains(5), true)
|
||||
})
|
||||
}
|
||||
160
g/container/gset/gset_z_unit_string_test.go
Normal file
160
g/container/gset/gset_z_unit_string_test.go
Normal file
@ -0,0 +1,160 @@
|
||||
// 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.
|
||||
|
||||
// go test *.go
|
||||
|
||||
package gset_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/garray"
|
||||
"github.com/gogf/gf/g/container/gset"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestStringSet_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s := gset.NewStringSet()
|
||||
s.Add("1").Add("1").Add("2")
|
||||
s.Add([]string{"3","4"}...)
|
||||
gtest.Assert(s.Size(), 4)
|
||||
gtest.AssertIN("1", s.Slice())
|
||||
gtest.AssertIN("2", s.Slice())
|
||||
gtest.AssertIN("3", s.Slice())
|
||||
gtest.AssertIN("4", s.Slice())
|
||||
gtest.AssertNI("0", s.Slice())
|
||||
gtest.Assert(s.Contains("4"), true)
|
||||
gtest.Assert(s.Contains("5"), false)
|
||||
s.Remove("1")
|
||||
gtest.Assert(s.Size(), 3)
|
||||
s.Clear()
|
||||
gtest.Assert(s.Size(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStringSet_Iterator(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s := gset.NewStringSet()
|
||||
s.Add("1").Add("2").Add("3")
|
||||
gtest.Assert(s.Size(), 3)
|
||||
|
||||
a1 := garray.New()
|
||||
a2 := garray.New()
|
||||
s.Iterator(func(v string) bool {
|
||||
a1.Append("1")
|
||||
return false
|
||||
})
|
||||
s.Iterator(func(v string) bool {
|
||||
a2.Append("1")
|
||||
return true
|
||||
})
|
||||
gtest.Assert(a1.Len(), 1)
|
||||
gtest.Assert(a2.Len(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStringSet_LockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s := gset.NewStringSet()
|
||||
s.Add("1").Add("2").Add("3")
|
||||
gtest.Assert(s.Size(), 3)
|
||||
s.LockFunc(func(m map[string]struct{}) {
|
||||
delete(m, "1")
|
||||
})
|
||||
gtest.Assert(s.Size(), 2)
|
||||
s.RLockFunc(func(m map[string]struct{}) {
|
||||
gtest.Assert(m, map[string]struct{}{
|
||||
"3" : struct{}{},
|
||||
"2" : struct{}{},
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestStringSet_Equal(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.NewStringSet()
|
||||
s2 := gset.NewStringSet()
|
||||
s3 := gset.NewStringSet()
|
||||
s1.Add("1").Add("2").Add("3")
|
||||
s2.Add("1").Add("2").Add("3")
|
||||
s3.Add("1").Add("2").Add("3").Add("4")
|
||||
gtest.Assert(s1.Equal(s2), true)
|
||||
gtest.Assert(s1.Equal(s3), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStringSet_IsSubsetOf(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.NewStringSet()
|
||||
s2 := gset.NewStringSet()
|
||||
s3 := gset.NewStringSet()
|
||||
s1.Add("1").Add("2")
|
||||
s2.Add("1").Add("2").Add("3")
|
||||
s3.Add("1").Add("2").Add("3").Add("4")
|
||||
gtest.Assert(s1.IsSubsetOf(s2), true)
|
||||
gtest.Assert(s2.IsSubsetOf(s3), true)
|
||||
gtest.Assert(s1.IsSubsetOf(s3), true)
|
||||
gtest.Assert(s2.IsSubsetOf(s1), false)
|
||||
gtest.Assert(s3.IsSubsetOf(s2), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStringSet_Union(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.NewStringSet()
|
||||
s2 := gset.NewStringSet()
|
||||
s1.Add("1").Add("2")
|
||||
s2.Add("3").Add("4")
|
||||
s3 := s1.Union(s2)
|
||||
gtest.Assert(s3.Contains("1"), true)
|
||||
gtest.Assert(s3.Contains("2"), true)
|
||||
gtest.Assert(s3.Contains("3"), true)
|
||||
gtest.Assert(s3.Contains("4"), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStringSet_Diff(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.NewStringSet()
|
||||
s2 := gset.NewStringSet()
|
||||
s1.Add("1").Add("2").Add("3")
|
||||
s2.Add("3").Add("4").Add("5")
|
||||
s3 := s1.Diff(s2)
|
||||
gtest.Assert(s3.Contains("1"), true)
|
||||
gtest.Assert(s3.Contains("2"), true)
|
||||
gtest.Assert(s3.Contains("3"), false)
|
||||
gtest.Assert(s3.Contains("4"), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStringSet_Intersect(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.NewStringSet()
|
||||
s2 := gset.NewStringSet()
|
||||
s1.Add("1").Add("2").Add("3")
|
||||
s2.Add("3").Add("4").Add("5")
|
||||
s3 := s1.Intersect(s2)
|
||||
gtest.Assert(s3.Contains("1"), false)
|
||||
gtest.Assert(s3.Contains("2"), false)
|
||||
gtest.Assert(s3.Contains("3"), true)
|
||||
gtest.Assert(s3.Contains("4"), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStringSet_Complement(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.NewStringSet()
|
||||
s2 := gset.NewStringSet()
|
||||
s1.Add("1").Add("2").Add("3")
|
||||
s2.Add("3").Add("4").Add("5")
|
||||
s3 := s1.Complement(s2)
|
||||
gtest.Assert(s3.Contains("1"), false)
|
||||
gtest.Assert(s3.Contains("2"), false)
|
||||
gtest.Assert(s3.Contains("4"), true)
|
||||
gtest.Assert(s3.Contains("5"), true)
|
||||
})
|
||||
}
|
||||
160
g/container/gset/gset_z_unit_test.go
Normal file
160
g/container/gset/gset_z_unit_test.go
Normal file
@ -0,0 +1,160 @@
|
||||
// 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.
|
||||
|
||||
// go test *.go
|
||||
|
||||
package gset_test
|
||||
|
||||
import (
|
||||
"github.com/gogf/gf/g/container/garray"
|
||||
"github.com/gogf/gf/g/container/gset"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestSet_Basic(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s := gset.NewSet()
|
||||
s.Add(1).Add(1).Add(2)
|
||||
s.Add([]interface{}{3,4}...)
|
||||
gtest.Assert(s.Size(), 4)
|
||||
gtest.AssertIN(1, s.Slice())
|
||||
gtest.AssertIN(2, s.Slice())
|
||||
gtest.AssertIN(3, s.Slice())
|
||||
gtest.AssertIN(4, s.Slice())
|
||||
gtest.AssertNI(0, s.Slice())
|
||||
gtest.Assert(s.Contains(4), true)
|
||||
gtest.Assert(s.Contains(5), false)
|
||||
s.Remove(1)
|
||||
gtest.Assert(s.Size(), 3)
|
||||
s.Clear()
|
||||
gtest.Assert(s.Size(), 0)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSet_Iterator(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s := gset.NewSet()
|
||||
s.Add(1).Add(2).Add(3)
|
||||
gtest.Assert(s.Size(), 3)
|
||||
|
||||
a1 := garray.New()
|
||||
a2 := garray.New()
|
||||
s.Iterator(func(v interface{}) bool {
|
||||
a1.Append(1)
|
||||
return false
|
||||
})
|
||||
s.Iterator(func(v interface{}) bool {
|
||||
a2.Append(1)
|
||||
return true
|
||||
})
|
||||
gtest.Assert(a1.Len(), 1)
|
||||
gtest.Assert(a2.Len(), 3)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSet_LockFunc(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s := gset.NewSet()
|
||||
s.Add(1).Add(2).Add(3)
|
||||
gtest.Assert(s.Size(), 3)
|
||||
s.LockFunc(func(m map[interface{}]struct{}) {
|
||||
delete(m, 1)
|
||||
})
|
||||
gtest.Assert(s.Size(), 2)
|
||||
s.RLockFunc(func(m map[interface{}]struct{}) {
|
||||
gtest.Assert(m, map[interface{}]struct{}{
|
||||
3 : struct{}{},
|
||||
2 : struct{}{},
|
||||
})
|
||||
})
|
||||
})
|
||||
}
|
||||
|
||||
func TestSet_Equal(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
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)
|
||||
gtest.Assert(s1.Equal(s2), true)
|
||||
gtest.Assert(s1.Equal(s3), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSet_IsSubsetOf(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
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)
|
||||
gtest.Assert(s1.IsSubsetOf(s2), true)
|
||||
gtest.Assert(s2.IsSubsetOf(s3), true)
|
||||
gtest.Assert(s1.IsSubsetOf(s3), true)
|
||||
gtest.Assert(s2.IsSubsetOf(s1), false)
|
||||
gtest.Assert(s3.IsSubsetOf(s2), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSet_Union(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.NewSet()
|
||||
s2 := gset.NewSet()
|
||||
s1.Add(1).Add(2)
|
||||
s2.Add(3).Add(4)
|
||||
s3 := s1.Union(s2)
|
||||
gtest.Assert(s3.Contains(1), true)
|
||||
gtest.Assert(s3.Contains(2), true)
|
||||
gtest.Assert(s3.Contains(3), true)
|
||||
gtest.Assert(s3.Contains(4), true)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSet_Diff(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.NewSet()
|
||||
s2 := gset.NewSet()
|
||||
s1.Add(1).Add(2).Add(3)
|
||||
s2.Add(3).Add(4).Add(5)
|
||||
s3 := s1.Diff(s2)
|
||||
gtest.Assert(s3.Contains(1), true)
|
||||
gtest.Assert(s3.Contains(2), true)
|
||||
gtest.Assert(s3.Contains(3), false)
|
||||
gtest.Assert(s3.Contains(4), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSet_Intersect(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.NewSet()
|
||||
s2 := gset.NewSet()
|
||||
s1.Add(1).Add(2).Add(3)
|
||||
s2.Add(3).Add(4).Add(5)
|
||||
s3 := s1.Intersect(s2)
|
||||
gtest.Assert(s3.Contains(1), false)
|
||||
gtest.Assert(s3.Contains(2), false)
|
||||
gtest.Assert(s3.Contains(3), true)
|
||||
gtest.Assert(s3.Contains(4), false)
|
||||
})
|
||||
}
|
||||
|
||||
func TestSet_Complement(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
s1 := gset.NewSet()
|
||||
s2 := gset.NewSet()
|
||||
s1.Add(1).Add(2).Add(3)
|
||||
s2.Add(3).Add(4).Add(5)
|
||||
s3 := s1.Complement(s2)
|
||||
gtest.Assert(s3.Contains(1), false)
|
||||
gtest.Assert(s3.Contains(2), false)
|
||||
gtest.Assert(s3.Contains(4), true)
|
||||
gtest.Assert(s3.Contains(5), true)
|
||||
})
|
||||
}
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gtype
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gtype
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gtype
|
||||
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gtype
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"gitee.com/johng/gf/g/encoding/gbinary"
|
||||
"github.com/gogf/gf/g/encoding/gbinary"
|
||||
)
|
||||
|
||||
type Float32 struct {
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gtype
|
||||
|
||||
import (
|
||||
"sync/atomic"
|
||||
"gitee.com/johng/gf/g/encoding/gbinary"
|
||||
"github.com/gogf/gf/g/encoding/gbinary"
|
||||
)
|
||||
|
||||
type Float64 struct {
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gtype provides kinds of high performance, concurrent-safe basic variable types.
|
||||
//
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// go test *.go -bench=".*" -benchmem
|
||||
|
||||
@ -11,7 +11,7 @@ package gtype
|
||||
import (
|
||||
"testing"
|
||||
"strconv"
|
||||
"gitee.com/johng/gf/g/encoding/gbinary"
|
||||
"github.com/gogf/gf/g/encoding/gbinary"
|
||||
"sync/atomic"
|
||||
)
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gtype
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gtype
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gtype
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gtype
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gtype
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gtype
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gtype
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gtype
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gvar provides an universal variable type, like generics.
|
||||
//
|
||||
@ -10,9 +10,9 @@
|
||||
package gvar
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/container/gtype"
|
||||
"gitee.com/johng/gf/g/os/gtime"
|
||||
"gitee.com/johng/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gvar
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/os/gtime"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// go test *.go -bench=".*" -benchmem
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
// Copyright 2019 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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 this file,
|
||||
// You can obtain one at https://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package crypto
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gaes provides useful API for AES encryption/decryption algorithms.
|
||||
package gaes
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gcrc32 provides useful API for CRC32 encryption/decryption algorithms.
|
||||
package gcrc32
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
// @author: wenzi1<liyz23@qq.com>
|
||||
|
||||
// Package gdes provides useful API for DES encryption/decryption algorithms.
|
||||
@ -262,14 +262,14 @@ func PKCS5Unpadding(text []byte) []byte{
|
||||
//补位方法
|
||||
func Padding(text []byte, padding int)([]byte, error) {
|
||||
switch padding {
|
||||
case NOPADDING:
|
||||
if len(text) % 8 != 0 {
|
||||
return nil, errors.New("text length invalid")
|
||||
}
|
||||
case PKCS5PADDING:
|
||||
return PKCS5Padding(text, 8), nil
|
||||
default:
|
||||
return nil, errors.New("padding type error")
|
||||
case NOPADDING:
|
||||
if len(text) % 8 != 0 {
|
||||
return nil, errors.New("text length invalid")
|
||||
}
|
||||
case PKCS5PADDING:
|
||||
return PKCS5Padding(text, 8), nil
|
||||
default:
|
||||
return nil, errors.New("padding type error")
|
||||
}
|
||||
|
||||
return text, nil
|
||||
@ -278,14 +278,14 @@ func Padding(text []byte, padding int)([]byte, error) {
|
||||
//去除补位方法
|
||||
func UnPadding(text []byte, padding int)([]byte, error) {
|
||||
switch padding {
|
||||
case NOPADDING:
|
||||
if len(text) % 8 != 0 {
|
||||
return nil, errors.New("text length invalid")
|
||||
}
|
||||
case PKCS5PADDING:
|
||||
return PKCS5Unpadding(text), nil
|
||||
default:
|
||||
return nil, errors.New("padding type error.")
|
||||
case NOPADDING:
|
||||
if len(text) % 8 != 0 {
|
||||
return nil, errors.New("text length invalid")
|
||||
}
|
||||
case PKCS5PADDING:
|
||||
return PKCS5Unpadding(text), nil
|
||||
default:
|
||||
return nil, errors.New("padding type error.")
|
||||
}
|
||||
return text, nil
|
||||
}
|
||||
@ -5,7 +5,7 @@ import (
|
||||
"bytes"
|
||||
"encoding/hex"
|
||||
"fmt"
|
||||
"gitee.com/johng/gf/g/crypto/gdes"
|
||||
"github.com/gogf/gf/g/crypto/gdes"
|
||||
)
|
||||
|
||||
func TestDesECB(t *testing.T){
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gmd5 provides useful API for MD5 encryption/decryption algorithms.
|
||||
package gmd5
|
||||
@ -12,7 +12,7 @@ import (
|
||||
"fmt"
|
||||
"os"
|
||||
"io"
|
||||
"gitee.com/johng/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
// 将任意类型的变量进行md5摘要(注意map等非排序变量造成的不同结果)
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gsha1 provides useful API for SHA1 encryption/decryption algorithms.
|
||||
package gsha1
|
||||
@ -12,7 +12,7 @@ import (
|
||||
"encoding/hex"
|
||||
"os"
|
||||
"io"
|
||||
"gitee.com/johng/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
// 将任意类型的变量进行SHA摘要(注意map等非排序变量造成的不同结果)
|
||||
@ -34,6 +34,7 @@ func EncryptFile(path string) string {
|
||||
if e != nil {
|
||||
return ""
|
||||
}
|
||||
defer f.Close()
|
||||
h := sha1.New()
|
||||
_, e = io.Copy(h, f)
|
||||
if e != nil {
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package database
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
// Package gdb provides ORM features for popular relationship databases.
|
||||
//
|
||||
@ -14,12 +14,12 @@ import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"gitee.com/johng/gf/g/container/gring"
|
||||
"gitee.com/johng/gf/g/container/gtype"
|
||||
"gitee.com/johng/gf/g/container/gvar"
|
||||
"gitee.com/johng/gf/g/os/gcache"
|
||||
"gitee.com/johng/gf/g/util/grand"
|
||||
_ "gitee.com/johng/gf/third/github.com/go-sql-driver/mysql"
|
||||
"github.com/gogf/gf/g/container/gring"
|
||||
"github.com/gogf/gf/g/container/gtype"
|
||||
"github.com/gogf/gf/g/container/gvar"
|
||||
"github.com/gogf/gf/g/os/gcache"
|
||||
"github.com/gogf/gf/g/util/grand"
|
||||
_ "github.com/gogf/gf/third/github.com/go-sql-driver/mysql"
|
||||
"time"
|
||||
)
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
//
|
||||
|
||||
package gdb
|
||||
@ -11,11 +11,11 @@ import (
|
||||
"database/sql"
|
||||
"errors"
|
||||
"fmt"
|
||||
"gitee.com/johng/gf/g/container/gvar"
|
||||
"gitee.com/johng/gf/g/os/gcache"
|
||||
"gitee.com/johng/gf/g/os/gtime"
|
||||
"gitee.com/johng/gf/g/util/gconv"
|
||||
"gitee.com/johng/gf/g/util/gregex"
|
||||
"github.com/gogf/gf/g/container/gvar"
|
||||
"github.com/gogf/gf/g/os/gcache"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
@ -265,7 +265,7 @@ func (bs *dbBase) doInsert(link dbLink, table string, data Map, option int) (res
|
||||
params = append(params, v)
|
||||
}
|
||||
operation := getInsertOperationByOption(option)
|
||||
updatestr := ""
|
||||
updateStr := ""
|
||||
if option == OPTION_SAVE {
|
||||
var updates []string
|
||||
for k, _ := range data {
|
||||
@ -276,7 +276,7 @@ func (bs *dbBase) doInsert(link dbLink, table string, data Map, option int) (res
|
||||
),
|
||||
)
|
||||
}
|
||||
updatestr = fmt.Sprintf("ON DUPLICATE KEY UPDATE %s", strings.Join(updates, ","))
|
||||
updateStr = fmt.Sprintf("ON DUPLICATE KEY UPDATE %s", strings.Join(updates, ","))
|
||||
}
|
||||
if link == nil {
|
||||
if link, err = bs.db.Master(); err != nil {
|
||||
@ -285,7 +285,7 @@ func (bs *dbBase) doInsert(link dbLink, table string, data Map, option int) (res
|
||||
}
|
||||
return bs.db.doExec(link, fmt.Sprintf("%s INTO %s(%s) VALUES(%s) %s",
|
||||
operation, table, strings.Join(fields, ","),
|
||||
strings.Join(values, ","), updatestr),
|
||||
strings.Join(values, ","), updateStr),
|
||||
params...)
|
||||
}
|
||||
|
||||
@ -324,12 +324,13 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list List, batch int,
|
||||
keys = append(keys, k)
|
||||
values = append(values, "?")
|
||||
}
|
||||
batchResult := new(batchSqlResult)
|
||||
charl, charr := bs.db.getChars()
|
||||
keyStr := charl + strings.Join(keys, charl + "," + charr) + charr
|
||||
valueHolderStr := "(" + strings.Join(values, ",") + ")"
|
||||
// 操作判断
|
||||
operation := getInsertOperationByOption(option)
|
||||
updatestr := ""
|
||||
updateStr := ""
|
||||
if option == OPTION_SAVE {
|
||||
var updates []string
|
||||
for _, k := range keys {
|
||||
@ -340,7 +341,7 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list List, batch int,
|
||||
),
|
||||
)
|
||||
}
|
||||
updatestr = fmt.Sprintf(" ON DUPLICATE KEY UPDATE %s", strings.Join(updates, ","))
|
||||
updateStr = fmt.Sprintf(" ON DUPLICATE KEY UPDATE %s", strings.Join(updates, ","))
|
||||
}
|
||||
// 构造批量写入数据格式(注意map的遍历是无序的)
|
||||
for i := 0; i < len(list); i++ {
|
||||
@ -351,12 +352,17 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list List, batch int,
|
||||
if len(bvalues) == batch {
|
||||
r, err := bs.db.doExec(link, fmt.Sprintf("%s INTO %s(%s) VALUES%s %s",
|
||||
operation, table, keyStr, strings.Join(bvalues, ","),
|
||||
updatestr),
|
||||
updateStr),
|
||||
params...)
|
||||
if err != nil {
|
||||
return result, err
|
||||
return r, err
|
||||
}
|
||||
if n, err := r.RowsAffected(); err != nil {
|
||||
return r, err
|
||||
} else {
|
||||
batchResult.lastResult = r
|
||||
batchResult.rowsAffected += n
|
||||
}
|
||||
result = r
|
||||
params = params[:0]
|
||||
bvalues = bvalues[:0]
|
||||
}
|
||||
@ -365,14 +371,19 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list List, batch int,
|
||||
if len(bvalues) > 0 {
|
||||
r, err := bs.db.doExec(link, fmt.Sprintf("%s INTO %s(%s) VALUES%s %s",
|
||||
operation, table, keyStr, strings.Join(bvalues, ","),
|
||||
updatestr),
|
||||
updateStr),
|
||||
params...)
|
||||
if err != nil {
|
||||
return result, err
|
||||
return r, err
|
||||
}
|
||||
if n, err := r.RowsAffected(); err != nil {
|
||||
return r, err
|
||||
} else {
|
||||
batchResult.lastResult = r
|
||||
batchResult.rowsAffected += n
|
||||
}
|
||||
result = r
|
||||
}
|
||||
return result, nil
|
||||
return batchResult, nil
|
||||
}
|
||||
|
||||
// CURD操作:数据更新,统一采用sql预处理
|
||||
|
||||
25
g/database/gdb/gdb_batch_result.go
Normal file
25
g/database/gdb/gdb_batch_result.go
Normal file
@ -0,0 +1,25 @@
|
||||
// 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 this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gdb
|
||||
|
||||
import "database/sql"
|
||||
|
||||
// 批量执行的结果对象
|
||||
type batchSqlResult struct {
|
||||
rowsAffected int64
|
||||
lastResult sql.Result
|
||||
}
|
||||
|
||||
// see sql.Result.RowsAffected
|
||||
func (r *batchSqlResult) RowsAffected() (int64, error) {
|
||||
return r.rowsAffected, nil
|
||||
}
|
||||
|
||||
// see sql.Result.LastInsertId
|
||||
func (r *batchSqlResult) LastInsertId() (int64, error) {
|
||||
return r.lastResult.LastInsertId()
|
||||
}
|
||||
@ -1,15 +1,15 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
// 对常用关系数据库的封装管理包
|
||||
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gitee.com/johng/gf/g/container/gring"
|
||||
"github.com/gogf/gf/g/container/gring"
|
||||
"sync"
|
||||
)
|
||||
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2017-2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// Copyright 2017-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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gdb
|
||||
|
||||
@ -10,12 +10,12 @@ import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"fmt"
|
||||
"gitee.com/johng/gf/g/os/glog"
|
||||
"gitee.com/johng/gf/g/os/gtime"
|
||||
"gitee.com/johng/gf/g/util/gconv"
|
||||
"gitee.com/johng/gf/g/util/gregex"
|
||||
"gitee.com/johng/gf/g/util/gstr"
|
||||
_ "gitee.com/johng/gf/third/github.com/go-sql-driver/mysql"
|
||||
"github.com/gogf/gf/g/os/glog"
|
||||
"github.com/gogf/gf/g/os/gtime"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
"github.com/gogf/gf/g/text/gstr"
|
||||
_ "github.com/gogf/gf/third/github.com/go-sql-driver/mysql"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gdb
|
||||
|
||||
@ -10,8 +10,8 @@ import (
|
||||
"fmt"
|
||||
"errors"
|
||||
"database/sql"
|
||||
"gitee.com/johng/gf/g/util/gconv"
|
||||
_ "gitee.com/johng/gf/third/github.com/go-sql-driver/mysql"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
_ "github.com/gogf/gf/third/github.com/go-sql-driver/mysql"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
/*
|
||||
@author wenzi1<liyz23@qq.com>
|
||||
@date 20181109
|
||||
@ -16,7 +16,7 @@ package gdb
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"gitee.com/johng/gf/g/util/gregex"
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
|
||||
package gdb
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
/*
|
||||
@author wenzi1<liyz23@qq.com>
|
||||
@date 20181026
|
||||
@ -16,7 +16,7 @@ package gdb
|
||||
import (
|
||||
"database/sql"
|
||||
"fmt"
|
||||
"gitee.com/johng/gf/g/util/gregex"
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
"strconv"
|
||||
"strings"
|
||||
)
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
|
||||
package gdb
|
||||
@ -15,7 +15,7 @@ import (
|
||||
|
||||
// PostgreSQL的适配.
|
||||
// 使用时需要import:
|
||||
// _ "gitee.com/johng/gf/third/github.com/lib/pq"
|
||||
// _ "github.com/gogf/gf/third/github.com/lib/pq"
|
||||
// @todo 需要完善replace和save的操作覆盖
|
||||
|
||||
// 数据库链接对象
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
// @author wxkj<wxscz@qq.com>
|
||||
|
||||
package gdb
|
||||
@ -12,7 +12,7 @@ import (
|
||||
)
|
||||
|
||||
// 使用时需要import:
|
||||
// _ "gitee.com/johng/gf/third/github.com/mattn/go-sqlite3"
|
||||
// _ "github.com/gogf/gf/third/github.com/mattn/go-sqlite3"
|
||||
|
||||
// Sqlite接口对象
|
||||
// @author wxkj<wxscz@qq.com>
|
||||
|
||||
@ -1,16 +1,16 @@
|
||||
// Copyright 2019 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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 this file,
|
||||
// You can obtain one at https://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"gitee.com/johng/gf/g/util/gconv"
|
||||
"gitee.com/johng/gf/g/util/gregex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
"strings"
|
||||
)
|
||||
|
||||
|
||||
@ -1,15 +1,15 @@
|
||||
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"database/sql"
|
||||
"gitee.com/johng/gf/g/util/gregex"
|
||||
_ "gitee.com/johng/gf/third/github.com/go-sql-driver/mysql"
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
_ "github.com/gogf/gf/third/github.com/go-sql-driver/mysql"
|
||||
)
|
||||
|
||||
// 数据库事务对象
|
||||
|
||||
@ -1,14 +1,14 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/encoding/gparser"
|
||||
"gitee.com/johng/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/encoding/gparser"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
)
|
||||
|
||||
// 将记录结果转换为JSON字符串
|
||||
|
||||
@ -1,13 +1,13 @@
|
||||
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
|
||||
// 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://gitee.com/johng/gf.
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package gdb
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/encoding/gparser"
|
||||
"github.com/gogf/gf/g/encoding/gparser"
|
||||
)
|
||||
|
||||
// 将结果集转换为JSON字符串
|
||||
|
||||
@ -1,8 +1,8 @@
|
||||
package gdb_test
|
||||
|
||||
import (
|
||||
"gitee.com/johng/gf/g/database/gdb"
|
||||
"gitee.com/johng/gf/g/util/gtest"
|
||||
"github.com/gogf/gf/g/database/gdb"
|
||||
"github.com/gogf/gf/g/test/gtest"
|
||||
)
|
||||
|
||||
var (
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user