mirror of
https://gitee.com/johng/gf
synced 2026-06-07 18:26:02 +08:00
Compare commits
127 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 94bd5da68a | |||
| 3eee95caf2 | |||
| 5c638c630a | |||
| a6ec9d7a1c | |||
| 374c70c0e3 | |||
| 40771066d4 | |||
| 22a7ef43ce | |||
| 05f22d1cee | |||
| ebf56a86ab | |||
| 2ba59e8943 | |||
| 83be1de04c | |||
| c8251ed82f | |||
| 2335ea0c4d | |||
| 5d874e9063 | |||
| f2c080d25f | |||
| 975da97b4a | |||
| 37617589a6 | |||
| 5c9f0db903 | |||
| 28abf0c175 | |||
| 13749feab4 | |||
| c1e77b7e09 | |||
| 962a5e93f7 | |||
| c3b9b8d5ae | |||
| 1ad076c522 | |||
| b01777fcd1 | |||
| 55a5532c2e | |||
| adb928941a | |||
| f92c1fc527 | |||
| 3ae7279ebc | |||
| 66287c2d0e | |||
| 5d37626981 | |||
| d0ed3b979d | |||
| fa256aec9f | |||
| 86834c5a15 | |||
| c2046157d6 | |||
| cdb2cc89c0 | |||
| 4964c09a77 | |||
| ef34b2c9ce | |||
| 9afe242293 | |||
| 136d93d373 | |||
| 3102cec5b8 | |||
| e28eb9da04 | |||
| 754ed86dfb | |||
| e352b07055 | |||
| fdea242b50 | |||
| 7058e4f2c4 | |||
| 704a5dbd73 | |||
| fbd4ce8c2e | |||
| cb3ce71cdc | |||
| 7f44f2f5e4 | |||
| 66efbe63f0 | |||
| 49a1308875 | |||
| 4332580c01 | |||
| 72ecf2d2af | |||
| 0f854e46d8 | |||
| 4564f38e1a | |||
| 7e06bf6705 | |||
| 3dd8b6ad33 | |||
| 3e0a975a88 | |||
| 6aa1c5b1eb | |||
| d780cf64c2 | |||
| 1fb5a8cd6f | |||
| 8925460718 | |||
| 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 | |||
| 1dc6c799e1 | |||
| ae1e075696 | |||
| 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 |
34
.gitee/ISSUE_TEMPLATE.MD
Normal file
34
.gitee/ISSUE_TEMPLATE.MD
Normal file
@ -0,0 +1,34 @@
|
||||
<!-- 为更高效率地交流并解决问题,请按照以下模板提交issue,感谢! -->
|
||||
|
||||
### 1. 您当前使用的`Go`版本,及系统版本、系统架构?
|
||||
|
||||
<!-- 使用 `go version` 命令查看,期望的结果如:`go 1.12, linux/amd64` -->
|
||||
|
||||
|
||||
### 2. 您当前使用的`GoFrame`框架版本?
|
||||
|
||||
<!-- 框架版本可以查看自己项目下的 `go.mod`,或者框架文件 `version.go` -->
|
||||
|
||||
|
||||
### 3. 更新到最新的框架版本是否能够解决问题?
|
||||
|
||||
<!-- 务必检查是否相同问题已在新版本中已修复 -->
|
||||
|
||||
|
||||
### 4. 问题描述?
|
||||
|
||||
<!--
|
||||
请您尽可能地提供一份最短的,可复现问题的代码。
|
||||
代码尽可能地完整,最好是可以直接编译运行。
|
||||
-->
|
||||
|
||||
|
||||
|
||||
### 5. 您期望得到的结果?
|
||||
|
||||
|
||||
|
||||
### 6. 您实际得到的结果?
|
||||
|
||||
|
||||
|
||||
36
.github/ISSUE_TEMPLATE.MD
vendored
Normal file
36
.github/ISSUE_TEMPLATE.MD
vendored
Normal file
@ -0,0 +1,36 @@
|
||||
<!-- Please answer these questions before submitting your issue. Thanks! -->
|
||||
|
||||
### 1. What version of `Go` and system type/arch are you using?
|
||||
|
||||
<!--
|
||||
Please paste the output of command `go version` from your terminal.
|
||||
What expect to see is like: `go 1.12, linux/amd64`
|
||||
-->
|
||||
|
||||
|
||||
### 2. What version of `GoFrame` are you using?
|
||||
|
||||
<!-- You can find the GF version from your `go.mod`, or from the `version.go` in `GF` -->
|
||||
|
||||
|
||||
### 3. Can this issue be reproduced with the latest release?
|
||||
|
||||
|
||||
|
||||
### 4. What did you do?
|
||||
|
||||
<!--
|
||||
If possible, provide a copy of shortest codes for reproducing the error.
|
||||
A complete runnable program is best.
|
||||
-->
|
||||
|
||||
|
||||
|
||||
### 5. What did you expect to see?
|
||||
|
||||
|
||||
|
||||
### 6. What did you see instead?
|
||||
|
||||
|
||||
|
||||
26
.travis.yml
26
.travis.yml
@ -1,7 +1,8 @@
|
||||
language: go
|
||||
|
||||
go:
|
||||
- "1.11.x"
|
||||
- "1.11.x"
|
||||
- "1.12.x"
|
||||
|
||||
branches:
|
||||
only:
|
||||
@ -9,27 +10,28 @@ branches:
|
||||
- develop
|
||||
|
||||
env:
|
||||
- GITEE_GF=$GOPATH/src/gitee.com/johng/gf GO111MODULE=on
|
||||
- GO111MODULE=on
|
||||
|
||||
services:
|
||||
- mysql
|
||||
- mysql
|
||||
|
||||
addons:
|
||||
hosts:
|
||||
- local
|
||||
|
||||
before_install:
|
||||
- pwd
|
||||
- pwd
|
||||
|
||||
install:
|
||||
- pwd
|
||||
- mkdir -p $GITEE_GF
|
||||
- cp * $GITEE_GF -R
|
||||
- cd $GITEE_GF/g
|
||||
- cat /etc/hosts
|
||||
|
||||
script:
|
||||
- GOARCH=386 go test -v ./...
|
||||
- GOARCH=amd64 go test -v ./... -race -coverprofile=coverage.txt -covermode=atomic
|
||||
|
||||
- cd g
|
||||
- GOARCH=386 go test -v ./...
|
||||
- GOARCH=amd64 go test -v ./... -race -coverprofile=coverage.txt -covermode=atomic
|
||||
|
||||
after_success:
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
- bash <(curl -s https://codecov.io/bash)
|
||||
|
||||
|
||||
|
||||
|
||||
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
|
||||
|
||||
32
README.MD
32
README.MD
@ -1,28 +1,27 @@
|
||||
# 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://codecov.io/gh/gogf/gf/branch/master)
|
||||
[](https://github.com/gogf/gf)
|
||||
[](https://github.com/gogf/gf)
|
||||
[](https://github.com/gogf/gf/releases)
|
||||
|
||||
<!--
|
||||
[](https://codecov.io/gh/gogf/gf)
|
||||
[](https://www.codetriage.com/gogf/gf)
|
||||
-->
|
||||
GoFrame is a modular, loose-coupled, production-ready and most-powerful application development framework of golang. Providing a series of core components and dozens of practical modules, such as: cache, logging, containers, timer, validator, database orm, etc. Supporting web server integrated with router, cookie, session, logger, configure, template, https, hooks, rewrites and many more features.
|
||||
-->
|
||||
|
||||
`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, production-ready and most-powerful application development framework of golang. Providing a series of core components and dozens of practical modules, such as: memcache, configure, validator, logging, array/queue/set/map containers, timer/timing tasks, file/memory lock, object pool, database ORM, etc. Supporting web server integrated with router, cookie, session, logger, template, https, hooks, rewrites 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 +31,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 +45,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 +58,7 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
[View More..](https://gfer.me/start/index)
|
||||
[View More..](https://goframe.org/start/index)
|
||||
|
||||
|
||||
# License
|
||||
@ -71,7 +71,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 +84,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 +92,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>
|
||||
|
||||
40
README_ZH.MD
40
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,32 +74,42 @@ func main() {
|
||||
}
|
||||
```
|
||||
|
||||
[更多..](https://gfer.me/start/index)
|
||||
[更多..](https://goframe.org/start/index)
|
||||
|
||||
|
||||
# 协议
|
||||
|
||||
`GF` 使用非常友好的 [MIT](LICENSE) 开源协议进行发布,永久`100%`开源免费。
|
||||
|
||||
# 捐赠
|
||||
|
||||
捐赠支持`GF`框架的研发,
|
||||
请在捐赠时备注您的`github`/`gitee`账号名称。
|
||||
|
||||
<a href="https://goframe.org/images/donate.png" target="_blank">
|
||||
<img src="https://goframe.org/images/donate.png" width="300"/>
|
||||
</a>
|
||||
|
||||
# 贡献者(TOP 10)
|
||||
|
||||
<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>
|
||||
|
||||
77
RELEASE.MD
77
RELEASE.MD
@ -1,3 +1,38 @@
|
||||
# `v1.5.8` (2019-02-28)
|
||||
|
||||
## 新特性
|
||||
1. 主库从`gitee`迁移到了`github`( https://github.com/gogf/gf ),`gitee`作为镜像站,用于国内的代码贡献及ISSUE提交,迁移说明详见:https://goframe.org/upgradeto150
|
||||
1. 对常用的`container`数组模块: `garray`做了大量改进/完善工作,新增大量常用方法,并完善单元测试用例及方法注释,详见API文档:https://godoc.org/github.com/gogf/gf/g/container/garray
|
||||
1. 对常用的`container`集合模块: `gset`做了大量改进/完善工作,新增大量常用方法,并完善单元测试用例及方法注释,详见API文档:https://godoc.org/github.com/gogf/gf/g/container/gset
|
||||
1. 对常用的`container`MAP模块: `gmap`做了大量改进/完善工作,新增大量常用方法,并完善单元测试用例及方法注释,详见API文档:https://godoc.org/github.com/gogf/gf/g/container/gmap
|
||||
1. 对常用的字符串模块: `gstr`做了大量改进/完善工作,新增大量常用方法,并完善单元测试用例及方法注释,详见API文档:https://godoc.org/github.com/gogf/gf/g/text/gstr
|
||||
1. 改进`gform`中对`struct`/`*struct`参数的支持,`*Insert/*Save/*Replace/*Update/Where/Data`方法的参数调整为`interface{}`类型,并支持任意类型的: `string/map/slice/struct/*struct`参数传递,具体请参考:https://goframe.org/database/orm/chaining
|
||||
1. 新增/完善若干模块的单元测试用例, 包括:`gvalid`/`gregex`/`garray`/`gset`/`gmap`/`gstr`/`gconv`/`ghttp`/`gdb`;
|
||||
1. 由于`gkafka`模块比较重,且不是框架核心模块,因此将该模块迁移到新的仓库中独立管理,并去掉相关依赖包:https://github.com/gogf/gkafka
|
||||
1. 新增`greuseport`模块,用以实现TCP的`REUSEPORT`特性:https://godoc.org/github.com/gogf/gf/g/net/greuseport
|
||||
|
||||
## 新功能/改进
|
||||
1. 去掉模板引擎内置变量中自动初始化`session`对象带来的内存占用问题;
|
||||
1. `ghttp.Client`改进,增加若干方法,详见:https://goframe.org/net/ghttp/client
|
||||
1. `ghttp`分组路由增加`COMMON`方法,用以注册常用的`HTTP METHOD`(`GET/PUT/POST/DELETE`)路由;
|
||||
1. 更新框架依赖的`golang.org/x/sys`模块;
|
||||
1. 改进`gform`的批量操作(`Batch*`操作)返回结果对象,可以通过该结果对象获得批量操作准确的受影响记录行数;
|
||||
1. 将`gstr`/`gregex`模块从`util`分类迁移到了`text`分类目录下;
|
||||
1. 将`gtest`模块从`util`分类迁移到了`test`分类目录下;
|
||||
1. 完善`glog`方法注释;
|
||||
|
||||
## Bug Fix
|
||||
1. 修复带点的邮件格式,用`gvalid.Check`的"`email`"规则不能匹配成功;
|
||||
1. 修复`gvalid.Check`在`regex`规则下的检查失败问题;
|
||||
1. 修复`gcron`模块定时规则中天和周不允许`?`符号的问题;
|
||||
1. 修复`ghttp.Server`在部分异常情况下仍然返回`200`状态码的问题;
|
||||
1. 修复`gfpool`模块中由于原子操作问题造成的高并发"内存泄露"问题;
|
||||
1. 修复分组路由注册对象/控制时,方法`Index`的路由仅能通过`/xxx/index`访问的问题;
|
||||
1. 修复模板引擎使用中,当不存在`config.toml`(即使没使用)配置文件时的报错问题;
|
||||
1. 其他一些修复;
|
||||
|
||||
|
||||
|
||||
# `v1.4.6` (2019-01-24)
|
||||
|
||||
## 新特性
|
||||
@ -31,20 +66,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 +102,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 +123,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 +134,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 +378,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);
|
||||
|
||||
|
||||
|
||||
16
TODO.MD
16
TODO.MD
@ -46,7 +46,21 @@
|
||||
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参数传入;
|
||||
1. gfcache依旧使用gcache作为缓存控制对象,不要使用gmap;
|
||||
1. 增加对ghttp路由注册的{.struct}/{.method}单元测试;
|
||||
1. 更新跨域请求CORS相关功能文档;
|
||||
1. ghttp的热重启的本地进程端口监听,在不使用该特性时默认关闭掉;
|
||||
1. gcfg包目前允许添加重复的目录路径,需要在SetPath/AddPath时判断重复性,不能添加重复的路径;
|
||||
|
||||
|
||||
|
||||
|
||||
|
||||
# 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...)
|
||||
}
|
||||
20
g/container/garray/garray_func.go
Normal file
20
g/container/garray/garray_func.go
Normal file
@ -0,0 +1,20 @@
|
||||
// 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
|
||||
|
||||
|
||||
type apiSliceInterface interface {
|
||||
Slice() []interface{}
|
||||
}
|
||||
|
||||
type apiSliceInt interface {
|
||||
Slice() []int
|
||||
}
|
||||
|
||||
type apiSliceString interface {
|
||||
Slice() []string
|
||||
}
|
||||
@ -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)
|
||||
}
|
||||
644
g/container/garray/garray_normal_int.go
Normal file
644
g/container/garray/garray_normal_int.go
Normal file
@ -0,0 +1,644 @@
|
||||
// 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 (
|
||||
"bytes"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/util/grand"
|
||||
"math"
|
||||
"sort"
|
||||
)
|
||||
|
||||
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,
|
||||
}
|
||||
}
|
||||
|
||||
// Create an array from a copy of 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 NewIntArrayFromCopy(array []int, unsafe...bool) *IntArray {
|
||||
newArray := make([]int, len(array))
|
||||
copy(newArray, array)
|
||||
return &IntArray{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
array : newArray,
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// PopRand picks an random item out of array.
|
||||
//
|
||||
// 随机将一个数据项移出数组,并返回该数据项。
|
||||
func (a *IntArray) PopRand() int {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands picks <size> items out of array.
|
||||
//
|
||||
// 随机将size个数据项移出数组,并返回该数据项。
|
||||
func (a *IntArray) PopRands(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size > len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
array := make([]int, size)
|
||||
for i := 0; i < size; i++ {
|
||||
index := grand.Intn(len(a.array))
|
||||
array[i] = a.array[index]
|
||||
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
|
||||
}
|
||||
return 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. The parameter <array> can be any garray type or slice type.
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more variable types.
|
||||
//
|
||||
// 合并两个数组, 支持任意的garray数组类型及slice类型.
|
||||
func (a *IntArray) Merge(array interface{}) *IntArray {
|
||||
switch v := array.(type) {
|
||||
case *Array: a.Append(gconv.Ints(v.Slice())...)
|
||||
case *IntArray: a.Append(gconv.Ints(v.Slice())...)
|
||||
case *StringArray: a.Append(gconv.Ints(v.Slice())...)
|
||||
case *SortedArray: a.Append(gconv.Ints(v.Slice())...)
|
||||
case *SortedIntArray: a.Append(gconv.Ints(v.Slice())...)
|
||||
case *SortedStringArray: a.Append(gconv.Ints(v.Slice())...)
|
||||
default:
|
||||
a.Append(gconv.Ints(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:]
|
||||
}
|
||||
}
|
||||
|
||||
// Rand gets one random entry from array.
|
||||
//
|
||||
// 从数组中随机获得1个元素项(不删除)。
|
||||
func (a *IntArray) Rand() int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
|
||||
// Rands gets one or more random entries from array(a copy).
|
||||
//
|
||||
// 从数组中随机拷贝size个元素项,构成slice返回。
|
||||
func (a *IntArray) Rands(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()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(gconv.String(v))
|
||||
if k != len(a.array) - 1 {
|
||||
buffer.WriteString(glue)
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
657
g/container/garray/garray_normal_interface.go
Normal file
657
g/container/garray/garray_normal_interface.go
Normal file
@ -0,0 +1,657 @@
|
||||
// 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 (
|
||||
"bytes"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/internal/rwmutex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"github.com/gogf/gf/g/util/grand"
|
||||
"math"
|
||||
"sort"
|
||||
)
|
||||
|
||||
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.
|
||||
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),
|
||||
}
|
||||
}
|
||||
|
||||
// See NewArrayFrom.
|
||||
func NewFrom(array []interface{}, unsafe...bool) *Array {
|
||||
return NewArrayFrom(array, unsafe...)
|
||||
}
|
||||
|
||||
// See NewArrayFromCopy.
|
||||
func NewFromCopy(array []interface{}, unsafe...bool) *Array {
|
||||
return NewArrayFromCopy(array, unsafe...)
|
||||
}
|
||||
|
||||
// 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,
|
||||
}
|
||||
}
|
||||
|
||||
// Create an array from a copy of 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 NewArrayFromCopy(array []interface{}, unsafe...bool) *Array {
|
||||
newArray := make([]interface{}, len(array))
|
||||
copy(newArray, array)
|
||||
return &Array{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
array : newArray,
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// PopRand picks an random item out of array.
|
||||
//
|
||||
// 随机将一个数据项移出数组,并返回该数据项。
|
||||
func (a *Array) PopRand() interface{} {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands picks <size> items out of array.
|
||||
//
|
||||
// 随机将size个数据项移出数组,并返回该数据项。
|
||||
func (a *Array) PopRands(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size > len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
array := make([]interface{}, size)
|
||||
for i := 0; i < size; i++ {
|
||||
index := grand.Intn(len(a.array))
|
||||
array[i] = a.array[index]
|
||||
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
|
||||
}
|
||||
return 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. The parameter <array> can be any garray type or slice type.
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more variable types.
|
||||
//
|
||||
// 合并两个数组, 支持任意的garray数组类型及slice类型.
|
||||
func (a *Array) Merge(array interface{}) *Array {
|
||||
switch v := array.(type) {
|
||||
case *Array: a.Append(gconv.Interfaces(v.Slice())...)
|
||||
case *IntArray: a.Append(gconv.Interfaces(v.Slice())...)
|
||||
case *StringArray: a.Append(gconv.Interfaces(v.Slice())...)
|
||||
case *SortedArray: a.Append(gconv.Interfaces(v.Slice())...)
|
||||
case *SortedIntArray: a.Append(gconv.Interfaces(v.Slice())...)
|
||||
case *SortedStringArray: a.Append(gconv.Interfaces(v.Slice())...)
|
||||
default:
|
||||
a.Append(gconv.Interfaces(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:]
|
||||
}
|
||||
}
|
||||
|
||||
// Rand gets one random entry from array.
|
||||
//
|
||||
// 从数组中随机获得1个元素项(不删除)。
|
||||
func (a *Array) Rand() interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
|
||||
// Rands gets one or more random entries from array(a copy).
|
||||
//
|
||||
// 从数组中随机拷贝size个元素项,构成slice返回。
|
||||
func (a *Array) Rands(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()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(gconv.String(v))
|
||||
if k != len(a.array) - 1 {
|
||||
buffer.WriteString(glue)
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// String returns current array as a string.
|
||||
//
|
||||
// 将当前数组转换为字符串返回。
|
||||
func (a *Array) String() string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return fmt.Sprint(a.array)
|
||||
}
|
||||
645
g/container/garray/garray_normal_string.go
Normal file
645
g/container/garray/garray_normal_string.go
Normal file
@ -0,0 +1,645 @@
|
||||
// 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 (
|
||||
"bytes"
|
||||
"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,
|
||||
}
|
||||
}
|
||||
|
||||
// Create an array from a copy of 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 NewStringArrayFromCopy(array []string, unsafe...bool) *StringArray {
|
||||
newArray := make([]string, len(array))
|
||||
copy(newArray, array)
|
||||
return &StringArray{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
array : newArray,
|
||||
}
|
||||
}
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
// PopRand picks an random item out of array.
|
||||
//
|
||||
// 随机将一个数据项移出数组,并返回该数据项。
|
||||
func (a *StringArray) PopRand() string {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands picks <size> items out of array.
|
||||
//
|
||||
// 随机将size个数据项移出数组,并返回该数据项。
|
||||
func (a *StringArray) PopRands(size int) []string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size > len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
array := make([]string, size)
|
||||
for i := 0; i < size; i++ {
|
||||
index := grand.Intn(len(a.array))
|
||||
array[i] = a.array[index]
|
||||
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
|
||||
}
|
||||
return 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. The parameter <array> can be any garray type or slice type.
|
||||
// The difference between Merge and Append is Append supports only specified slice type,
|
||||
// but Merge supports more variable types.
|
||||
//
|
||||
// 合并两个数组, 支持任意的garray数组类型及slice类型.
|
||||
func (a *StringArray) Merge(array interface{}) *StringArray {
|
||||
switch v := array.(type) {
|
||||
case *Array: a.Append(gconv.Strings(v.Slice())...)
|
||||
case *IntArray: a.Append(gconv.Strings(v.Slice())...)
|
||||
case *StringArray: a.Append(gconv.Strings(v.Slice())...)
|
||||
case *SortedArray: a.Append(gconv.Strings(v.Slice())...)
|
||||
case *SortedIntArray: a.Append(gconv.Strings(v.Slice())...)
|
||||
case *SortedStringArray: a.Append(gconv.Strings(v.Slice())...)
|
||||
default:
|
||||
a.Append(gconv.Strings(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:]
|
||||
}
|
||||
}
|
||||
|
||||
// Rand gets one random entry from array.
|
||||
//
|
||||
// 从数组中随机获得1个元素项(不删除)。
|
||||
func (a *StringArray) Rand() string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
|
||||
// Rands gets one or more random entries from array(a copy).
|
||||
//
|
||||
// 从数组中随机拷贝size个元素项,构成slice返回。
|
||||
func (a *StringArray) Rands(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()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(gconv.String(v))
|
||||
if k != len(a.array) - 1 {
|
||||
buffer.WriteString(glue)
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
|
||||
@ -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"
|
||||
"bytes"
|
||||
"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"
|
||||
)
|
||||
|
||||
// 默认按照从低到高进行排序
|
||||
// 默认按照从小到大进行排序
|
||||
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,59 @@ 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
|
||||
}
|
||||
|
||||
// Create an array from a copy of 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 NewSortedIntArrayFromCopy(array []int, unsafe...bool) *SortedIntArray {
|
||||
newArray := make([]int, len(array))
|
||||
copy(newArray, array)
|
||||
return &SortedIntArray{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
array : newArray,
|
||||
}
|
||||
}
|
||||
|
||||
// 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 +128,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 +141,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 +163,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 +174,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 +186,94 @@ func (a *SortedIntArray) PopRight() int {
|
||||
return value
|
||||
}
|
||||
|
||||
// 数组长度
|
||||
// PopRand picks an random item out of array.
|
||||
//
|
||||
// 随机将一个数据项移出数组,并返回该数据项。
|
||||
func (a *SortedIntArray) PopRand() int {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands picks <size> items out of array.
|
||||
//
|
||||
// 随机将size个数据项移出数组,并返回该数据项。
|
||||
func (a *SortedIntArray) PopRands(size int) []int {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size > len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
array := make([]int, size)
|
||||
for i := 0; i < size; i++ {
|
||||
index := grand.Intn(len(a.array))
|
||||
array[i] = a.array[index]
|
||||
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
|
||||
}
|
||||
return 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 +281,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
|
||||
@ -156,27 +344,34 @@ func (a *SortedIntArray) binSearch(value int, lock bool) (index int, result int)
|
||||
for min <= max {
|
||||
mid = int((min + max) / 2)
|
||||
cmp = a.compareFunc(value, a.array[mid])
|
||||
switch cmp {
|
||||
case -1 : max = mid - 1
|
||||
case 1 : min = mid + 1
|
||||
case 0 :
|
||||
switch {
|
||||
case cmp < 0 : max = mid - 1
|
||||
case cmp > 0 : min = mid + 1
|
||||
default :
|
||||
return mid, cmp
|
||||
}
|
||||
}
|
||||
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 +385,159 @@ 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. The parameter <array> can be any garray type or slice type.
|
||||
// The difference between Merge and Add is Add supports only specified slice type,
|
||||
// but Merge supports more variable types.
|
||||
//
|
||||
// 合并两个数组, 支持任意的garray数组类型及slice类型.
|
||||
func (a *SortedIntArray) Merge(array interface{}) *SortedIntArray {
|
||||
switch v := array.(type) {
|
||||
case *Array: a.Add(gconv.Ints(v.Slice())...)
|
||||
case *IntArray: a.Add(gconv.Ints(v.Slice())...)
|
||||
case *StringArray: a.Add(gconv.Ints(v.Slice())...)
|
||||
case *SortedArray: a.Add(gconv.Ints(v.Slice())...)
|
||||
case *SortedIntArray: a.Add(gconv.Ints(v.Slice())...)
|
||||
case *SortedStringArray: a.Add(gconv.Ints(v.Slice())...)
|
||||
default:
|
||||
a.Add(gconv.Ints(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:]
|
||||
}
|
||||
}
|
||||
|
||||
// Rand gets one random entry from array.
|
||||
//
|
||||
// 从数组中随机获得1个元素项(不删除)。
|
||||
func (a *SortedIntArray) Rand() int {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
|
||||
// Rands gets one or more random entries from array(a copy).
|
||||
//
|
||||
// 从数组中随机拷贝size个元素项,构成slice返回。
|
||||
func (a *SortedIntArray) Rands(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()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(gconv.String(v))
|
||||
if k != len(a.array) - 1 {
|
||||
buffer.WriteString(glue)
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
@ -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"
|
||||
"bytes"
|
||||
"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"
|
||||
)
|
||||
|
||||
// 默认按照从低到高进行排序
|
||||
// 默认按照从小到大进行排序
|
||||
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,65 @@ 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
|
||||
}
|
||||
|
||||
// Create an array from a copy of 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 NewSortedArrayFromCopy(array []interface{}, unsafe...bool) *SortedArray {
|
||||
newArray := make([]interface{}, len(array))
|
||||
copy(newArray, array)
|
||||
return &SortedArray{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
array : newArray,
|
||||
}
|
||||
}
|
||||
|
||||
// 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 +134,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 +147,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 +169,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 +180,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 +192,106 @@ func (a *SortedArray) PopRight() interface{} {
|
||||
return value
|
||||
}
|
||||
|
||||
// 数组长度
|
||||
// PopRand picks an random item out of array.
|
||||
//
|
||||
// 随机将一个数据项移出数组,并返回该数据项。
|
||||
func (a *SortedArray) PopRand() interface{} {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands picks <size> items out of array.
|
||||
//
|
||||
// 随机将size个数据项移出数组,并返回该数据项。
|
||||
func (a *SortedArray) PopRands(size int) []interface{} {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size > len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
array := make([]interface{}, size)
|
||||
for i := 0; i < size; i++ {
|
||||
index := grand.Intn(len(a.array))
|
||||
array[i] = a.array[index]
|
||||
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
|
||||
}
|
||||
return 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 +299,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
|
||||
@ -149,27 +351,34 @@ func (a *SortedArray) binSearch(value interface{}, lock bool)(index int, result
|
||||
for min <= max {
|
||||
mid = int((min + max) / 2)
|
||||
cmp = a.compareFunc(value, a.array[mid])
|
||||
switch cmp {
|
||||
case -1 : max = mid - 1
|
||||
case 1 : min = mid + 1
|
||||
case 0 :
|
||||
switch {
|
||||
case cmp < 0 : max = mid - 1
|
||||
case cmp > 0 : min = mid + 1
|
||||
default :
|
||||
return mid, cmp
|
||||
}
|
||||
}
|
||||
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 +392,159 @@ 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. The parameter <array> can be any garray type or slice type.
|
||||
// The difference between Merge and Add is Add supports only specified slice type,
|
||||
// but Merge supports more variable types.
|
||||
//
|
||||
// 合并两个数组, 支持任意的garray数组类型及slice类型.
|
||||
func (a *SortedArray) Merge(array interface{}) *SortedArray {
|
||||
switch v := array.(type) {
|
||||
case *Array: a.Add(gconv.Interfaces(v.Slice())...)
|
||||
case *IntArray: a.Add(gconv.Interfaces(v.Slice())...)
|
||||
case *StringArray: a.Add(gconv.Interfaces(v.Slice())...)
|
||||
case *SortedArray: a.Add(gconv.Interfaces(v.Slice())...)
|
||||
case *SortedIntArray: a.Add(gconv.Interfaces(v.Slice())...)
|
||||
case *SortedStringArray: a.Add(gconv.Interfaces(v.Slice())...)
|
||||
default:
|
||||
a.Add(gconv.Interfaces(array)...)
|
||||
}
|
||||
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:]
|
||||
}
|
||||
}
|
||||
|
||||
// Rand gets one random entry from array.
|
||||
//
|
||||
// 从数组中随机获得1个元素项(不删除)。
|
||||
func (a *SortedArray) Rand() interface{} {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
|
||||
// Rands gets one or more random entries from array(a copy).
|
||||
//
|
||||
// 从数组中随机拷贝size个元素项,构成slice返回。
|
||||
func (a *SortedArray) Rands(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()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(gconv.String(v))
|
||||
if k != len(a.array) - 1 {
|
||||
buffer.WriteString(glue)
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
@ -1,27 +1,45 @@
|
||||
// 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"
|
||||
"bytes"
|
||||
"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 +50,59 @@ 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
|
||||
}
|
||||
|
||||
// Create an array from a copy of 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 NewSortedStringArrayFromCopy(array []string, unsafe...bool) *SortedStringArray {
|
||||
newArray := make([]string, len(array))
|
||||
copy(newArray, array)
|
||||
return &SortedStringArray{
|
||||
mu : rwmutex.New(unsafe...),
|
||||
array : newArray,
|
||||
}
|
||||
}
|
||||
|
||||
// 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 +123,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 +136,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 +158,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 +169,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 +181,106 @@ func (a *SortedStringArray) PopRight() string {
|
||||
return value
|
||||
}
|
||||
|
||||
// 数组长度
|
||||
// PopRand picks an random item out of array.
|
||||
//
|
||||
// 随机将一个数据项移出数组,并返回该数据项。
|
||||
func (a *SortedStringArray) PopRand() string {
|
||||
return a.Remove(grand.Intn(len(a.array)))
|
||||
}
|
||||
|
||||
// PopRands picks <size> items out of array.
|
||||
//
|
||||
// 随机将size个数据项移出数组,并返回该数据项。
|
||||
func (a *SortedStringArray) PopRands(size int) []string {
|
||||
a.mu.Lock()
|
||||
defer a.mu.Unlock()
|
||||
if size > len(a.array) {
|
||||
size = len(a.array)
|
||||
}
|
||||
array := make([]string, size)
|
||||
for i := 0; i < size; i++ {
|
||||
index := grand.Intn(len(a.array))
|
||||
array[i] = a.array[index]
|
||||
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
|
||||
}
|
||||
return 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 +288,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
|
||||
@ -150,27 +339,34 @@ func (a *SortedStringArray) binSearch(value string, lock bool) (index int, resul
|
||||
for min <= max {
|
||||
mid = int((min + max) / 2)
|
||||
cmp = a.compareFunc(value, a.array[mid])
|
||||
switch cmp {
|
||||
case -1 : max = mid - 1
|
||||
case 1 : min = mid + 1
|
||||
case 0 :
|
||||
switch {
|
||||
case cmp < 0 : max = mid - 1
|
||||
case cmp > 0 : min = mid + 1
|
||||
default :
|
||||
return mid, cmp
|
||||
}
|
||||
}
|
||||
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 +380,159 @@ 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. The parameter <array> can be any garray type or slice type.
|
||||
// The difference between Merge and Add is Add supports only specified slice type,
|
||||
// but Merge supports more variable types.
|
||||
//
|
||||
// 合并两个数组, 支持任意的garray数组类型及slice类型.
|
||||
func (a *SortedStringArray) Merge(array interface{}) *SortedStringArray {
|
||||
switch v := array.(type) {
|
||||
case *Array: a.Add(gconv.Strings(v.Slice())...)
|
||||
case *IntArray: a.Add(gconv.Strings(v.Slice())...)
|
||||
case *StringArray: a.Add(gconv.Strings(v.Slice())...)
|
||||
case *SortedArray: a.Add(gconv.Strings(v.Slice())...)
|
||||
case *SortedIntArray: a.Add(gconv.Strings(v.Slice())...)
|
||||
case *SortedStringArray: a.Add(gconv.Strings(v.Slice())...)
|
||||
default:
|
||||
a.Add(gconv.Strings(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:]
|
||||
}
|
||||
}
|
||||
|
||||
// Rand gets one random entry from array.
|
||||
//
|
||||
// 从数组中随机获得1个元素项(不删除)。
|
||||
func (a *SortedStringArray) Rand() string {
|
||||
a.mu.RLock()
|
||||
defer a.mu.RUnlock()
|
||||
return a.array[grand.Intn(len(a.array))]
|
||||
}
|
||||
|
||||
// Rands gets one or more random entries from array(a copy).
|
||||
//
|
||||
// 从数组中随机拷贝size个元素项,构成slice返回。
|
||||
func (a *SortedStringArray) Rands(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()
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
for k, v := range a.array {
|
||||
buffer.WriteString(gconv.String(v))
|
||||
if k != len(a.array) - 1 {
|
||||
buffer.WriteString(glue)
|
||||
}
|
||||
}
|
||||
return buffer.String()
|
||||
}
|
||||
@ -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) {
|
||||
|
||||
111
g/container/garray/garray_z_example_test.go
Normal file
111
g/container/garray/garray_z_example_test.go
Normal file
@ -0,0 +1,111 @@
|
||||
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
|
||||
//
|
||||
// This Source Code Form is subject to the terms of the MIT License.
|
||||
// If a copy of the MIT was not distributed with this file,
|
||||
// You can obtain one at https://github.com/gogf/gf.
|
||||
|
||||
package garray_test
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/container/garray"
|
||||
)
|
||||
|
||||
func Example_basic() {
|
||||
// 创建普通的数组,默认并发安全(带锁)
|
||||
a := garray.New()
|
||||
|
||||
// 添加数据项
|
||||
for i := 0; i < 10; i++ {
|
||||
a.Append(i)
|
||||
}
|
||||
|
||||
// 获取当前数组长度
|
||||
fmt.Println(a.Len())
|
||||
|
||||
// 获取当前数据项列表
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// 获取指定索引项
|
||||
fmt.Println(a.Get(6))
|
||||
|
||||
// 查找指定数据项是否存在
|
||||
fmt.Println(a.Contains(6))
|
||||
fmt.Println(a.Contains(100))
|
||||
|
||||
// 在指定索引前插入数据项
|
||||
a.InsertAfter(9, 11)
|
||||
// 在指定索引后插入数据项
|
||||
a.InsertBefore(10, 10)
|
||||
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// 修改指定索引的数据项
|
||||
a.Set(0, 100)
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// 搜索数据项,返回搜索到的索引位置
|
||||
fmt.Println(a.Search(5))
|
||||
|
||||
// 删除指定索引的数据项
|
||||
a.Remove(0)
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// 清空数组
|
||||
fmt.Println(a.Slice())
|
||||
a.Clear()
|
||||
fmt.Println(a.Slice())
|
||||
|
||||
// Output:
|
||||
// 10
|
||||
// [0 1 2 3 4 5 6 7 8 9]
|
||||
// 6
|
||||
// true
|
||||
// false
|
||||
// [0 1 2 3 4 5 6 7 8 9 10 11]
|
||||
// [100 1 2 3 4 5 6 7 8 9 10 11]
|
||||
// 5
|
||||
// [1 2 3 4 5 6 7 8 9 10 11]
|
||||
// [1 2 3 4 5 6 7 8 9 10 11]
|
||||
// []
|
||||
}
|
||||
|
||||
func Example_rand() {
|
||||
array := garray.NewFrom([]interface{}{1,2,3,4,5,6,7,8,9})
|
||||
// 随机返回两个数据项(不删除)
|
||||
fmt.Println(array.Rands(2))
|
||||
fmt.Println(array.PopRand())
|
||||
}
|
||||
|
||||
func Example_pop() {
|
||||
array := garray.NewFrom([]interface{}{1,2,3,4,5,6,7,8,9})
|
||||
fmt.Println(array.PopLeft())
|
||||
fmt.Println(array.PopLefts(2))
|
||||
fmt.Println(array.PopRight())
|
||||
fmt.Println(array.PopRights(2))
|
||||
|
||||
// Output:
|
||||
// 1
|
||||
// [2 3]
|
||||
// 9
|
||||
// [7 8]
|
||||
}
|
||||
|
||||
func Example_merge() {
|
||||
array1 := garray.NewFrom([]interface{}{1,2})
|
||||
array2 := garray.NewFrom([]interface{}{3,4})
|
||||
slice1 := []interface{}{5,6}
|
||||
slice2 := []int{7,8}
|
||||
slice3 := []string{"9","0"}
|
||||
fmt.Println(array1.Slice())
|
||||
array1.Merge(array1)
|
||||
array1.Merge(array2)
|
||||
array1.Merge(slice1)
|
||||
array1.Merge(slice2)
|
||||
array1.Merge(slice3)
|
||||
fmt.Println(array1.Slice())
|
||||
|
||||
// Output:
|
||||
// [1 2]
|
||||
// [1 2 1 2 3 4 5 6 7 8 9 0]
|
||||
}
|
||||
@ -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++ {
|
||||
201
g/container/garray/garray_z_unit_int_test.go
Normal file
201
g/container/garray/garray_z_unit_int_test.go
Normal file
@ -0,0 +1,201 @@
|
||||
// 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.Rands(2)), 2)
|
||||
gtest.Assert(len(array1.Rands(10)), 7)
|
||||
gtest.AssertIN(array1.Rands(1)[0], a1)
|
||||
gtest.AssertIN(array1.Rand(), a1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestIntArray_PopRands(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []interface{}{100, 200, 300, 400, 500, 600}
|
||||
array := garray.NewFromCopy(a1)
|
||||
gtest.AssertIN(array.PopRands(2), 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")
|
||||
})
|
||||
}
|
||||
204
g/container/garray/garray_z_unit_interface_test.go
Normal file
204
g/container/garray/garray_z_unit_interface_test.go
Normal file
@ -0,0 +1,204 @@
|
||||
// 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_PopRands(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []interface{}{100, 200, 300, 400, 500, 600}
|
||||
array := garray.NewFromCopy(a1)
|
||||
gtest.AssertIN(array.PopRands(2), a1)
|
||||
})
|
||||
}
|
||||
|
||||
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.Rands(2)), 2)
|
||||
gtest.Assert(len(array1.Rands(10)), 7)
|
||||
gtest.AssertIN(array1.Rands(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")
|
||||
})
|
||||
}
|
||||
201
g/container/garray/garray_z_unit_string_test.go
Normal file
201
g/container/garray/garray_z_unit_string_test.go
Normal file
@ -0,0 +1,201 @@
|
||||
// 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.Rands(2)), "2")
|
||||
gtest.Assert(len(array1.Rands(10)), "7")
|
||||
gtest.AssertIN(array1.Rands(1)[0], a1)
|
||||
})
|
||||
}
|
||||
|
||||
func TestStringArray_PopRands(t *testing.T) {
|
||||
gtest.Case(t, func() {
|
||||
a1 := []interface{}{"100", "200", "300", "400", "500", "600"}
|
||||
array := garray.NewFromCopy(a1)
|
||||
gtest.AssertIN(array.PopRands(2), 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"
|
||||
)
|
||||
|
||||
@ -37,8 +37,8 @@ type DB interface {
|
||||
doQuery(link dbLink, query string, args ...interface{}) (rows *sql.Rows, err error)
|
||||
doExec(link dbLink, query string, args ...interface{}) (result sql.Result, err error)
|
||||
doPrepare(link dbLink, query string) (*sql.Stmt, error)
|
||||
doInsert(link dbLink, table string, data Map, option int) (result sql.Result, err error)
|
||||
doBatchInsert(link dbLink, table string, list List, batch int, option int) (result sql.Result, err error)
|
||||
doInsert(link dbLink, table string, data interface{}, option int, batch...int) (result sql.Result, err error)
|
||||
doBatchInsert(link dbLink, table string, list interface{}, option int, batch...int) (result sql.Result, err error)
|
||||
doUpdate(link dbLink, table string, data interface{}, condition interface{}, args ...interface{}) (result sql.Result, err error)
|
||||
doDelete(link dbLink, table string, condition interface{}, args ...interface{}) (result sql.Result, err error)
|
||||
|
||||
@ -47,7 +47,9 @@ type DB interface {
|
||||
GetOne(query string, args ...interface{}) (Record, error)
|
||||
GetValue(query string, args ...interface{}) (Value, error)
|
||||
GetCount(query string, args ...interface{}) (int, error)
|
||||
GetStruct(obj interface{}, query string, args ...interface{}) error
|
||||
GetStruct(objPointer interface{}, query string, args ...interface{}) error
|
||||
GetStructs(objPointerSlice interface{}, query string, args ...interface{}) error
|
||||
GetScan(objPointer interface{}, query string, args ...interface{}) error
|
||||
|
||||
// 创建底层数据库master/slave链接对象
|
||||
Master() (*sql.DB, error)
|
||||
@ -61,14 +63,14 @@ type DB interface {
|
||||
Begin() (*TX, error)
|
||||
|
||||
// 数据表插入/更新/保存操作
|
||||
Insert(table string, data Map) (sql.Result, error)
|
||||
Replace(table string, data Map) (sql.Result, error)
|
||||
Save(table string, data Map) (sql.Result, error)
|
||||
Insert(table string, data interface{}, batch...int) (sql.Result, error)
|
||||
Replace(table string, data interface{}, batch...int) (sql.Result, error)
|
||||
Save(table string, data interface{}, batch...int) (sql.Result, error)
|
||||
|
||||
// 数据表插入/更新/保存操作(批量)
|
||||
BatchInsert(table string, list List, batch int) (sql.Result, error)
|
||||
BatchReplace(table string, list List, batch int) (sql.Result, error)
|
||||
BatchSave(table string, list List, batch int) (sql.Result, error)
|
||||
BatchInsert(table string, list interface{}, batch...int) (sql.Result, error)
|
||||
BatchReplace(table string, list interface{}, batch...int) (sql.Result, error)
|
||||
BatchSave(table string, list interface{}, batch...int) (sql.Result, error)
|
||||
|
||||
// 数据修改/删除
|
||||
Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error)
|
||||
@ -149,8 +151,11 @@ const (
|
||||
OPTION_REPLACE = 1
|
||||
OPTION_SAVE = 2
|
||||
OPTION_IGNORE = 3
|
||||
// 默认批量操作的数量值(Batch*操作)
|
||||
gDEFAULT_BATCH_NUM = 10
|
||||
// 默认的连接池连接存活时间(秒)
|
||||
gDEFAULT_CONN_MAX_LIFE_TIME = 30
|
||||
|
||||
)
|
||||
|
||||
// 使用默认/指定分组配置进行连接,数据库集群配置项:default
|
||||
|
||||
@ -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/text/gregex"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
@ -165,13 +165,44 @@ func (bs *dbBase) GetOne(query string, args ...interface{}) (Record, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 数据库查询,获取查询结果记录,自动映射数据到给定的struct对象中
|
||||
func (bs *dbBase) GetStruct(obj interface{}, query string, args ...interface{}) error {
|
||||
// 数据库查询,查询单条记录,自动映射数据到给定的struct对象中
|
||||
func (bs *dbBase) GetStruct(objPointer interface{}, query string, args ...interface{}) error {
|
||||
one, err := bs.GetOne(query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return one.ToStruct(obj)
|
||||
return one.ToStruct(objPointer)
|
||||
}
|
||||
|
||||
// 数据库查询,查询多条记录,并自动转换为指定的slice对象, 如: []struct/[]*struct。
|
||||
func (bs *dbBase) GetStructs(objPointerSlice interface{}, query string, args ...interface{}) error {
|
||||
all, err := bs.GetAll(query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return all.ToStructs(objPointerSlice)
|
||||
}
|
||||
|
||||
// 将结果转换为指定的struct/*struct/[]struct/[]*struct,
|
||||
// 参数应该为指针类型,否则返回失败。
|
||||
// 该方法自动识别参数类型,调用Struct/Structs方法。
|
||||
func (bs *dbBase) GetScan(objPointer interface{}, query string, args ...interface{}) error {
|
||||
t := reflect.TypeOf(objPointer)
|
||||
k := t.Kind()
|
||||
if k != reflect.Ptr {
|
||||
return fmt.Errorf("params should be type of pointer, but got: %v", k)
|
||||
}
|
||||
k = t.Elem().Kind()
|
||||
switch k {
|
||||
case reflect.Array:
|
||||
case reflect.Slice:
|
||||
return bs.db.GetStructs(objPointer, query, args ...)
|
||||
case reflect.Struct:
|
||||
return bs.db.GetStruct(objPointer, query, args ...)
|
||||
default:
|
||||
return fmt.Errorf("element type should be type of struct/slice, unsupported: %v", k)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 数据库查询,获取查询字段值
|
||||
@ -234,49 +265,76 @@ func (bs *dbBase) Begin() (*TX, error) {
|
||||
}
|
||||
}
|
||||
|
||||
// CURD操作:单条数据写入, 仅仅执行写入操作,如果存在冲突的主键或者唯一索引,那么报错返回
|
||||
func (bs *dbBase) Insert(table string, data Map) (sql.Result, error) {
|
||||
return bs.db.doInsert(nil, table, data, OPTION_INSERT)
|
||||
// CURD操作:单条数据写入, 仅仅执行写入操作,如果存在冲突的主键或者唯一索引,那么报错返回。
|
||||
// 参数data支持map/struct/*struct/slice类型,
|
||||
// 当为slice(例如[]map/[]struct/[]*struct)类型时,batch参数生效,并自动切换为批量操作。
|
||||
func (bs *dbBase) Insert(table string, data interface{}, batch...int) (sql.Result, error) {
|
||||
return bs.db.doInsert(nil, table, data, OPTION_INSERT, batch...)
|
||||
}
|
||||
|
||||
// CURD操作:单条数据写入, 如果数据存在(主键或者唯一索引),那么删除后重新写入一条
|
||||
func (bs *dbBase) Replace(table string, data Map) (sql.Result, error) {
|
||||
return bs.db.doInsert(nil, table, data, OPTION_REPLACE)
|
||||
// CURD操作:单条数据写入, 如果数据存在(主键或者唯一索引),那么删除后重新写入一条。
|
||||
// 参数data支持map/struct/*struct/slice类型,
|
||||
// 当为slice(例如[]map/[]struct/[]*struct)类型时,batch参数生效,并自动切换为批量操作。
|
||||
func (bs *dbBase) Replace(table string, data interface{}, batch...int) (sql.Result, error) {
|
||||
return bs.db.doInsert(nil, table, data, OPTION_REPLACE, batch...)
|
||||
}
|
||||
|
||||
// CURD操作:单条数据写入, 如果数据存在(主键或者唯一索引),那么更新,否则写入一条新数据
|
||||
func (bs *dbBase) Save(table string, data Map) (sql.Result, error) {
|
||||
return bs.db.doInsert(nil, table, data, OPTION_SAVE)
|
||||
// CURD操作:单条数据写入, 如果数据存在(主键或者唯一索引),那么更新,否则写入一条新数据。
|
||||
// 参数data支持map/struct/*struct/slice类型,
|
||||
// 当为slice(例如[]map/[]struct/[]*struct)类型时,batch参数生效,并自动切换为批量操作。
|
||||
func (bs *dbBase) Save(table string, data interface{}, batch...int) (sql.Result, error) {
|
||||
return bs.db.doInsert(nil, table, data, OPTION_SAVE, batch...)
|
||||
}
|
||||
|
||||
// insert、replace, save, ignore操作
|
||||
// 0: insert: 仅仅执行写入操作,如果存在冲突的主键或者唯一索引,那么报错返回
|
||||
// 1: replace: 如果数据存在(主键或者唯一索引),那么删除后重新写入一条
|
||||
// 2: save: 如果数据存在(主键或者唯一索引),那么更新,否则写入一条新数据
|
||||
// 3: ignore: 如果数据存在(主键或者唯一索引),那么什么也不做
|
||||
func (bs *dbBase) doInsert(link dbLink, table string, data Map, option int) (result sql.Result, err error) {
|
||||
var fields []string
|
||||
var values []string
|
||||
var params []interface{}
|
||||
charl, charr := bs.db.getChars()
|
||||
for k, v := range data {
|
||||
fields = append(fields, charl + k + charr)
|
||||
// 支持insert、replace, save, ignore操作。
|
||||
// 0: insert: 仅仅执行写入操作,如果存在冲突的主键或者唯一索引,那么报错返回;
|
||||
// 1: replace: 如果数据存在(主键或者唯一索引),那么删除后重新写入一条;
|
||||
// 2: save: 如果数据存在(主键或者唯一索引),那么更新,否则写入一条新数据;
|
||||
// 3: ignore: 如果数据存在(主键或者唯一索引),那么什么也不做;
|
||||
//
|
||||
// 参数data支持map/struct/*struct/slice类型,
|
||||
// 当为slice(例如[]map/[]struct/[]*struct)类型时,batch参数生效,并自动切换为批量操作。
|
||||
func (bs *dbBase) doInsert(link dbLink, table string, data interface{}, option int, batch...int) (result sql.Result, err error) {
|
||||
var fields []string
|
||||
var values []string
|
||||
var params []interface{}
|
||||
var dataMap Map
|
||||
// 使用反射判断data数据类型,如果为slice类型,那么自动转为批量操作
|
||||
rv := reflect.ValueOf(data)
|
||||
kind := rv.Kind()
|
||||
if kind == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
kind = rv.Kind()
|
||||
}
|
||||
switch kind {
|
||||
case reflect.Slice: fallthrough
|
||||
case reflect.Array:
|
||||
return bs.db.doBatchInsert(link, table, data, option, batch...)
|
||||
case reflect.Map: fallthrough
|
||||
case reflect.Struct:
|
||||
dataMap = Map(gconv.Map(data))
|
||||
default:
|
||||
return result, errors.New(fmt.Sprint("unsupported data type:", kind))
|
||||
}
|
||||
charL, charR := bs.db.getChars()
|
||||
for k, v := range dataMap {
|
||||
fields = append(fields, charL + k + charR)
|
||||
values = append(values, "?")
|
||||
params = append(params, v)
|
||||
}
|
||||
operation := getInsertOperationByOption(option)
|
||||
updatestr := ""
|
||||
updateStr := ""
|
||||
if option == OPTION_SAVE {
|
||||
var updates []string
|
||||
for k, _ := range data {
|
||||
for k, _ := range dataMap {
|
||||
updates = append(updates,
|
||||
fmt.Sprintf("%s%s%s=VALUES(%s%s%s)",
|
||||
charl, k, charr,
|
||||
charl, k, charr,
|
||||
charL, k, charR,
|
||||
charL, k, charR,
|
||||
),
|
||||
)
|
||||
}
|
||||
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,33 +343,60 @@ 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...)
|
||||
}
|
||||
|
||||
// CURD操作:批量数据指定批次量写入
|
||||
func (bs *dbBase) BatchInsert(table string, list List, batch int) (sql.Result, error) {
|
||||
return bs.db.doBatchInsert(nil, table, list, batch, OPTION_INSERT)
|
||||
func (bs *dbBase) BatchInsert(table string, list interface{}, batch...int) (sql.Result, error) {
|
||||
return bs.db.doBatchInsert(nil, table, list, OPTION_INSERT, batch...)
|
||||
}
|
||||
|
||||
// CURD操作:批量数据指定批次量写入, 如果数据存在(主键或者唯一索引),那么删除后重新写入一条
|
||||
func (bs *dbBase) BatchReplace(table string, list List, batch int) (sql.Result, error) {
|
||||
return bs.db.doBatchInsert(nil, table, list, batch, OPTION_REPLACE)
|
||||
func (bs *dbBase) BatchReplace(table string, list interface{}, batch...int) (sql.Result, error) {
|
||||
return bs.db.doBatchInsert(nil, table, list, OPTION_REPLACE, batch...)
|
||||
}
|
||||
|
||||
// CURD操作:批量数据指定批次量写入, 如果数据存在(主键或者唯一索引),那么更新,否则写入一条新数据
|
||||
func (bs *dbBase) BatchSave(table string, list List, batch int) (sql.Result, error) {
|
||||
return bs.db.doBatchInsert(nil, table, list, batch, OPTION_SAVE)
|
||||
func (bs *dbBase) BatchSave(table string, list interface{}, batch...int) (sql.Result, error) {
|
||||
return bs.db.doBatchInsert(nil, table, list, OPTION_SAVE, batch...)
|
||||
}
|
||||
|
||||
// 批量写入数据
|
||||
func (bs *dbBase) doBatchInsert(link dbLink, table string, list List, batch int, option int) (result sql.Result, err error) {
|
||||
var keys []string
|
||||
var values []string
|
||||
var bvalues []string
|
||||
var params []interface{}
|
||||
// 批量写入数据, 参数list支持slice类型,例如: []map/[]struct/[]*struct。
|
||||
func (bs *dbBase) doBatchInsert(link dbLink, table string, list interface{}, option int, batch...int) (result sql.Result, err error) {
|
||||
var keys []string
|
||||
var values []string
|
||||
var params []interface{}
|
||||
listMap := (List)(nil)
|
||||
switch v := list.(type) {
|
||||
case List:
|
||||
listMap = v
|
||||
case Map:
|
||||
listMap = List{v}
|
||||
default:
|
||||
rv := reflect.ValueOf(list)
|
||||
kind := rv.Kind()
|
||||
if kind == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
kind = rv.Kind()
|
||||
}
|
||||
switch kind {
|
||||
// 如果是slice,那么转换为List类型
|
||||
case reflect.Slice: fallthrough
|
||||
case reflect.Array:
|
||||
listMap = make(List, rv.Len())
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
listMap[i] = gconv.Map(rv.Index(i).Interface())
|
||||
}
|
||||
case reflect.Map: fallthrough
|
||||
case reflect.Struct:
|
||||
listMap = List{Map(gconv.Map(list))}
|
||||
default:
|
||||
return result, errors.New(fmt.Sprint("unsupported list type:", kind))
|
||||
}
|
||||
}
|
||||
// 判断长度
|
||||
if len(list) < 1 {
|
||||
if len(listMap) < 1 {
|
||||
return result, errors.New("empty data list")
|
||||
}
|
||||
if link == nil {
|
||||
@ -320,63 +405,79 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list List, batch int,
|
||||
}
|
||||
}
|
||||
// 首先获取字段名称及记录长度
|
||||
for k, _ := range list[0] {
|
||||
keys = append(keys, k)
|
||||
values = append(values, "?")
|
||||
holders := []string(nil)
|
||||
for k, _ := range listMap[0] {
|
||||
keys = append(keys, k)
|
||||
holders = append(holders, "?")
|
||||
}
|
||||
charl, charr := bs.db.getChars()
|
||||
keyStr := charl + strings.Join(keys, charl + "," + charr) + charr
|
||||
valueHolderStr := "(" + strings.Join(values, ",") + ")"
|
||||
batchResult := new(batchSqlResult)
|
||||
charL, charR := bs.db.getChars()
|
||||
keyStr := charL + strings.Join(keys, charL + "," + charR) + charR
|
||||
valueHolderStr := "(" + strings.Join(holders, ",") + ")"
|
||||
// 操作判断
|
||||
operation := getInsertOperationByOption(option)
|
||||
updatestr := ""
|
||||
updateStr := ""
|
||||
if option == OPTION_SAVE {
|
||||
var updates []string
|
||||
for _, k := range keys {
|
||||
updates = append(updates,
|
||||
fmt.Sprintf("%s%s%s=VALUES(%s%s%s)",
|
||||
charl, k, charr,
|
||||
charl, k, charr,
|
||||
charL, k, charR,
|
||||
charL, k, charR,
|
||||
),
|
||||
)
|
||||
}
|
||||
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++ {
|
||||
batchNum := gDEFAULT_BATCH_NUM
|
||||
if len(batch) > 0 {
|
||||
batchNum = batch[0]
|
||||
}
|
||||
for i := 0; i < len(listMap); i++ {
|
||||
for _, k := range keys {
|
||||
params = append(params, list[i][k])
|
||||
params = append(params, listMap[i][k])
|
||||
}
|
||||
bvalues = append(bvalues, valueHolderStr)
|
||||
if len(bvalues) == batch {
|
||||
values = append(values, valueHolderStr)
|
||||
if len(values) == batchNum {
|
||||
r, err := bs.db.doExec(link, fmt.Sprintf("%s INTO %s(%s) VALUES%s %s",
|
||||
operation, table, keyStr, strings.Join(bvalues, ","),
|
||||
updatestr),
|
||||
operation, table, keyStr, strings.Join(values, ","),
|
||||
updateStr),
|
||||
params...)
|
||||
if err != nil {
|
||||
return result, err
|
||||
return r, err
|
||||
}
|
||||
result = r
|
||||
params = params[:0]
|
||||
bvalues = bvalues[:0]
|
||||
if n, err := r.RowsAffected(); err != nil {
|
||||
return r, err
|
||||
} else {
|
||||
batchResult.lastResult = r
|
||||
batchResult.rowsAffected += n
|
||||
}
|
||||
params = params[:0]
|
||||
values = values[:0]
|
||||
}
|
||||
}
|
||||
// 处理最后不构成指定批量的数据
|
||||
if len(bvalues) > 0 {
|
||||
if len(values) > 0 {
|
||||
r, err := bs.db.doExec(link, fmt.Sprintf("%s INTO %s(%s) VALUES%s %s",
|
||||
operation, table, keyStr, strings.Join(bvalues, ","),
|
||||
updatestr),
|
||||
operation, table, keyStr, strings.Join(values, ","),
|
||||
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预处理
|
||||
// data参数支持字符串或者关联数组类型,内部会自行做判断处理
|
||||
// CURD操作:数据更新,统一采用sql预处理。
|
||||
// data参数支持string/map/struct/*struct类型。
|
||||
func (bs *dbBase) Update(table string, data interface{}, condition interface{}, args ...interface{}) (sql.Result, error) {
|
||||
link, err := bs.db.Master()
|
||||
if err != nil {
|
||||
@ -385,23 +486,30 @@ func (bs *dbBase) Update(table string, data interface{}, condition interface{},
|
||||
return bs.db.doUpdate(link, table, data, condition, args ...)
|
||||
}
|
||||
|
||||
// CURD操作:数据更新,统一采用sql预处理
|
||||
// data参数支持字符串或者关联数组类型,内部会自行做判断处理
|
||||
// CURD操作:数据更新,统一采用sql预处理。
|
||||
// data参数支持string/map/struct/*struct类型类型。
|
||||
func (bs *dbBase) doUpdate(link dbLink, table string, data interface{}, condition interface{}, args ...interface{}) (result sql.Result, err error) {
|
||||
params := ([]interface{})(nil)
|
||||
updates := ""
|
||||
charl, charr := bs.db.getChars()
|
||||
refValue := reflect.ValueOf(data)
|
||||
if refValue.Kind() == reflect.Map {
|
||||
var fields []string
|
||||
keys := refValue.MapKeys()
|
||||
for _, k := range keys {
|
||||
fields = append(fields, fmt.Sprintf("%s%s%s=?", charl, k, charr))
|
||||
params = append(params, gconv.String(refValue.MapIndex(k).Interface()))
|
||||
}
|
||||
updates = strings.Join(fields, ",")
|
||||
} else {
|
||||
updates = gconv.String(data)
|
||||
charL, charR := bs.db.getChars()
|
||||
// 使用反射进行类型判断
|
||||
rv := reflect.ValueOf(data)
|
||||
kind := rv.Kind()
|
||||
if kind == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
kind = rv.Kind()
|
||||
}
|
||||
switch kind {
|
||||
case reflect.Map: fallthrough
|
||||
case reflect.Struct:
|
||||
var fields []string
|
||||
for k, v := range gconv.Map(data) {
|
||||
fields = append(fields, fmt.Sprintf("%s%s%s=?", charL, k, charR))
|
||||
params = append(params, gconv.String(v))
|
||||
}
|
||||
updates = strings.Join(fields, ",")
|
||||
default:
|
||||
updates = gconv.String(data)
|
||||
}
|
||||
for _, v := range args {
|
||||
params = append(params, gconv.String(v))
|
||||
|
||||
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,44 +10,60 @@ 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/text/gregex"
|
||||
"github.com/gogf/gf/g/text/gstr"
|
||||
"github.com/gogf/gf/g/util/gconv"
|
||||
_ "github.com/gogf/gf/third/github.com/go-sql-driver/mysql"
|
||||
"reflect"
|
||||
"strings"
|
||||
)
|
||||
|
||||
// 格式化SQL查询条件
|
||||
func formatCondition(where interface{}, args []interface{}) (string, []interface{}) {
|
||||
func formatCondition(where interface{}, args []interface{}) (newWhere string, newArgs []interface{}) {
|
||||
// 条件字符串处理
|
||||
buffer := bytes.NewBuffer(nil)
|
||||
if reflect.ValueOf(where).Kind() == reflect.Map {
|
||||
ks := reflect.ValueOf(where).MapKeys()
|
||||
vs := reflect.ValueOf(where)
|
||||
for _, k := range ks {
|
||||
key := gconv.String(k.Interface())
|
||||
value := gconv.String(vs.MapIndex(k).Interface())
|
||||
if buffer.Len() > 0 {
|
||||
buffer.WriteString(" AND ")
|
||||
// 使用反射进行类型判断
|
||||
rv := reflect.ValueOf(where)
|
||||
kind := rv.Kind()
|
||||
if kind == reflect.Ptr {
|
||||
rv = rv.Elem()
|
||||
kind = rv.Kind()
|
||||
}
|
||||
switch kind {
|
||||
// 注意当where为map/struct类型时,args参数必须为空。
|
||||
case reflect.Map: fallthrough
|
||||
case reflect.Struct:
|
||||
for k, v := range gconv.Map(where) {
|
||||
if buffer.Len() > 0 {
|
||||
buffer.WriteString(" AND ")
|
||||
}
|
||||
// 支持slice键值/属性,这个时候作为IN查询
|
||||
switch reflect.ValueOf(v).Kind() {
|
||||
case reflect.Slice: fallthrough
|
||||
case reflect.Array:
|
||||
buffer.WriteString(k + " IN(?)")
|
||||
default:
|
||||
if gstr.Pos(k, "<") == -1 && gstr.Pos(k, ">") == -1 && gstr.Pos(k, "=") == -1 {
|
||||
buffer.WriteString(k + "=?")
|
||||
} else {
|
||||
buffer.WriteString(k + "?")
|
||||
}
|
||||
}
|
||||
// 当给定的Where参数为map/struct时,args参数必定为空,
|
||||
// 考虑到后续还会对args做处理,特别是判断slice类型,这里直接给args赋值。
|
||||
args = append(args, v)
|
||||
}
|
||||
if gstr.IsNumeric(value) || value == "?" {
|
||||
buffer.WriteString(key + "=" + value)
|
||||
} else {
|
||||
buffer.WriteString(key + "='" + value + "'")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
buffer.Write(gconv.Bytes(where))
|
||||
newWhere = buffer.String()
|
||||
default:
|
||||
buffer.WriteString(gconv.String(where))
|
||||
}
|
||||
if buffer.Len() == 0 {
|
||||
buffer.WriteString("1=1")
|
||||
}
|
||||
// 查询条件处理
|
||||
newWhere := buffer.String()
|
||||
newArgs := make([]interface{}, 0)
|
||||
// 查询条件参数处理,主要处理slice参数类型
|
||||
newWhere = buffer.String()
|
||||
if len(args) > 0 {
|
||||
for index, arg := range args {
|
||||
rv := reflect.ValueOf(arg)
|
||||
@ -57,11 +73,14 @@ func formatCondition(where interface{}, args []interface{}) (string, []interface
|
||||
kind = rv.Kind()
|
||||
}
|
||||
switch kind {
|
||||
// '?'占位符支持slice类型,
|
||||
// 这里会将slice参数拆散,并更新原有占位符'?'为多个'?',使用','符号连接。
|
||||
case reflect.Slice: fallthrough
|
||||
case reflect.Array:
|
||||
for i := 0; i < rv.Len(); i++ {
|
||||
newArgs = append(newArgs, rv.Index(i).Interface())
|
||||
}
|
||||
// counter用于匹配该参数的位置(与index对应)
|
||||
counter := 0
|
||||
newWhere, _ = gregex.ReplaceStringFunc(`\?`, newWhere, func(s string) string {
|
||||
counter++
|
||||
@ -75,7 +94,7 @@ func formatCondition(where interface{}, args []interface{}) (string, []interface
|
||||
}
|
||||
}
|
||||
}
|
||||
return newWhere, newArgs
|
||||
return
|
||||
}
|
||||
|
||||
// 打印SQL对象(仅在debug=true时有效)
|
||||
@ -109,13 +128,13 @@ func formatError(err error, query string, args ...interface{}) error {
|
||||
|
||||
// 根据insert选项获得操作名称
|
||||
func getInsertOperationByOption(option int) string {
|
||||
oper := "INSERT"
|
||||
operator := "INSERT"
|
||||
switch option {
|
||||
case OPTION_REPLACE:
|
||||
oper = "REPLACE"
|
||||
operator = "REPLACE"
|
||||
case OPTION_SAVE:
|
||||
case OPTION_IGNORE:
|
||||
oper = "INSERT IGNORE"
|
||||
operator = "INSERT IGNORE"
|
||||
}
|
||||
return oper
|
||||
return operator
|
||||
}
|
||||
|
||||
@ -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"
|
||||
)
|
||||
@ -35,6 +35,7 @@ type Model struct {
|
||||
cacheEnabled bool // 当前SQL操作是否开启查询缓存功能
|
||||
cacheTime int // 查询缓存时间
|
||||
cacheName string // 查询缓存名称
|
||||
alterable bool // 当前模型是否运行可修改模式(默认情况下链式操作不会修改当前模型,而是创建新的模型返回)
|
||||
}
|
||||
|
||||
// 链式操作,数据表字段,可支持多个表,以半角逗号连接
|
||||
@ -79,47 +80,69 @@ func (md *Model) Clone() *Model {
|
||||
return newModel
|
||||
}
|
||||
|
||||
// 标识当前对象可被修改。
|
||||
// 1. 默认情况下,模型对象的对象属性无法被修改,
|
||||
// 每一次链式操作都是克隆一个新的模型对象,这样所有的操作都不会污染模型对象。
|
||||
// 但是链式操作如果需要分开执行,那么需要将新的克隆对象赋值给旧的模型对象继续操作。
|
||||
// 2. 当标识模型对象为可修改,那么在当前模型对象的所有链式操作均会影响下一次的链式操作,
|
||||
// 即使是链式操作分开执行。
|
||||
// 3. 大部分ORM框架默认模型对象是可修改的,但是GF框架的ORM提供给开发者更灵活,更安全的链式操作选项。
|
||||
func (md *Model) Alterable() *Model {
|
||||
md.alterable = true
|
||||
return md
|
||||
}
|
||||
|
||||
// 返回操作的模型对象,可能是当前对象,也可能是新的克隆对象,根据alterable决定。
|
||||
func (md *Model) getModel() *Model {
|
||||
if md.alterable {
|
||||
return md
|
||||
} else {
|
||||
return md.Clone()
|
||||
}
|
||||
}
|
||||
|
||||
// 链式操作,左联表
|
||||
func (md *Model) LeftJoin(joinTable string, on string) (*Model) {
|
||||
model := md.Clone()
|
||||
model := md.getModel()
|
||||
model.tables += fmt.Sprintf(" LEFT JOIN %s ON (%s)", joinTable, on)
|
||||
return model
|
||||
}
|
||||
|
||||
// 链式操作,右联表
|
||||
func (md *Model) RightJoin(joinTable string, on string) (*Model) {
|
||||
model := md.Clone()
|
||||
model := md.getModel()
|
||||
model.tables += fmt.Sprintf(" RIGHT JOIN %s ON (%s)", joinTable, on)
|
||||
return model
|
||||
}
|
||||
|
||||
// 链式操作,内联表
|
||||
func (md *Model) InnerJoin(joinTable string, on string) (*Model) {
|
||||
model := md.Clone()
|
||||
model := md.getModel()
|
||||
model.tables += fmt.Sprintf(" INNER JOIN %s ON (%s)", joinTable, on)
|
||||
return model
|
||||
}
|
||||
|
||||
// 链式操作,查询字段
|
||||
func (md *Model) Fields(fields string) (*Model) {
|
||||
model := md.Clone()
|
||||
model := md.getModel()
|
||||
model.fields = fields
|
||||
return model
|
||||
}
|
||||
|
||||
// 链式操作,过滤字段
|
||||
func (md *Model) Filter() (*Model) {
|
||||
model := md.Clone()
|
||||
model := md.getModel()
|
||||
model.filter = true
|
||||
return model
|
||||
}
|
||||
|
||||
// 链式操作,condition,支持string & gdb.Map
|
||||
// 链式操作,condition,支持string & gdb.Map.
|
||||
// 注意,多个Where调用时,会相互覆盖,只有最后一个Where语句生效。
|
||||
func (md *Model) Where(where interface{}, args ...interface{}) (*Model) {
|
||||
model := md.Clone()
|
||||
model := md.getModel()
|
||||
newWhere, newArgs := formatCondition(where, args)
|
||||
model.where = newWhere
|
||||
model.whereArgs = append(model.whereArgs, newArgs...)
|
||||
model.whereArgs = newArgs
|
||||
// 支持 Where("uid", 1)这种格式
|
||||
if len(args) == 1 && strings.Index(model.where , "?") < 0 {
|
||||
model.where += "=?"
|
||||
@ -129,7 +152,7 @@ func (md *Model) Where(where interface{}, args ...interface{}) (*Model) {
|
||||
|
||||
// 链式操作,添加AND条件到Where中
|
||||
func (md *Model) And(where interface{}, args ...interface{}) (*Model) {
|
||||
model := md.Clone()
|
||||
model := md.getModel()
|
||||
newWhere, newArgs := formatCondition(where, args)
|
||||
model.where += " AND " + newWhere
|
||||
model.whereArgs = append(model.whereArgs, newArgs...)
|
||||
@ -138,7 +161,7 @@ func (md *Model) And(where interface{}, args ...interface{}) (*Model) {
|
||||
|
||||
// 链式操作,添加OR条件到Where中
|
||||
func (md *Model) Or(where interface{}, args ...interface{}) (*Model) {
|
||||
model := md.Clone()
|
||||
model := md.getModel()
|
||||
newWhere, newArgs := formatCondition(where, args)
|
||||
model.where += " OR " + newWhere
|
||||
model.whereArgs = append(model.whereArgs, newArgs...)
|
||||
@ -147,21 +170,21 @@ func (md *Model) Or(where interface{}, args ...interface{}) (*Model) {
|
||||
|
||||
// 链式操作,group by
|
||||
func (md *Model) GroupBy(groupBy string) (*Model) {
|
||||
model := md.Clone()
|
||||
model := md.getModel()
|
||||
model.groupBy = groupBy
|
||||
return model
|
||||
}
|
||||
|
||||
// 链式操作,order by
|
||||
func (md *Model) OrderBy(orderBy string) (*Model) {
|
||||
model := md.Clone()
|
||||
model := md.getModel()
|
||||
model.orderBy = orderBy
|
||||
return model
|
||||
}
|
||||
|
||||
// 链式操作,limit
|
||||
func (md *Model) Limit(start int, limit int) (*Model) {
|
||||
model := md.Clone()
|
||||
model := md.getModel()
|
||||
model.start = start
|
||||
model.limit = limit
|
||||
return model
|
||||
@ -170,7 +193,7 @@ func (md *Model) Limit(start int, limit int) (*Model) {
|
||||
// 链式操作,翻页
|
||||
// @author ymrjqyy
|
||||
func (md *Model) ForPage(page, limit int) (*Model) {
|
||||
model := md.Clone()
|
||||
model := md.getModel()
|
||||
model.start = (page - 1) * limit
|
||||
model.limit = limit
|
||||
return model
|
||||
@ -178,7 +201,7 @@ func (md *Model) ForPage(page, limit int) (*Model) {
|
||||
|
||||
// 设置批处理的大小
|
||||
func (md *Model) Batch(batch int) *Model {
|
||||
model := md.Clone()
|
||||
model := md.getModel()
|
||||
model.batch = batch
|
||||
return model
|
||||
}
|
||||
@ -188,7 +211,7 @@ func (md *Model) Batch(batch int) *Model {
|
||||
// name表示自定义的缓存名称,便于业务层精准定位缓存项(如果业务层需要手动清理时,必须指定缓存名称),
|
||||
// 例如:查询缓存时设置名称,清理缓存时可以给定清理的缓存名称进行精准清理。
|
||||
func (md *Model) Cache(time int, name ... string) *Model {
|
||||
model := md.Clone()
|
||||
model := md.getModel()
|
||||
model.cacheTime = time
|
||||
if len(name) > 0 {
|
||||
model.cacheName = name[0]
|
||||
@ -200,13 +223,14 @@ func (md *Model) Cache(time int, name ... string) *Model {
|
||||
return model
|
||||
}
|
||||
|
||||
// 链式操作,操作数据记录项,可以是string/Map, 也可以是:key,value,key,value,...
|
||||
func (md *Model) Data(data ...interface{}) (*Model) {
|
||||
model := md.Clone()
|
||||
// 链式操作,操作数据项,参数data类型支持 string/map/slice/struct/*struct ,
|
||||
// 也可以是:key,value,key,value,...。
|
||||
func (md *Model) Data(data ...interface{}) *Model {
|
||||
model := md.getModel()
|
||||
if len(data) > 1 {
|
||||
m := make(map[string]interface{})
|
||||
for i := 0; i < len(data); i += 2 {
|
||||
m[gconv.String(data[i])] = data[i+1]
|
||||
m[gconv.String(data[i])] = data[i + 1]
|
||||
}
|
||||
model.data = m
|
||||
} else {
|
||||
@ -223,6 +247,7 @@ func (md *Model) Data(data ...interface{}) (*Model) {
|
||||
kind = rv.Kind()
|
||||
}
|
||||
switch kind {
|
||||
// 如果是slice,那么转换为List类型
|
||||
case reflect.Slice: fallthrough
|
||||
case reflect.Array:
|
||||
list := make(List, rv.Len())
|
||||
@ -230,8 +255,9 @@ func (md *Model) Data(data ...interface{}) (*Model) {
|
||||
list[i] = gconv.Map(rv.Index(i).Interface())
|
||||
}
|
||||
model.data = list
|
||||
case reflect.Map:
|
||||
model.data = gconv.Map(data[0])
|
||||
case reflect.Map: fallthrough
|
||||
case reflect.Struct:
|
||||
model.data = Map(gconv.Map(data[0]))
|
||||
default:
|
||||
model.data = data[0]
|
||||
}
|
||||
@ -240,7 +266,9 @@ func (md *Model) Data(data ...interface{}) (*Model) {
|
||||
return model
|
||||
}
|
||||
|
||||
// 链式操作, CURD - Insert/BatchInsert
|
||||
// 链式操作, CURD - Insert/BatchInsert。
|
||||
// 根据Data方法传递的参数类型决定该操作是单条操作还是批量操作,
|
||||
// 如果Data方法传递的是slice类型,那么为批量操作。
|
||||
func (md *Model) Insert() (result sql.Result, err error) {
|
||||
defer func() {
|
||||
if err == nil {
|
||||
@ -279,7 +307,9 @@ func (md *Model) Insert() (result sql.Result, err error) {
|
||||
return nil, errors.New("inserting into table with invalid data type")
|
||||
}
|
||||
|
||||
// 链式操作, CURD - Replace/BatchReplace
|
||||
// 链式操作, CURD - Replace/BatchReplace。
|
||||
// 根据Data方法传递的参数类型决定该操作是单条操作还是批量操作,
|
||||
// 如果Data方法传递的是slice类型,那么为批量操作。
|
||||
func (md *Model) Replace() (result sql.Result, err error) {
|
||||
defer func() {
|
||||
if err == nil {
|
||||
@ -318,7 +348,9 @@ func (md *Model) Replace() (result sql.Result, err error) {
|
||||
return nil, errors.New("replacing into table with invalid data type")
|
||||
}
|
||||
|
||||
// 链式操作, CURD - Save/BatchSave
|
||||
// 链式操作, CURD - Save/BatchSave。
|
||||
// 根据Data方法传递的参数类型决定该操作是单条操作还是批量操作,
|
||||
// 如果Data方法传递的是slice类型,那么为批量操作。
|
||||
func (md *Model) Save() (result sql.Result, err error) {
|
||||
defer func() {
|
||||
if err == nil {
|
||||
@ -330,7 +362,7 @@ func (md *Model) Save() (result sql.Result, err error) {
|
||||
}
|
||||
// 批量操作
|
||||
if list, ok := md.data.(List); ok {
|
||||
batch := 10
|
||||
batch := gDEFAULT_BATCH_NUM
|
||||
if md.batch > 0 {
|
||||
batch = md.batch
|
||||
}
|
||||
@ -429,13 +461,44 @@ func (md *Model) Value() (Value, error) {
|
||||
return nil, nil
|
||||
}
|
||||
|
||||
// 链式操作,查询单条记录,并自动转换为struct对象
|
||||
func (md *Model) Struct(obj interface{}) error {
|
||||
// 链式操作,查询单条记录,并自动转换为struct对象, 参数必须为对象的指针,不能为空指针。
|
||||
func (md *Model) Struct(objPointer interface{}) error {
|
||||
one, err := md.One()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return one.ToStruct(obj)
|
||||
return one.ToStruct(objPointer)
|
||||
}
|
||||
|
||||
// 链式操作,查询多条记录,并自动转换为指定的slice对象, 如: []struct/[]*struct。
|
||||
func (md *Model) Structs(objPointerSlice interface{}) error {
|
||||
r, err := md.All()
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return r.ToStructs(objPointerSlice)
|
||||
}
|
||||
|
||||
// 链式操作,将结果转换为指定的struct/*struct/[]struct/[]*struct,
|
||||
// 参数应该为指针类型,否则返回失败。
|
||||
// 该方法自动识别参数类型,调用Struct/Structs方法。
|
||||
func (md *Model) Scan(objPointer interface{}) error {
|
||||
t := reflect.TypeOf(objPointer)
|
||||
k := t.Kind()
|
||||
if k != reflect.Ptr {
|
||||
return fmt.Errorf("params should be type of pointer, but got: %v", k)
|
||||
}
|
||||
k = t.Elem().Kind()
|
||||
switch k {
|
||||
case reflect.Array:
|
||||
case reflect.Slice:
|
||||
return md.Structs(objPointer)
|
||||
case reflect.Struct:
|
||||
return md.Struct(objPointer)
|
||||
default:
|
||||
return fmt.Errorf("element type should be type of struct/slice, unsupported: %v", k)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 链式操作,查询数量,fields可以为空,也可以自定义查询字段,
|
||||
|
||||
@ -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
|
||||
@ -11,12 +11,13 @@
|
||||
2.不支持save/replace方法
|
||||
3.不支持LastInsertId方法
|
||||
*/
|
||||
|
||||
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
|
||||
@ -11,12 +11,13 @@
|
||||
2.不支持save/replace方法,可以调用这2个方法估计会报错,还没测试过,(应该是可以通过oracle的merge来实现这2个功能的,还没仔细研究)
|
||||
3.不支持LastInsertId方法
|
||||
*/
|
||||
|
||||
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,15 @@
|
||||
// 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"
|
||||
)
|
||||
|
||||
@ -85,8 +84,8 @@ func (bs *dbBase) getTableFields(table string) (fields map[string]string, err er
|
||||
// 缓存不存在时会查询数据表结构,缓存后不过期,直至程序重启(重新部署)
|
||||
v := bs.cache.GetOrSetFunc("table_fields_" + table, func() interface{} {
|
||||
result := (Result)(nil)
|
||||
charl, charr := bs.db.getChars()
|
||||
result, err = bs.GetAll(fmt.Sprintf(`SHOW COLUMNS FROM %s%s%s`, charl, table, charr))
|
||||
charL, charR := bs.db.getChars()
|
||||
result, err = bs.GetAll(fmt.Sprintf(`SHOW COLUMNS FROM %s%s%s`, charL, table, charR))
|
||||
if err != nil {
|
||||
return nil
|
||||
}
|
||||
|
||||
@ -1,15 +1,17 @@
|
||||
// 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"
|
||||
"fmt"
|
||||
"github.com/gogf/gf/g/text/gregex"
|
||||
_ "github.com/gogf/gf/third/github.com/go-sql-driver/mysql"
|
||||
"reflect"
|
||||
)
|
||||
|
||||
// 数据库事务对象
|
||||
@ -75,6 +77,37 @@ func (tx *TX) GetStruct(obj interface{}, query string, args ...interface{}) erro
|
||||
return one.ToStruct(obj)
|
||||
}
|
||||
|
||||
// 数据库查询,查询多条记录,并自动转换为指定的slice对象, 如: []struct/[]*struct。
|
||||
func (tx *TX) GetStructs(objPointerSlice interface{}, query string, args ...interface{}) error {
|
||||
all, err := tx.GetAll(query, args...)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
return all.ToStructs(objPointerSlice)
|
||||
}
|
||||
|
||||
// 将结果转换为指定的struct/*struct/[]struct/[]*struct,
|
||||
// 参数应该为指针类型,否则返回失败。
|
||||
// 该方法自动识别参数类型,调用Struct/Structs方法。
|
||||
func (tx *TX) GetScan(objPointer interface{}, query string, args ...interface{}) error {
|
||||
t := reflect.TypeOf(objPointer)
|
||||
k := t.Kind()
|
||||
if k != reflect.Ptr {
|
||||
return fmt.Errorf("params should be type of pointer, but got: %v", k)
|
||||
}
|
||||
k = t.Elem().Kind()
|
||||
switch k {
|
||||
case reflect.Array:
|
||||
case reflect.Slice:
|
||||
return tx.db.GetStructs(objPointer, query, args ...)
|
||||
case reflect.Struct:
|
||||
return tx.db.GetStruct(objPointer, query, args ...)
|
||||
default:
|
||||
return fmt.Errorf("element type should be type of struct/slice, unsupported: %v", k)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// 数据库查询,获取查询字段值
|
||||
func (tx *TX) GetValue(query string, args ...interface{}) (Value, error) {
|
||||
one, err := tx.GetOne(query, args ...)
|
||||
@ -100,33 +133,33 @@ func (tx *TX) GetCount(query string, args ...interface{}) (int, error) {
|
||||
}
|
||||
|
||||
// CURD操作:单条数据写入, 仅仅执行写入操作,如果存在冲突的主键或者唯一索引,那么报错返回
|
||||
func (tx *TX) Insert(table string, data Map) (sql.Result, error) {
|
||||
return tx.db.doInsert(tx.tx, table, data, OPTION_INSERT)
|
||||
func (tx *TX) Insert(table string, data interface{}, batch...int) (sql.Result, error) {
|
||||
return tx.db.doInsert(tx.tx, table, data, OPTION_INSERT, batch...)
|
||||
}
|
||||
|
||||
// CURD操作:单条数据写入, 如果数据存在(主键或者唯一索引),那么删除后重新写入一条
|
||||
func (tx *TX) Replace(table string, data Map) (sql.Result, error) {
|
||||
return tx.db.doInsert(tx.tx, table, data, OPTION_REPLACE)
|
||||
func (tx *TX) Replace(table string, data interface{}, batch...int) (sql.Result, error) {
|
||||
return tx.db.doInsert(tx.tx, table, data, OPTION_REPLACE, batch...)
|
||||
}
|
||||
|
||||
// CURD操作:单条数据写入, 如果数据存在(主键或者唯一索引),那么更新,否则写入一条新数据
|
||||
func (tx *TX) Save(table string, data Map) (sql.Result, error) {
|
||||
return tx.db.doInsert(tx.tx, table, data, OPTION_SAVE)
|
||||
func (tx *TX) Save(table string, data interface{}, batch...int) (sql.Result, error) {
|
||||
return tx.db.doInsert(tx.tx, table, data, OPTION_SAVE, batch...)
|
||||
}
|
||||
|
||||
// CURD操作:批量数据指定批次量写入
|
||||
func (tx *TX) BatchInsert(table string, list List, batch int) (sql.Result, error) {
|
||||
return tx.db.doBatchInsert(tx.tx, table, list, batch, OPTION_INSERT)
|
||||
func (tx *TX) BatchInsert(table string, list interface{}, batch...int) (sql.Result, error) {
|
||||
return tx.db.doBatchInsert(tx.tx, table, list, OPTION_INSERT, batch...)
|
||||
}
|
||||
|
||||
// CURD操作:批量数据指定批次量写入, 如果数据存在(主键或者唯一索引),那么删除后重新写入一条
|
||||
func (tx *TX) BatchReplace(table string, list List, batch int) (sql.Result, error) {
|
||||
return tx.db.doBatchInsert(tx.tx, table, list, batch, OPTION_REPLACE)
|
||||
func (tx *TX) BatchReplace(table string, list interface{}, batch...int) (sql.Result, error) {
|
||||
return tx.db.doBatchInsert(tx.tx, table, list, OPTION_REPLACE, batch...)
|
||||
}
|
||||
|
||||
// CURD操作:批量数据指定批次量写入, 如果数据存在(主键或者唯一索引),那么更新,否则写入一条新数据
|
||||
func (tx *TX) BatchSave(table string, list List, batch int) (sql.Result, error) {
|
||||
return tx.db.doBatchInsert(tx.tx, table, list, batch, OPTION_SAVE)
|
||||
func (tx *TX) BatchSave(table string, list interface{}, batch...int) (sql.Result, error) {
|
||||
return tx.db.doBatchInsert(tx.tx, table, list, OPTION_SAVE, batch...)
|
||||
}
|
||||
|
||||
// CURD操作:数据更新,统一采用sql预处理
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user