Compare commits

...

139 Commits

Author SHA1 Message Date
cfdeb87093 gfile updates 2019-02-15 21:30:35 +08:00
74eef34ec2 gtest updates; TODO++ 2019-02-15 08:55:29 +08:00
2e87d5322f remove sarama-cluster 2019-02-14 19:30:58 +08:00
e89a49f39a remove gkafka 2019-02-14 19:24:45 +08:00
5d3fd91f0b remove sarama 2019-02-14 19:21:38 +08:00
f455d22893 move gkafka to new repo 2019-02-14 17:04:20 +08:00
c00f528098 remove reuseport in ghttp 2019-02-14 15:43:31 +08:00
6f02ad60eb hot fix issue in tpl auto update 2019-02-14 13:38:52 +08:00
ca6c0791ae fix issue with greuseport in windows 2019-02-03 21:41:32 +08:00
ee485dbfd8 README updates 2019-02-02 17:55:22 +08:00
2ecaa12647 travis updates 2019-02-02 16:54:04 +08:00
580e099cb7 update imports 2019-02-02 16:42:26 +08:00
83e50f0d38 update imports 2019-02-02 16:35:26 +08:00
48c770b475 update dependences 2019-02-02 16:31:34 +08:00
ba67101942 updates 2019-02-02 16:22:41 +08:00
72efde09b3 travis updates 2019-02-02 16:20:36 +08:00
76c49170bd change gitee.com/johng/gf -> github.com/gogf/gf 2019-02-02 16:18:25 +08:00
150aa7e2c2 comments update 2019-02-02 15:21:48 +08:00
0762fec696 comments update 2019-02-02 15:16:45 +08:00
1447496efa comments update 2019-02-02 14:22:32 +08:00
6fb9eafef0 garray updates 2019-02-02 09:07:40 +08:00
16bde5e9fc gmap updates 2019-02-01 22:25:58 +08:00
7b85c44444 add Clone for garray; comments of garray update 2019-02-01 22:00:58 +08:00
87d553fca2 greuseport updates 2019-02-01 21:05:56 +08:00
ba050d4c86 merge qiangg_reuseport 2019-02-01 20:51:02 +08:00
90cd7f49fd greuseport updates 2019-02-01 20:49:02 +08:00
29b42290c7 merge master 2019-02-01 20:46:53 +08:00
6c6c64bb3b rename category string to text 2019-02-01 20:45:11 +08:00
ae7db2cf9f gmap updates 2019-02-01 18:40:48 +08:00
1afab62dec fulfil unit test cases for garray 2019-02-01 18:33:53 +08:00
4171ca992e add reverse feature for function Sort of normal array; fulfil unit test cases for StringArray 2019-02-01 18:14:12 +08:00
e1166a8a80 Merge branch 'master' into develop 2019-02-01 17:55:06 +08:00
1dc6c799e1 version updates 2019-02-01 17:48:48 +08:00
ae1e075696 fix issue in binSearch of garray 2019-02-01 17:44:58 +08:00
74a7f71894 gset/gmap update 2019-02-01 17:30:23 +08:00
f2e149d3b3 fulfil unit test cases of gstr 2019-02-01 14:38:45 +08:00
92c0bf9cdc fix unit test cases of ghttp 2019-02-01 11:53:10 +08:00
020b050fb1 fix unit test cases for gstr/gconv/ghttp 2019-02-01 11:42:25 +08:00
08d71cede3 merge qiangg_php 2019-02-01 00:04:34 +08:00
be1f6cfbae Merge branch 'master' of https://gitee.com/johng/gf into develop 2019-02-01 00:02:17 +08:00
9aa5e139cf fulfil unit test cases of gstr 2019-02-01 00:02:01 +08:00
1d5c3d62dd fullfil unit test cases of gset 2019-01-31 21:15:39 +08:00
0e611ae94b merge master 2019-01-31 18:14:02 +08:00
104613b056 VERSION up 2019-01-31 13:58:35 +08:00
99577ad874 hot fix gfcache issue 2019-01-31 13:55:53 +08:00
908a46d27d gtest updates 2019-01-31 13:00:17 +08:00
85677b952f fulfil funcs of gstr/gset/garray; move gstr/gregex to the string category; move gtest to the test category 2019-01-30 21:27:03 +08:00
f4773ef1e4 add func ghttp.Group.Common for common http methods(GET/PUT/POST/DELETE) binding; fix issue #IRHB3,#IRHSJ; add more unit test cases for gvalid 2019-01-29 23:01:14 +08:00
5eaa6183b5 update benchmark test cases of gregex 2019-01-28 12:03:47 +08:00
4036d40c58 add batchSqlResult for gform; TODO++ 2019-01-27 19:44:05 +08:00
0b80cbb0dc merge master 2019-01-27 19:18:24 +08:00
79cb386d82 reuseport updates 2019-01-27 16:40:16 +08:00
09be68831b reuseport updates 2019-01-27 14:25:31 +08:00
0ac13c2b81 reuseport updates 2019-01-27 14:14:59 +08:00
94ef38e3cc reuseport updates 2019-01-27 03:28:55 +08:00
76882ac01c update golang.org/x/sys, add reuseport package 2019-01-27 03:13:20 +08:00
0d315218dd README updates 2019-01-25 12:48:22 +08:00
59945fbe91 README updates 2019-01-25 12:47:53 +08:00
c4962ec017 gfpool updates 2019-01-24 20:15:11 +08:00
58d60bc899 add json tag support for struct converting of gconv 2019-01-24 15:37:53 +08:00
5dd0a78423 ghttp updates 2019-01-24 14:03:48 +08:00
141ea7cc2d ghttp updates 2019-01-24 13:50:26 +08:00
a488d1dbf7 ghttp updates 2019-01-24 13:40:48 +08:00
ec130d0763 RELEASE updates 2019-01-23 21:30:02 +08:00
30729e3f93 README updates 2019-01-23 18:34:52 +08:00
241d7402cc README updates 2019-01-23 18:30:04 +08:00
3b14aba1a2 README updates 2019-01-23 17:33:51 +08:00
b02205f7cd gpool updates 2019-01-23 17:27:30 +08:00
c27bc0023f Merge branch 'master' of https://gitee.com/johng/gf 2019-01-23 15:01:29 +08:00
9698a7c5be gpool updates 2019-01-23 15:01:21 +08:00
071e2f8bb4 !17 修复where为空时不是1=1的bug
Merge pull request !17 from 张金富/master
2019-01-23 13:46:41 +08:00
726d3f7024 update unit tests of gtimer 2019-01-23 13:36:39 +08:00
3503aa43b4 gtimer, gmlock updates 2019-01-23 13:30:46 +08:00
e865b46304 add console and env values to change the default values of gtimer 2019-01-23 13:01:58 +08:00
494f96495e fulfil unit test cases of gtimer 2019-01-23 11:28:57 +08:00
7ed2081513 gcron, gtimer updates 2019-01-22 22:07:46 +08:00
5110313657 gcron, gtimer updates 2019-01-22 13:50:10 +08:00
24990e26c8 gcron, gtimer updates 2019-01-21 22:09:51 +08:00
3ca086bcec 修复where为空时不是1=1的bug 2019-01-21 16:26:47 +08:00
7d103c4ee8 gcron updates 2019-01-18 22:02:17 +08:00
5fed6f5681 update unit test case of gmlock 2019-01-18 15:14:05 +08:00
616539ecb0 update unit test cases of gmlock, concurrent safe reason 2019-01-18 15:03:45 +08:00
9e99e88d27 gmlock updates, add more unit test cases for gmlock 2019-01-18 11:30:52 +08:00
0e39400dd0 johng-cn/gf -> gogf/gf 2019-01-17 22:20:37 +08:00
2ba796de01 gconv examples update 2019-01-17 20:32:02 +08:00
efe2535977 README updates 2019-01-17 20:04:17 +08:00
c17352b8af travis updates 2019-01-17 16:37:26 +08:00
b1fc3ff17a travis updates 2019-01-17 16:06:56 +08:00
485dafb616 update grand.MeetProb, change param type from float64 to float32 2019-01-17 14:20:18 +08:00
bf25a3a601 gtimer updates; grand updates, change buffer slice type from uint64 to uint32 2019-01-17 14:15:23 +08:00
14fcd0b2f9 move gtime.SetTimeout&gtime.SetInterval to gtimer 2019-01-16 23:23:53 +08:00
cb24714faa gcron updates 2019-01-16 22:59:26 +08:00
36199334f0 gcron updates 2019-01-16 22:34:22 +08:00
72c7e65dfa gcron updates 2019-01-16 21:06:35 +08:00
a4ad301b44 fulfil logics of Exit* funcs of ghttp 2019-01-16 20:27:58 +08:00
72569321fa package comments update 2019-01-16 13:35:16 +08:00
1600a80124 comment updates 2019-01-16 13:02:59 +08:00
80c1a02377 pakage comments update 2019-01-16 09:00:23 +08:00
0c41909454 pakage comments updates 2019-01-15 23:27:47 +08:00
2b5d889bb9 fulfil unit test cases of gconv 2019-01-15 21:54:34 +08:00
2c2a71d429 fix issue in concurrent safe handling of gregex 2019-01-15 20:20:34 +08:00
f900414e38 add surport for field type auto detection in gform 2019-01-14 22:55:43 +08:00
1c72766c34 add new func Meet/MeetProb and alias N/Str/Digits/Letters for grand 2019-01-14 20:12:44 +08:00
302f3c1467 optimized package gconv, supported struct attr with ptr 2019-01-14 13:55:07 +08:00
9415419324 fix issue in gconv.Struct when given atrr of struct is nil time.Time 2019-01-14 09:00:31 +08:00
602592a354 TODO-- 2019-01-13 17:02:32 +08:00
1a4cba5fa5 VERSION updates 2019-01-13 00:47:23 +08:00
018853e976 optimize WebServer in hook priority handler and request exit mechanism 2019-01-13 00:43:36 +08:00
651bd33b73 add new 16x and 19x phone number validation for gvalid 2019-01-12 23:45:45 +08:00
f4644ce685 change func(xxx, safe...bool) to func(xxx, unsafe...bool); add new internal package mutex 2019-01-12 23:36:22 +08:00
0a422e9a89 rename go test funcs for gtimer 2019-01-12 22:47:07 +08:00
432c16c89f complete pckage gtimer development, a levelled timing wheel for interval/delayed jobs running and management 2019-01-12 22:41:12 +08:00
241706cbbf up 2019-01-12 20:20:30 +08:00
33dd0f9922 timing wheel up 2019-01-11 13:46:40 +08:00
ed8bb354e5 up 2019-01-09 18:47:11 +08:00
e373392f64 up 2019-01-09 13:26:59 +08:00
292fd2f39e up 2019-01-09 13:06:15 +08:00
1c9cb8286f up 2019-01-09 12:54:37 +08:00
8296061b64 up 2019-01-06 16:43:42 +08:00
bb5d84c29c up 2019-01-06 11:09:50 +08:00
eae857bcf7 merge master 2019-01-04 18:39:40 +08:00
40b5162fdf up 2019-01-04 15:32:16 +08:00
7934ad6904 Merge branch 'develop' of https://gitee.com/johng/gf into develop 2019-01-03 19:12:08 +08:00
858b010caa up 2019-01-03 19:11:54 +08:00
37cd2351e2 !16 增加ORACLE,SQL SERVER中获取指定表的所有字段名的方法
Merge pull request !16 from 蚊子/master
2019-01-02 21:11:30 +08:00
be1e250a6b 增加ORACLE,SQL SERVER中获取指定表的所有字段名的方法 2019-01-02 18:17:01 +08:00
f86896e5af Merge branch 'master' into develop 2019-01-02 12:42:36 +08:00
a95b1f0dae add more unit test cases 2019-01-02 10:18:00 +08:00
f1c7b95b33 up 2019-01-01 19:43:31 +08:00
e4a7e23c46 VERSION updates 2018-12-31 17:56:06 +08:00
6f15adf57f unit test cases++ 2018-12-31 17:46:04 +08:00
1966b40d01 ghttp.Client updates; add more unit test cases for web server 2018-12-31 00:50:55 +08:00
24ce4d098e ghttp.Client updates; add more unit test cases for web server 2018-12-31 00:22:18 +08:00
98619f9bc9 add more unit test cases for web server 2018-12-30 22:02:46 +08:00
1efeb2515d refract package 'gcron'; add package 'gtimew' for Time Wheel style job management 2018-12-30 18:56:21 +08:00
ccf837b2bf up 2018-12-30 14:53:16 +08:00
e558863743 up 2018-12-30 11:08:07 +08:00
43f21dfe92 Merge branch 'master' into qiangg_unit_test1 2018-12-29 13:07:32 +08:00
f5b2556b70 up 2018-12-28 21:44:36 +08:00
1940 changed files with 97785 additions and 138766 deletions

View File

@ -9,7 +9,7 @@ branches:
- develop
env:
- GITEE_GF=$GOPATH/src/gitee.com/johng/gf GO111MODULE=on
- GO111MODULE=on
services:
- mysql
@ -19,12 +19,12 @@ before_install:
install:
- pwd
- mkdir -p $GITEE_GF
- cp * $GITEE_GF -R
- cd $GITEE_GF
script:
- cd g && 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)

View File

@ -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

View File

@ -1,28 +1,28 @@
# GoFrame
<img align="right" height="150px" src="https://gfer.me/cover.png">
<img align="right" height="150px" src="https://goframe.org/cover.png">
[![Go Doc](https://godoc.org/github.com/johng-cn/gf?status.svg)](https://godoc.org/github.com/johng-cn/gf)
[![Build Status](https://travis-ci.org/johng-cn/gf.svg?branch=master)](https://travis-ci.org/johng-cn/gf)
[![Go Report](https://goreportcard.com/badge/github.com/johng-cn/gf)](https://goreportcard.com/report/github.com/johng-cn/gf)
[![Documents](https://img.shields.io/badge/docs-100%25-green.svg)](https://gfer.me)
[![License](https://img.shields.io/github/license/johng-cn/gf.svg?style=flat)](https://github.com/johng-cn/gf)
[![Language](https://img.shields.io/badge/language-go-blue.svg)](https://github.com/johng-cn/gf)
[![Release](https://img.shields.io/github/release/johng-cn/gf.svg?style=flat)](https://github.com/johng-cn/gf/releases)
[![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf)
[![Build Status](https://travis-ci.org/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf)
[![Go Report](https://goreportcard.com/badge/github.com/gogf/gf)](https://goreportcard.com/report/github.com/gogf/gf)
[![Documents](https://img.shields.io/badge/docs-100%25-green.svg)](https://goframe.org)
[![License](https://img.shields.io/github/license/gogf/gf.svg?style=flat)](https://github.com/gogf/gf)
[![Language](https://img.shields.io/badge/language-go-blue.svg)](https://github.com/gogf/gf)
[![Release](https://img.shields.io/github/release/gogf/gf.svg?style=flat)](https://github.com/gogf/gf/releases)
<!--
[![Code Coverage](https://codecov.io/gh/johng-cn/gf/branch/master/graph/badge.svg)](https://codecov.io/gh/johng-cn/gf)
[![Code Helper](https://www.codetriage.com/johng-cn/gf/badges/users.svg)](https://www.codetriage.com/johng-cn/gf)
[![Code Coverage](https://codecov.io/gh/gogf/gf/branch/master/graph/badge.svg)](https://codecov.io/gh/gogf/gf)
[![Code Helper](https://www.codetriage.com/gogf/gf/badges/users.svg)](https://www.codetriage.com/gogf/gf)
-->
`GF(GoFrame)` is a modular, lightweight, loosely coupled, high performance application development framework written in Go. Supporting graceful server, hot updates, multi-domain, multi-port, multi-service, HTTP/HTTPS, dynamic/hook routing and many more features. Providing a series of core components and dozens of practical modules.
# Installation
```
go get -u gitee.com/johng/gf
go get -u github.com/gogf/gf
```
or use `go.mod`
```
require gitee.com/johng/gf latest
require github.com/gogf/gf latest
```
# Limitation
@ -32,11 +32,11 @@ golang version >= 1.9.2
# Documentation
* [中文文档](https://gfer.me/)
* [中文文档](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/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>
<a href="https://gitee.com/hailaz" target="_blank" title="HaiLaz"><img src="https://gitee.com/uploads/87/1273187_hailaz.png" width="60" align="left"></a>

View File

@ -1,23 +1,21 @@
# GoFrame
<img align="right" height="150px" src="https://gfer.me/cover.png">
<img align="right" height="150px" src="https://goframe.org/cover.png">
[![Go Doc](https://godoc.org/github.com/johng-cn/gf?status.svg)](https://godoc.org/github.com/johng-cn/gf)
[![Build Status](https://travis-ci.org/johng-cn/gf.svg?branch=master)](https://travis-ci.org/johng-cn/gf)
[![Go Report](https://goreportcard.com/badge/github.com/johng-cn/gf)](https://goreportcard.com/report/github.com/johng-cn/gf)
[![Documents](https://img.shields.io/badge/docs-100%25-green.svg)](https://gfer.me)
[![License](https://img.shields.io/github/license/johng-cn/gf.svg?style=flat)](https://github.com/johng-cn/gf)
[![Language](https://img.shields.io/badge/language-go-blue.svg)](https://github.com/johng-cn/gf)
[![Release](https://img.shields.io/github/release/johng-cn/gf.svg?style=flat)](https://github.com/johng-cn/gf/releases)
[![Go Doc](https://godoc.org/github.com/gogf/gf?status.svg)](https://godoc.org/github.com/gogf/gf)
[![Build Status](https://travis-ci.org/gogf/gf.svg?branch=master)](https://travis-ci.org/gogf/gf)
[![Go Report](https://goreportcard.com/badge/github.com/gogf/gf)](https://goreportcard.com/report/github.com/gogf/gf)
[![Documents](https://img.shields.io/badge/docs-100%25-green.svg)](https://goframe.org)
[![License](https://img.shields.io/github/license/gogf/gf.svg?style=flat)](https://github.com/gogf/gf)
[![Language](https://img.shields.io/badge/language-go-blue.svg)](https://github.com/gogf/gf)
[![Release](https://img.shields.io/github/release/gogf/gf.svg?style=flat)](https://github.com/gogf/gf/releases)
<!--
[![Code Coverage](https://codecov.io/gh/johng-cn/gf/branch/master/graph/badge.svg)](https://codecov.io/gh/johng-cn/gf)
[![Code Helper](https://www.codetriage.com/johng-cn/gf/badges/users.svg)](https://www.codetriage.com/johng-cn/gf)
[![Code Coverage](https://codecov.io/gh/gogf/gf/branch/master/graph/badge.svg)](https://codecov.io/gh/gogf/gf)
[![Code Helper](https://www.codetriage.com/gogf/gf/badges/users.svg)](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`
```
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,22 +45,24 @@ golang版本 >= 1.9.2
# 架构
<div align=center>
<img src="https://gfer.me/images/arch.png"/>
<img src="https://goframe.org/images/arch.png"/>
</div>
# 文档
[https://gfer.me](https://gfer.me)
开发文档:[https://goframe.org](https://goframe.org)
接口文档:[https://godoc.org/github.com/gogf/gf](https://godoc.org/github.com/gogf/gf)
# 使用
```go
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() {
@ -72,31 +74,34 @@ func main() {
}
```
[更多..](https://gfer.me/start/index)
[更多..](https://goframe.org/start/index)
# 协议
`GF` 使用非常友好的 [MIT](LICENSE) 开源协议进行发布,永久`100%`开源免费。
# 贡献者(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/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>
<a href="https://gitee.com/hailaz" target="_blank" title="HaiLaz"><img src="https://gitee.com/uploads/87/1273187_hailaz.png" width="60" align="left"></a>

View File

@ -1,20 +1,50 @@
# `v1.4.6` (2019-01-24)
## 新特性
1. 新增并发安全的高性能任务定时器模块`gtimer`, 类似于Java的`Timer`但是比较于Java的`Timer`更加强大,内部实现采用灵活高效的`分层时间轮`设计,被设计为可管理维护百万级别以上数量的定时任务。`gtimer`为`GF`框架的核心模块之一,单元测试覆盖率达到`93.6%`[https://goframe.org/os/gtimer/index](https://goframe.org/os/gtimer/index)
1. 采用任务定时器`gtimer`重构`gcron`定时任务模块,去掉第三方`github.com/robfig/cron`包的使用。`gcron`增加单例模式的定时任务:[https://goframe.org/os/gcron/index#](https://goframe.org/os/gcron/index#)
1. `gconv`类型转换模块支持对`struct`结构体中的**指针属性**转换:[https://goframe.org/util/gconv/struct](https://goframe.org/util/gconv/struct)
1. `gform`增加对数据库类型的自动识别特性,这一特性在需要将查询结果`json`编码返回时非常有用: [https://goframe.org/database/orm/index](https://goframe.org/database/orm/index)
1. `Travis CI`增加对`386`架构的自动化测试支持(目前已支持`386`和`amd64`)
## 新功能
1. `ghttp`模块新增`Exit`、`ExitAll`、`ExitHook`方法用于HTTP请求处理流程控制: [https://goframe.org/net/ghttp/service/object](https://goframe.org/net/ghttp/service/object)
1. `grand`模块增加`Meet/MeetProb`方法,用于给定概率的随机满足判断,增加别名方法`N/Str/Digits/Letters`
1. `gvalid`数据/表单校验模块增加`16X`及`19X`手机号的校验支持;
## 功能改进
1. `gform`设置默认的数据库连接池`CONN_MAX_LIFE`参数值为`30`秒;
1. 改进`glist`模块,提高约`20%`左右性能,并增加若干链表操作方法;
1. 改进`gqueue`模块,提高约`50`左右性能,并增加模块对`select`语法的支持(使用`Queue.C`): [https://goframe.org/container/gqueue/index](https://goframe.org/container/gqueue/index)
1. 改进`gmlock`内存锁模块,并完善单元测试用例:[https://goframe.org/os/gmlock/index](https://goframe.org/os/gmlock/index)
1. 改进并发安全容器所有的模块,调整并发安全控制非必需参数`safe...bool`为`unsafe...bool`
1. 改进`gpool`对象复用模块,支持并发安全;
1. 更新`gkafka`模块的第三方依赖包;
1. 完善`ghttp`模块的单元测试用例;
## Bug Fix
1. 修复`gmd5`模块操作文件时的文件指针未关闭问题;
1. 修复`gcache`缓存项过期删除失效问题;
1. 其他修复;
# `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`内置函数,当给定的时间戳为空时打印当前的系统时间;
@ -37,20 +67,20 @@
# `v1.2.11` (2018-11-26)
## 新特性
1. `ORM`新增对`SQLServer`及`Oracle`的支持([https://gfer.me/database/orm/database](https://gfer.me/database/orm/database))
1. 完成`gvalid`模块校验结果的顺序特性([https://gfer.me/util/gvalid/checkmap](https://gfer.me/util/gvalid/checkmap));
1. 改进`ghttp.Request.Exit`,使得调用该方法时立即退出业务执行,开发者无需调用`Exit`方法时再使用`return`返回([https://gfer.me/net/ghttp/service/object](https://gfer.me/net/ghttp/service/object))
1. 模板引擎新增若干内置函数:`text/html/htmldecode/url/urldecode/date/compare/substr/strlimit/hidestr/highlight/toupper/tolower/nl2br` ([https://gfer.me/os/gview/funcs](https://gfer.me/os/gview/funcs));
1. 模板引擎新增内置变量`Config` ([https://gfer.me/os/gview/vars](https://gfer.me/os/gview/vars));
1. `ORM`新增对`SQLServer`及`Oracle`的支持([https://goframe.org/database/orm/database](https://goframe.org/database/orm/database))
1. 完成`gvalid`模块校验结果的顺序特性([https://goframe.org/util/gvalid/checkmap](https://goframe.org/util/gvalid/checkmap));
1. 改进`ghttp.Request.Exit`,使得调用该方法时立即退出业务执行,开发者无需调用`Exit`方法时再使用`return`返回([https://goframe.org/net/ghttp/service/object](https://goframe.org/net/ghttp/service/object))
1. 模板引擎新增若干内置函数:`text/html/htmldecode/url/urldecode/date/compare/substr/strlimit/hidestr/highlight/toupper/tolower/nl2br` ([https://goframe.org/os/gview/funcs](https://goframe.org/os/gview/funcs));
1. 模板引擎新增内置变量`Config` ([https://goframe.org/os/gview/vars](https://goframe.org/os/gview/vars));
1. 改进`gconv.Struct`转换默认规则,支持不区分大小写的键名与属性名称匹配;
1. `gform`配置文件支持`linkinfo`自定义数据库连接字段([https://gfer.me/database/orm/config](https://gfer.me/database/orm/config))
1. `gfsnotify`模块增加对特定回调的取消注册功能([https://gfer.me/os/gfsnotify/index](https://gfer.me/os/gfsnotify/index)
1. `gform`配置文件支持`linkinfo`自定义数据库连接字段([https://goframe.org/database/orm/config](https://goframe.org/database/orm/config))
1. `gfsnotify`模块增加对特定回调的取消注册功能([https://goframe.org/os/gfsnotify/index](https://goframe.org/os/gfsnotify/index)
## 新功能
1. 改进`ghttp.Request`,增加`SetParam/GetParam`请求流程自定义变量设置/获取方法,用于在请求流程中的回调函数共享变量([https://gfer.me/net/ghttp/request](https://gfer.me/net/ghttp/request);
1. 改进`ghttp.Response`,增加`ServeFileDownload`方法用于WebServer引导客户端下载文件([https://gfer.me/net/ghttp/response](https://gfer.me/net/ghttp/response));
1. 改进`ghttp.Request`,增加`SetParam/GetParam`请求流程自定义变量设置/获取方法,用于在请求流程中的回调函数共享变量([https://goframe.org/net/ghttp/request](https://goframe.org/net/ghttp/request);
1. 改进`ghttp.Response`,增加`ServeFileDownload`方法用于WebServer引导客户端下载文件([https://goframe.org/net/ghttp/response](https://goframe.org/net/ghttp/response));
1. `gvar`模块新增`gvar.VarRead`只读接口,用于控制对外只暴露数据读取功能;
1. 增加`g.Throw`抛异常方法,`g.TryCatch`异常捕获方法封装;
1. 改进`gcron`模块增加自定义的Cron管理对象增加`New/Start/Stop`方法;
@ -58,9 +88,9 @@
## 功能改进
1. WebServer添加`RouterCacheExpire`配置参数,用于设置路由检索缓存过期时间;
1. WebServer允许同一`HOOK`事件被多次绑定注册,先注册的回调函数优先级更高([https://gfer.me/net/ghttp/service/hook](https://gfer.me/net/ghttp/service/hook));
1. WebServer允许同一`HOOK`事件被多次绑定注册,先注册的回调函数优先级更高([https://goframe.org/net/ghttp/service/hook](https://goframe.org/net/ghttp/service/hook));
1. 当前工作目录为系统临时目录时,`gcfg`/`gview`/`ghttp`模块默认不添加工作目录到搜索路径;
1. 改进`WebSocket`默认支持跨域请求([https://gfer.me/net/ghttp/websocket](https://gfer.me/net/ghttp/websocket));
1. 改进`WebSocket`默认支持跨域请求([https://goframe.org/net/ghttp/websocket](https://goframe.org/net/ghttp/websocket));
1. 改进`gtime.Format`支持中文;
1. 改进`gfsnotify`,支持编辑器对文件非执行标准编辑时(RENAME+CHMOD)的热更新问题;
1. 改进`gtype.Set`方法增加Set原子操作返回旧的变量值;
@ -69,12 +99,12 @@
1. `gstr`模块增加对中文截取方法;
1. 改进`gtime.StrToTime`对常用时间格式匹配模式,新增`gtime.ParseTimeFromContent`方法;
1. 修改配置管理、模板引擎、调试模式的环境变量名称为大写下划线标准格式;
1. 改进`grand`模块随机数生成设计,底层使用`crypto/rand`+缓冲区实现高速的随机数生成([https://gfer.me/util/grand/index](https://gfer.me/util/grand/index));
1. 改进`grand`模块随机数生成设计,底层使用`crypto/rand`+缓冲区实现高速的随机数生成([https://goframe.org/util/grand/index](https://goframe.org/util/grand/index));
## 问题修复
1. 修复`gspath`模块在`windows`下搜索失效问题;
1. 修复`gspath`模块Search时带有indexFiles的检索问题;
1. bug fix INZS1([https://gitee.com/johng/gf/issues/INZS1](https://gitee.com/johng/gf/issues/INZS1));
1. bug fix INZS1([https://github.com/gogf/gf/issues/INZS1](https://github.com/gogf/gf/issues/INZS1));
1. 修复`gproc.ShellRun`在windows下的执行问题;
@ -313,8 +343,8 @@
12、gdb数据库ORM包增加And/Or条件链式方法并改进Where/Data方法参数灵活性
13、对于新增加的模块同时也增加了对应的开发文档并梳理完善了现有的其他模块开发文档
14、修复ISSUE:
#IISWI gitee.com/johng/gf/issues/IISWI,
#IISMY gitee.com/johng/gf/issues/IISMY,
#IISWI github.com/gogf/gf/issues/IISWI,
#IISMY github.com/gogf/gf/issues/IISMY,
反馈并跟踪完成第三方依赖mxj包的ISSUE修复(github.com/clbanning/mxj/issues/48)

21
TODO.MD
View File

@ -1,8 +1,4 @@
# ON THE WAY
1. orm增加更多数据库支持
1. 增加对于数据表Model的封装
1. 更多数据库的ORM功能支持
1. 考虑gdb对象管理增加二级连接池特性提高New&Close性能
1. 增加图形验证码支持,至少支持数字和英文字母;
1. 增加热编译工具,提高开发环境的开发/测试效率媲美PHP开发效率
1. 增加可选择性的orm tag特性用以数据表记录与struct对象转换的键名属性映射
@ -47,7 +43,15 @@
1. 改进WebServer获取POST参数处理逻辑当提交非form数据时例如json数据针对某些方法可以直接解析
1. WebServer增加可选择的路由覆盖配置默认情况下不覆盖
1. gkafka这个包比较重未来从框架中剥离出来
1. grpool性能压测结果变慢的问题
1. 增加jumplist的数据结构容器
1. DelayQueue/PriorityQueue
1. gconv针对struct的转换增加json tag支持gconv.Map默认也支持json tag, 完善开发文档;
1. 增加SO_REUSEPORT的支持
1. 权限管理模块;
1. 从ghttp中剥离SESSION功能构成单独的模块gsession
1. 改进gproc进程间通信处理逻辑提高稳定性以应对进程间大批量的数据发送/接收;
1. gdb的Data方法支持struct参数传入
@ -78,7 +82,7 @@
21. 改进控制器及执行对象注册,更友好地支持动态路由注册,例如:注册规则为 /channel/:name现有的控制器及执行对象注册很难友好支持这种动态形式
22. 当前gpage分页包的输出标签不支持li大多数CSS框架都是li+a标签模式需要提供可更加灵活的定制化功能实现
23. 平滑重启机制改进,以便于开发阶段调试;
24. 对grpool进行优化改进包括属性原子操作封装采用gtype实现修正设计BUGhttps://github.com/johng-cn/gf/issues/6
24. 对grpool进行优化改进包括属性原子操作封装采用gtype实现修正设计BUGhttps://github.com/gogf/gf/issues/6
25. gredis增加redis密码支持
26. 改进ghttp.Server平滑重启机制当新进程接管服务后再使用进程间通信方式通知父进程销毁
27. gproc进程间通信增加分组特性不同的进程间可以通过进程ID以及分组名称发送/获取进程消息;
@ -108,4 +112,7 @@
1. gcfg/gview/ghttp等模块加上对临时文件目录的自动添加监听判断基本是开发环境下特别是windows环境去掉临时文件的监听避免临时文件过大引起的运行缓慢占用内存问题
1. 改进gfpool在文件指针变化时的更新
1. ghttp hook回调使用方式在注册路由比较多的时候优先级可能使得开发者混乱考虑方式便于管理
1. gform对于MySQL字段类型为datetime类型的时区问题分析
1. gform对于MySQL字段类型为datetime类型的时区问题分析
1. 改进证书打开失败时的WebServer错误提示前置HOOK校验后关闭后续的HOOK逻辑执行
1. 目前WebServer的HOOK是按照优先级执行的需要增加覆盖特性

View File

@ -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

View File

@ -1,13 +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, safe...bool) *Array {
return NewArray(size, cap, safe...)
}

View File

@ -0,0 +1,8 @@
// Copyright 2019 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package garray

View File

@ -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/container/internal/rwmutex"
)
type IntArray struct {
mu *rwmutex.RWMutex // 互斥锁
cap int // 初始化设置的数组容量
size int // 初始化设置的数组大小
array []int // 底层数组
}
func NewIntArray(size int, cap int, safe...bool) *IntArray {
a := &IntArray{
mu : rwmutex.New(safe...),
}
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)
}

View File

@ -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/container/internal/rwmutex"
)
type Array struct {
mu *rwmutex.RWMutex // 互斥锁
cap int // 初始化设置的数组容量
size int // 初始化设置的数组大小
array []interface{} // 底层数组
}
func NewArray(size int, cap int, safe...bool) *Array {
a := &Array{
mu : rwmutex.New(safe...),
}
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)
}

View File

@ -0,0 +1,592 @@
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package garray
import (
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/util/gconv"
"github.com/gogf/gf/g/util/grand"
"math"
"sort"
"strings"
)
type IntArray struct {
mu *rwmutex.RWMutex // 互斥锁
array []int // 底层数组
}
// Create an empty array.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 创建一个空的数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
func NewIntArray(unsafe...bool) *IntArray {
return NewIntArraySize(0, 0, unsafe...)
}
// Create an array with given size and cap.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 创建一个指定大小的数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
func NewIntArraySize(size int, cap int, unsafe...bool) *IntArray {
return &IntArray{
mu : rwmutex.New(unsafe...),
array : make([]int, size, cap),
}
}
// Create an array with given slice <array>.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 通过给定的slice变量创建数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
func NewIntArrayFrom(array []int, unsafe...bool) *IntArray {
return &IntArray{
mu : rwmutex.New(unsafe...),
array : array,
}
}
// Get value by index.
//
// 获取指定索引的数据项, 调用方注意判断数组边界。
func (a *IntArray) Get(index int) int {
a.mu.RLock()
defer a.mu.RUnlock()
value := a.array[index]
return value
}
// Set value by index.
//
// 设置指定索引的数据项, 调用方注意判断数组边界。
func (a *IntArray) Set(index int, value int) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
a.array[index] = value
return a
}
// Set the underlying slice array with the given <array> param.
//
// 设置底层数组变量.
func (a *IntArray) SetArray(array []int) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
a.array = array
return a
}
// Replace the array items by given <array> from the beginning of array.
//
// 使用指定数组替换到对应的索引元素值.
func (a *IntArray) Replace(array []int) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
max := len(array)
if max > len(a.array) {
max = len(a.array)
}
for i := 0; i < max; i++ {
a.array[i] = array[i]
}
return a
}
// Calculate the sum of values in an array.
//
// 对数组中的元素项求和。
func (a *IntArray) Sum() (sum int) {
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
sum += v
}
return
}
// Sort the array in increasing order.
// The param <reverse> controls whether sort
// in increasing order(default) or decreasing order
//
// 将数组排序(默认从低到高).
func (a *IntArray) Sort(reverse...bool) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
if len(reverse) > 0 && reverse[0] {
sort.Slice(a.array, func(i, j int) bool {
if a.array[i] < a.array[j] {
return false
}
return true
})
} else {
sort.Ints(a.array)
}
return a
}
// Sort the array by custom function <less>.
//
// 使用自定义的排序函数将数组重新排序.
func (a *IntArray) SortFunc(less func(v1, v2 int) bool) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
sort.Slice(a.array, func(i, j int) bool {
return less(a.array[i], a.array[j])
})
return a
}
// Insert the <value> to the front of <index>.
//
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界。
func (a *IntArray) InsertBefore(index int, value int) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
rear := append([]int{}, a.array[index : ]...)
a.array = append(a.array[0 : index], value)
a.array = append(a.array, rear...)
return a
}
// Insert the <value> to the back of <index>.
//
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界。
func (a *IntArray) InsertAfter(index int, value int) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
rear := append([]int{}, a.array[index + 1:]...)
a.array = append(a.array[0 : index + 1], value)
a.array = append(a.array, rear...)
return a
}
// Remove an item by index.
//
// 删除指定索引的数据项, 调用方注意判断数组边界。
func (a *IntArray) Remove(index int) int {
a.mu.Lock()
defer a.mu.Unlock()
// 边界删除判断,以提高删除效率
if index == 0 {
value := a.array[0]
a.array = a.array[1 : ]
return value
} else if index == len(a.array) - 1 {
value := a.array[index]
a.array = a.array[: index]
return value
}
// 如果非边界删除,会涉及到数组创建,那么删除的效率差一些
value := a.array[index]
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
return value
}
// Push new items to the beginning of array.
//
// 将数据项添加到数组的最左端(索引为0)。
func (a *IntArray) PushLeft(value...int) *IntArray {
a.mu.Lock()
a.array = append(value, a.array...)
a.mu.Unlock()
return a
}
// Push new items to the end of array.
//
// 将数据项添加到数组的最右端(索引为length - 1), 等于: Append。
func (a *IntArray) PushRight(value...int) *IntArray {
a.mu.Lock()
a.array = append(a.array, value...)
a.mu.Unlock()
return a
}
// Pop an item from the beginning of array.
//
// 将最左端(索引为0)的数据项移出数组,并返回该数据项。
func (a *IntArray) PopLeft() int {
a.mu.Lock()
defer a.mu.Unlock()
value := a.array[0]
a.array = a.array[1 : ]
return value
}
// Pop an item from the end of array.
//
// 将最右端(索引为length - 1)的数据项移出数组,并返回该数据项。
func (a *IntArray) PopRight() int {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
value := a.array[index]
a.array = a.array[: index]
return value
}
// Pop an random item from array.
//
// 随机将一个数据项移出数组,并返回该数据项。
func (a *IntArray) PopRand() int {
return a.Remove(grand.Intn(len(a.array)))
}
// Pop <size> items from the beginning of array.
//
// 将最左端(首部)的size个数据项移出数组并返回该数据项。
func (a *IntArray) PopLefts(size int) []int {
a.mu.Lock()
defer a.mu.Unlock()
length := len(a.array)
if size > length {
size = length
}
value := a.array[0 : size]
a.array = a.array[size : ]
return value
}
// Pop <size> items from the end of array.
//
// 将最右端(尾部)的size个数据项移出数组并返回该数据项
func (a *IntArray) PopRights(size int) []int {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - size
if index < 0 {
index = 0
}
value := a.array[index :]
a.array = a.array[ : index]
return value
}
// Get items by range, returns array[start:end].
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
//
// 将最右端(尾部)的size个数据项移出数组并返回该数据项
func (a *IntArray) Range(start, end int) []int {
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
if start > length || start > end {
return nil
}
if start < 0 {
start = 0
}
if end > length {
end = length
}
array := ([]int)(nil)
if a.mu.IsSafe() {
a.mu.RLock()
defer a.mu.RUnlock()
array = make([]int, end - start)
copy(array, a.array[start : end])
} else {
array = a.array[start : end]
}
return array
}
// See PushRight.
//
// 追加数据项, 等于: PushRight。
func (a *IntArray) Append(value...int) *IntArray {
a.mu.Lock()
a.array = append(a.array, value...)
a.mu.Unlock()
return a
}
// Get the length of array.
//
// 数组长度。
func (a *IntArray) Len() int {
a.mu.RLock()
length := len(a.array)
a.mu.RUnlock()
return length
}
// Get the underlying data of array.
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
//
// 返回原始数据数组.
func (a *IntArray) Slice() []int {
array := ([]int)(nil)
if a.mu.IsSafe() {
a.mu.RLock()
defer a.mu.RUnlock()
array = make([]int, len(a.array))
copy(array, a.array)
} else {
array = a.array
}
return array
}
// Return a new array, which is a copy of current array.
//
// 克隆当前数组,返回当前数组的一个拷贝。
func (a *IntArray) Clone() (newArray *IntArray) {
a.mu.RLock()
array := make([]int, len(a.array))
copy(array, a.array)
a.mu.RUnlock()
return NewIntArrayFrom(array, !a.mu.IsSafe())
}
// Clear array.
//
// 清空数据数组。
func (a *IntArray) Clear() *IntArray {
a.mu.Lock()
if len(a.array) > 0 {
a.array = make([]int, 0)
}
a.mu.Unlock()
return a
}
// Check whether a value exists in the array.
//
// 查找指定数值是否存在。
func (a *IntArray) Contains(value int) bool {
return a.Search(value) != -1
}
// Search array by <value>, returns the index of <value>, returns -1 if not exists.
//
// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1。
func (a *IntArray) Search(value int) int {
if len(a.array) == 0 {
return -1
}
a.mu.RLock()
result := -1
for index, v := range a.array {
if v == value {
result = index
break
}
}
a.mu.RUnlock()
return result
}
// Unique the array, clear repeated values.
//
// 清理数组中重复的元素项。
func (a *IntArray) Unique() *IntArray {
a.mu.Lock()
for i := 0; i < len(a.array) - 1; i++ {
for j := i + 1; j < len(a.array); j++ {
if a.array[i] == a.array[j] {
a.array = append(a.array[ : j], a.array[j + 1 : ]...)
}
}
}
a.mu.Unlock()
return a
}
// Lock writing by callback function f.
//
// 使用自定义方法执行加锁修改操作。
func (a *IntArray) LockFunc(f func(array []int)) *IntArray {
a.mu.Lock(true)
defer a.mu.Unlock(true)
f(a.array)
return a
}
// Lock reading by callback function f.
//
// 使用自定义方法执行加锁读取操作。
func (a *IntArray) RLockFunc(f func(array []int)) *IntArray {
a.mu.RLock(true)
defer a.mu.RUnlock(true)
f(a.array)
return a
}
// Merge two arrays.
//
// 合并两个数组.
func (a *IntArray) Merge(array *IntArray) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
if a != array {
array.mu.RLock()
defer array.mu.RUnlock()
}
a.array = append(a.array, array.array...)
return a
}
// Fills an array with num entries of the value of the value parameter,
// keys starting at the startIndex parameter.
//
// 用value参数的值将数组填充num个条目位置由startIndex参数指定的开始。
func (a *IntArray) Fill(startIndex int, num int, value int) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
if startIndex < 0 {
startIndex = 0
}
for i := startIndex; i < startIndex + num; i++ {
if i > len(a.array) - 1 {
a.array = append(a.array, value)
} else {
a.array[i] = value
}
}
return a
}
// Chunks an array into arrays with size elements.
// The last chunk may contain less than size elements.
//
// 将一个数组分割成多个数组其中每个数组的单元数目由size决定。最后一个数组的单元数目可能会少于size个。
func (a *IntArray) Chunk(size int) [][]int {
if size < 1 {
return nil
}
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
chunks := int(math.Ceil(float64(length) / float64(size)))
var n [][]int
for i, end := 0, 0; chunks > 0; chunks-- {
end = (i + 1) * size
if end > length {
end = length
}
n = append(n, a.array[i*size : end])
i++
}
return n
}
// Pad array to the specified length with a value.
// If size is positive then the array is padded on the right, or negative on the left.
// If the absolute value of size is less than or equal to the length of the array
// then no padding takes place.
//
// 返回数组的一个拷贝并用value将其填补到size指定的长度。
// 如果size为正数则填补到数组的右侧如果为负数则从左侧开始填补。
// 如果size的绝对值小于或等于数组的长度则没有任何填补。
func (a *IntArray) Pad(size int, value int) *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
if size == 0 || (size > 0 && size < len(a.array)) || (size < 0 && size > -len(a.array)) {
return a
}
n := size
if size < 0 {
n = -size
}
n -= len(a.array)
tmp := make([]int, n)
for i := 0; i < n; i++ {
tmp[i] = value
}
if size > 0 {
a.array = append(a.array, tmp...)
} else {
a.array = append(tmp, a.array...)
}
return a
}
// Extract a slice of the array(If in concurrent safe usage,
// it returns a copy of the slice; else a pointer).
// It returns the sequence of elements from the array array as specified
// by the offset and length parameters.
//
// 返回根据offset和size参数所指定的数组中的一段序列。
func (a *IntArray) SubSlice(offset, size int) []int {
a.mu.RLock()
defer a.mu.RUnlock()
if offset > len(a.array) {
return nil
}
if offset + size > len(a.array) {
size = len(a.array) - offset
}
if a.mu.IsSafe() {
s := make([]int, size)
copy(s, a.array[offset:])
return s
} else {
return a.array[offset:]
}
}
// Picks one or more random entries out of an array(a copy),
// and returns the key (or keys) of the random entries.
//
// 从数组中随机取出size个元素项构成slice返回。
func (a *IntArray) Rand(size int) []int {
a.mu.RLock()
defer a.mu.RUnlock()
if size > len(a.array) {
size = len(a.array)
}
n := make([]int, size)
for i, v := range grand.Perm(len(a.array)) {
n[i] = a.array[v]
if i == size - 1 {
break
}
}
return n
}
// Randomly shuffles the array.
//
// 随机打乱当前数组。
func (a *IntArray) Shuffle() *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
for i, v := range grand.Perm(len(a.array)) {
a.array[i], a.array[v] = a.array[v], a.array[i]
}
return a
}
// Make array with elements in reverse order.
//
// 将当前数组反转。
func (a *IntArray) Reverse() *IntArray {
a.mu.Lock()
defer a.mu.Unlock()
for i, j := 0, len(a.array) - 1; i < j; i, j = i + 1, j - 1 {
a.array[i], a.array[j] = a.array[j], a.array[i]
}
return a
}
// Join array elements with a string.
//
// 使用glue字符串串连当前数组的元素项构造成新的字符串返回。
func (a *IntArray) Join(glue string) string {
a.mu.RLock()
defer a.mu.RUnlock()
return strings.Join(gconv.Strings(a.array), glue)
}

View File

@ -0,0 +1,587 @@
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package garray
import (
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/util/gconv"
"github.com/gogf/gf/g/util/grand"
"math"
"sort"
"strings"
)
type Array struct {
mu *rwmutex.RWMutex // 互斥锁
array []interface{} // 底层数组
}
// Create an empty array.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 创建一个空的数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
func New(unsafe...bool) *Array {
return NewArraySize(0, 0, unsafe...)
}
// See New.
//
// 同New方法。
func NewArray(unsafe...bool) *Array {
return NewArraySize(0, 0, unsafe...)
}
// Create an array with given size and cap.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 创建一个指定大小的数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
func NewArraySize(size int, cap int, unsafe...bool) *Array {
return &Array{
mu : rwmutex.New(unsafe...),
array : make([]interface{}, size, cap),
}
}
// Create an array with given slice <array>.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 通过给定的slice变量创建数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
func NewArrayFrom(array []interface{}, unsafe...bool) *Array {
return &Array{
mu : rwmutex.New(unsafe...),
array : array,
}
}
// Get value by index.
//
// 获取指定索引的数据项, 调用方注意判断数组边界
func (a *Array) Get(index int) interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
value := a.array[index]
return value
}
// Set value by index.
//
// 设置指定索引的数据项, 调用方注意判断数组边界
func (a *Array) Set(index int, value interface{}) *Array {
a.mu.Lock()
defer a.mu.Unlock()
a.array[index] = value
return a
}
// Set the underlying slice array with the given <array> param.
//
// 设置底层数组变量.
func (a *Array) SetArray(array []interface{}) *Array {
a.mu.Lock()
defer a.mu.Unlock()
a.array = array
return a
}
// Replace the array items by given <array> from the beginning of array.
//
// 使用指定数组替换到对应的索引元素值.
func (a *Array) Replace(array []interface{}) *Array {
a.mu.Lock()
defer a.mu.Unlock()
max := len(array)
if max > len(a.array) {
max = len(a.array)
}
for i := 0; i < max; i++ {
a.array[i] = array[i]
}
return a
}
// Calculate the sum of values in an array.
//
// 对数组中的元素项求和(将元素值转换为int类型后叠加)。
func (a *Array) Sum() (sum int) {
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
sum += gconv.Int(v)
}
return
}
// Sort the array by custom function <less>.
//
// 使用自定义的排序函数将数组重新排序.
func (a *Array) SortFunc(less func(v1, v2 interface{}) bool) *Array {
a.mu.Lock()
defer a.mu.Unlock()
sort.Slice(a.array, func(i, j int) bool {
return less(a.array[i], a.array[j])
})
return a
}
// Insert the <value> to the front of <index>.
//
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界。
func (a *Array) InsertBefore(index int, value interface{}) *Array {
a.mu.Lock()
defer a.mu.Unlock()
rear := append([]interface{}{}, a.array[index : ]...)
a.array = append(a.array[0 : index], value)
a.array = append(a.array, rear...)
return a
}
// Insert the <value> to the back of <index>.
//
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界。
func (a *Array) InsertAfter(index int, value interface{}) *Array {
a.mu.Lock()
defer a.mu.Unlock()
rear := append([]interface{}{}, a.array[index + 1 : ]...)
a.array = append(a.array[0 : index + 1], value)
a.array = append(a.array, rear...)
return a
}
// Remove an item by index.
//
// 删除指定索引的数据项, 调用方注意判断数组边界。
func (a *Array) Remove(index int) interface{} {
a.mu.Lock()
defer a.mu.Unlock()
// 边界删除判断,以提高删除效率
if index == 0 {
value := a.array[0]
a.array = a.array[1 : ]
return value
} else if index == len(a.array) - 1 {
value := a.array[index]
a.array = a.array[: index]
return value
}
// 如果非边界删除,会涉及到数组创建,那么删除的效率差一些
value := a.array[index]
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
return value
}
// Push new items to the beginning of array.
//
// 将数据项添加到数组的最左端(索引为0)。
func (a *Array) PushLeft(value...interface{}) *Array {
a.mu.Lock()
a.array = append(value, a.array...)
a.mu.Unlock()
return a
}
// Push new items to the end of array.
//
// 将数据项添加到数组的最右端(索引为length - 1), 等于: Append。
func (a *Array) PushRight(value...interface{}) *Array {
a.mu.Lock()
a.array = append(a.array, value...)
a.mu.Unlock()
return a
}
// Pop an random item from array.
//
// 随机将一个数据项移出数组,并返回该数据项。
func (a *Array) PopRand() interface{} {
return a.Remove(grand.Intn(len(a.array)))
}
// Pop an item from the beginning of array.
//
// 将最左端(索引为0)的数据项移出数组,并返回该数据项。
func (a *Array) PopLeft() interface{} {
a.mu.Lock()
defer a.mu.Unlock()
value := a.array[0]
a.array = a.array[1 : ]
return value
}
// Pop an item from the end of array.
//
// 将最右端(索引为length - 1)的数据项移出数组,并返回该数据项。
func (a *Array) PopRight() interface{} {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
value := a.array[index]
a.array = a.array[: index]
return value
}
// Pop <size> items from the beginning of array.
//
// 将最左端(首部)的size个数据项移出数组并返回该数据项
func (a *Array) PopLefts(size int) []interface{} {
a.mu.Lock()
defer a.mu.Unlock()
length := len(a.array)
if size > length {
size = length
}
value := a.array[0 : size]
a.array = a.array[size : ]
return value
}
// Pop <size> items from the end of array.
//
// 将最右端(尾部)的size个数据项移出数组并返回该数据项
func (a *Array) PopRights(size int) []interface{} {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - size
if index < 0 {
index = 0
}
value := a.array[index :]
a.array = a.array[ : index]
return value
}
// Get items by range, returns array[start:end].
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
//
// 将最右端(尾部)的size个数据项移出数组并返回该数据项
func (a *Array) Range(start, end int) []interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
if start > length || start > end {
return nil
}
if start < 0 {
start = 0
}
if end > length {
end = length
}
array := ([]interface{})(nil)
if a.mu.IsSafe() {
a.mu.RLock()
defer a.mu.RUnlock()
array = make([]interface{}, end - start)
copy(array, a.array[start : end])
} else {
array = a.array[start : end]
}
return array
}
// See PushRight.
//
// 追加数据项, 等于: PushRight。
func (a *Array) Append(value...interface{}) *Array {
a.PushRight(value...)
return a
}
// Get the length of array.
//
// 数组长度。
func (a *Array) Len() int {
a.mu.RLock()
length := len(a.array)
a.mu.RUnlock()
return length
}
// Get the underlying data of array.
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
//
// 返回原始数据数组.
func (a *Array) Slice() []interface{} {
array := ([]interface{})(nil)
if a.mu.IsSafe() {
a.mu.RLock()
defer a.mu.RUnlock()
array = make([]interface{}, len(a.array))
copy(array, a.array)
} else {
array = a.array
}
return array
}
// Return a new array, which is a copy of current array.
//
// 克隆当前数组,返回当前数组的一个拷贝。
func (a *Array) Clone() (newArray *Array) {
a.mu.RLock()
array := make([]interface{}, len(a.array))
copy(array, a.array)
a.mu.RUnlock()
return NewArrayFrom(array, !a.mu.IsSafe())
}
// Clear array.
//
// 清空数据数组
func (a *Array) Clear() *Array {
a.mu.Lock()
if len(a.array) > 0 {
a.array = make([]interface{}, 0)
}
a.mu.Unlock()
return a
}
// Check whether a value exists in the array.
//
// 查找指定数值是否存在
func (a *Array) Contains(value interface{}) bool {
return a.Search(value) != -1
}
// Search array by <value>, returns the index of <value>, returns -1 if not exists.
//
// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1
func (a *Array) Search(value interface{}) int {
if len(a.array) == 0 {
return -1
}
a.mu.RLock()
result := -1
for index, v := range a.array {
if v == value {
result = index
break
}
}
a.mu.RUnlock()
return result
}
// Unique the array, clear repeated values.
//
// 清理数组中重复的元素项
func (a *Array) Unique() *Array {
a.mu.Lock()
for i := 0; i < len(a.array) - 1; i++ {
for j := i + 1; j < len(a.array); j++ {
if a.array[i] == a.array[j] {
a.array = append(a.array[ : j], a.array[j + 1 : ]...)
}
}
}
a.mu.Unlock()
return a
}
// Lock writing by callback function f.
//
// 使用自定义方法执行加锁修改操作
func (a *Array) LockFunc(f func(array []interface{})) *Array {
a.mu.Lock(true)
defer a.mu.Unlock(true)
f(a.array)
return a
}
// Lock reading by callback function f.
//
// 使用自定义方法执行加锁读取操作
func (a *Array) RLockFunc(f func(array []interface{})) *Array {
a.mu.RLock(true)
defer a.mu.RUnlock(true)
f(a.array)
return a
}
// Merge two arrays.
//
// 合并两个数组.
func (a *Array) Merge(array *Array) *Array {
a.mu.Lock()
defer a.mu.Unlock()
if a != array {
array.mu.RLock()
defer array.mu.RUnlock()
}
a.array = append(a.array, array.array...)
return a
}
// Fills an array with num entries of the value of the value parameter,
// keys starting at the start_index parameter.
//
// 用value参数的值将数组填充num个条目位置由startIndex参数指定的开始。
func (a *Array) Fill(startIndex int, num int, value interface{}) *Array {
a.mu.Lock()
defer a.mu.Unlock()
if startIndex < 0 {
startIndex = 0
}
for i := startIndex; i < startIndex + num; i++ {
if i > len(a.array) - 1 {
a.array = append(a.array, value)
} else {
a.array[i] = value
}
}
return a
}
// Chunks an array into arrays with size elements.
// The last chunk may contain less than size elements.
//
// 将一个数组分割成多个数组其中每个数组的单元数目由size决定。最后一个数组的单元数目可能会少于size个。
func (a *Array) Chunk(size int) [][]interface{} {
if size < 1 {
return nil
}
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
chunks := int(math.Ceil(float64(length) / float64(size)))
var n [][]interface{}
for i, end := 0, 0; chunks > 0; chunks-- {
end = (i + 1) * size
if end > length {
end = length
}
n = append(n, a.array[i*size : end])
i++
}
return n
}
// Pad array to the specified length with a value.
// If size is positive then the array is padded on the right,
// if it's negative then on the left.
// If the absolute value of size is less than or equal to the length of the array
// then no padding takes place.
//
// 返回数组的一个拷贝并用value将其填补到size指定的长度。
// 如果size为正数则填补到数组的右侧如果为负数则从左侧开始填补。
// 如果size的绝对值小于或等于数组的长度则没有任何填补。
func (a *Array) Pad(size int, val interface{}) *Array {
a.mu.Lock()
defer a.mu.Unlock()
if size == 0 || (size > 0 && size < len(a.array)) || (size < 0 && size > -len(a.array)) {
return a
}
n := size
if size < 0 {
n = -size
}
n -= len(a.array)
tmp := make([]interface{}, n)
for i := 0; i < n; i++ {
tmp[i] = val
}
if size > 0 {
a.array = append(a.array, tmp...)
} else {
a.array = append(tmp, a.array...)
}
return a
}
// Extract a slice of the array(If in concurrent safe usage, it returns a copy of the slice; else a pointer).
// It returns the sequence of elements from the array array as specified by the offset and length parameters.
//
// 返回根据offset和size参数所指定的数组中的一段序列。
func (a *Array) SubSlice(offset, size int) []interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
if offset > len(a.array) {
return nil
}
if offset + size > len(a.array) {
size = len(a.array) - offset
}
if a.mu.IsSafe() {
s := make([]interface{}, size)
copy(s, a.array[offset:])
return s
} else {
return a.array[offset:]
}
}
// Picks one or more random entries out of an array(a copy),
// and returns the key (or keys) of the random entries.
//
// 从数组中随机取出size个元素项构成slice返回。
func (a *Array) Rand(size int) []interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
if size > len(a.array) {
size = len(a.array)
}
n := make([]interface{}, size)
for i, v := range grand.Perm(len(a.array)) {
n[i] = a.array[v]
if i == size - 1 {
break
}
}
return n
}
// Randomly shuffles the array.
//
// 随机打乱当前数组。
func (a *Array) Shuffle() *Array {
a.mu.Lock()
defer a.mu.Unlock()
for i, v := range grand.Perm(len(a.array)) {
a.array[i], a.array[v] = a.array[v], a.array[i]
}
return a
}
// Make array with elements in reverse order.
//
// 将当前数组反转。
func (a *Array) Reverse() *Array {
a.mu.Lock()
defer a.mu.Unlock()
for i, j := 0, len(a.array) - 1; i < j; i, j = i + 1, j - 1 {
a.array[i], a.array[j] = a.array[j], a.array[i]
}
return a
}
// Join array elements with a string.
//
// 使用glue字符串串连当前数组的元素项构造成新的字符串返回。
func (a *Array) Join(glue string) string {
a.mu.RLock()
defer a.mu.RUnlock()
return strings.Join(gconv.Strings(a.array), glue)
}
// Counts all the values of an array.
//
// 统计数组中所有的值出现的次数.
func (a *Array) CountValues() map[interface{}]int {
m := make(map[interface{}]int)
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
m[v]++
}
return m
}

View File

@ -0,0 +1,592 @@
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
package garray
import (
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/util/gconv"
"github.com/gogf/gf/g/util/grand"
"math"
"sort"
"strings"
)
type StringArray struct {
mu *rwmutex.RWMutex // 互斥锁
array []string // 底层数组
}
// Create an empty array.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 创建一个空的数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
func NewStringArray(unsafe...bool) *StringArray {
return NewStringArraySize(0, 0, unsafe...)
}
// Create an array with given size and cap.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 创建一个指定大小的数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
func NewStringArraySize(size int, cap int, unsafe...bool) *StringArray {
return &StringArray{
mu : rwmutex.New(unsafe...),
array : make([]string, size, cap),
}
}
// Create an array with given slice <array>.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 通过给定的slice变量创建数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
func NewStringArrayFrom(array []string, unsafe...bool) *StringArray {
return &StringArray {
mu : rwmutex.New(unsafe...),
array : array,
}
}
// Get value by index.
//
// 获取指定索引的数据项, 调用方注意判断数组边界。
func (a *StringArray) Get(index int) string {
a.mu.RLock()
defer a.mu.RUnlock()
value := a.array[index]
return value
}
// Set value by index.
//
// 设置指定索引的数据项, 调用方注意判断数组边界。
func (a *StringArray) Set(index int, value string) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
a.array[index] = value
return a
}
// Set the underlying slice array with the given <array> param.
//
// 设置底层数组变量.
func (a *StringArray) SetArray(array []string) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
a.array = array
return a
}
// Replace the array items by given <array> from the beginning of array.
//
// 使用指定数组替换到对应的索引元素值.
func (a *StringArray) Replace(array []string) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
max := len(array)
if max > len(a.array) {
max = len(a.array)
}
for i := 0; i < max; i++ {
a.array[i] = array[i]
}
return a
}
// Calculate the sum of values in an array.
//
// 对数组中的元素项求和(将元素值转换为int类型后叠加)。
func (a *StringArray) Sum() (sum int) {
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
sum += gconv.Int(v)
}
return
}
// Sort the array in increasing order.
// The param <reverse> controls whether sort
// in increasing order(default) or decreasing order
//
// 将数组排序(默认从低到高).
func (a *StringArray) Sort(reverse...bool) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
if len(reverse) > 0 && reverse[0] {
sort.Slice(a.array, func(i, j int) bool {
if strings.Compare(a.array[i], a.array[j]) < 0 {
return false
}
return true
})
} else {
sort.Strings(a.array)
}
return a
}
// Sort the array by custom function <less>.
//
// 使用自定义的排序函数将数组重新排序.
func (a *StringArray) SortFunc(less func(v1, v2 string) bool) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
sort.Slice(a.array, func(i, j int) bool {
return less(a.array[i], a.array[j])
})
return a
}
// Insert the <value> to the front of <index>.
//
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界。
func (a *StringArray) InsertBefore(index int, value string) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
rear := append([]string{}, a.array[index : ]...)
a.array = append(a.array[0 : index], value)
a.array = append(a.array, rear...)
return a
}
// Insert the <value> to the back of <index>.
//
// 在当前索引位置前插入一个数据项, 调用方注意判断数组边界。
func (a *StringArray) InsertAfter(index int, value string) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
rear := append([]string{}, a.array[index + 1:]...)
a.array = append(a.array[ 0: index + 1], value)
a.array = append(a.array, rear...)
return a
}
// Remove an item by index.
//
// 删除指定索引的数据项, 调用方注意判断数组边界。
func (a *StringArray) Remove(index int) string {
a.mu.Lock()
defer a.mu.Unlock()
// 边界删除判断,以提高删除效率
if index == 0 {
value := a.array[0]
a.array = a.array[1 : ]
return value
} else if index == len(a.array) - 1 {
value := a.array[index]
a.array = a.array[: index]
return value
}
// 如果非边界删除,会涉及到数组创建,那么删除的效率差一些
value := a.array[index]
a.array = append(a.array[ : index], a.array[index + 1 : ]...)
return value
}
// Push new items to the beginning of array.
//
// 将数据项添加到数组的最左端(索引为0)。
func (a *StringArray) PushLeft(value...string) *StringArray {
a.mu.Lock()
a.array = append(value, a.array...)
a.mu.Unlock()
return a
}
// Push new items to the end of array.
//
// 将数据项添加到数组的最右端(索引为length - 1), 等于: Append。
func (a *StringArray) PushRight(value...string) *StringArray {
a.mu.Lock()
a.array = append(a.array, value...)
a.mu.Unlock()
return a
}
// Pop an item from the beginning of array.
//
// 将最左端(索引为0)的数据项移出数组,并返回该数据项。
func (a *StringArray) PopLeft() string {
a.mu.Lock()
defer a.mu.Unlock()
value := a.array[0]
a.array = a.array[1 : ]
return value
}
// Pop an item from the end of array.
//
// 将最右端(索引为length - 1)的数据项移出数组,并返回该数据项。
func (a *StringArray) PopRight() string {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - 1
value := a.array[index]
a.array = a.array[: index]
return value
}
// Pop an random item from array.
//
// 随机将一个数据项移出数组,并返回该数据项。
func (a *StringArray) PopRand() string {
return a.Remove(grand.Intn(len(a.array)))
}
// Pop <size> items from the beginning of array.
//
// 将最左端(首部)的size个数据项移出数组并返回该数据项
func (a *StringArray) PopLefts(size int) []string {
a.mu.Lock()
defer a.mu.Unlock()
length := len(a.array)
if size > length {
size = length
}
value := a.array[0 : size]
a.array = a.array[size : ]
return value
}
// Pop <size> items from the end of array.
//
// 将最右端(尾部)的size个数据项移出数组并返回该数据项
func (a *StringArray) PopRights(size int) []string {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - size
if index < 0 {
index = 0
}
value := a.array[index :]
a.array = a.array[ : index]
return value
}
// Get items by range, returns array[start:end].
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
//
// 将最右端(尾部)的size个数据项移出数组并返回该数据项
func (a *StringArray) Range(start, end int) []string {
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
if start > length || start > end {
return nil
}
if start < 0 {
start = 0
}
if end > length {
end = length
}
array := ([]string)(nil)
if a.mu.IsSafe() {
a.mu.RLock()
defer a.mu.RUnlock()
array = make([]string, end - start)
copy(array, a.array[start : end])
} else {
array = a.array[start : end]
}
return array
}
// See PushRight.
//
// 追加数据项, 等于: PushRight。
func (a *StringArray) Append(value...string) *StringArray {
a.mu.Lock()
a.array = append(a.array, value...)
a.mu.Unlock()
return a
}
// Get the length of array.
//
// 数组长度。
func (a *StringArray) Len() int {
a.mu.RLock()
length := len(a.array)
a.mu.RUnlock()
return length
}
// Get the underlying data of array.
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
//
// 返回原始数据数组.
func (a *StringArray) Slice() []string {
array := ([]string)(nil)
if a.mu.IsSafe() {
a.mu.RLock()
defer a.mu.RUnlock()
array = make([]string, len(a.array))
copy(array, a.array)
} else {
array = a.array
}
return array
}
// Return a new array, which is a copy of current array.
//
// 克隆当前数组,返回当前数组的一个拷贝。
func (a *StringArray) Clone() (newArray *StringArray) {
a.mu.RLock()
array := make([]string, len(a.array))
copy(array, a.array)
a.mu.RUnlock()
return NewStringArrayFrom(array, !a.mu.IsSafe())
}
// Clear array.
//
// 清空数据数组。
func (a *StringArray) Clear() *StringArray {
a.mu.Lock()
if len(a.array) > 0 {
a.array = make([]string, 0)
}
a.mu.Unlock()
return a
}
// Check whether a value exists in the array.
//
// 查找指定数值是否存在。
func (a *StringArray) Contains(value string) bool {
return a.Search(value) != -1
}
// Search array by <value>, returns the index of <value>, returns -1 if not exists.
//
// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1。
func (a *StringArray) Search(value string) int {
if len(a.array) == 0 {
return -1
}
a.mu.RLock()
result := -1
for index, v := range a.array {
if strings.Compare(v, value) == 0 {
result = index
break
}
}
a.mu.RUnlock()
return result
}
// Unique the array, clear repeated values.
//
// 清理数组中重复的元素项。
func (a *StringArray) Unique() *StringArray {
a.mu.Lock()
for i := 0; i < len(a.array) - 1; i++ {
for j := i + 1; j < len(a.array); j++ {
if a.array[i] == a.array[j] {
a.array = append(a.array[ : j], a.array[j + 1 : ]...)
}
}
}
a.mu.Unlock()
return a
}
// Lock writing by callback function f.
//
// 使用自定义方法执行加锁修改操作。
func (a *StringArray) LockFunc(f func(array []string)) *StringArray {
a.mu.Lock(true)
defer a.mu.Unlock(true)
f(a.array)
return a
}
// Lock reading by callback function f.
//
// 使用自定义方法执行加锁读取操作。
func (a *StringArray) RLockFunc(f func(array []string)) *StringArray {
a.mu.RLock(true)
defer a.mu.RUnlock(true)
f(a.array)
return a
}
// Merge two arrays.
//
// 合并两个数组.
func (a *StringArray) Merge(array *StringArray) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
if a != array {
array.mu.RLock()
defer array.mu.RUnlock()
}
a.array = append(a.array, array.array...)
return a
}
// Fills an array with num entries of the value of the value parameter,
// keys starting at the start_index parameter.
//
// 用value参数的值将数组填充num个条目位置由startIndex参数指定的开始。
func (a *StringArray) Fill(startIndex int, num int, value string) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
if startIndex < 0 {
startIndex = 0
}
for i := startIndex; i < startIndex + num; i++ {
if i > len(a.array) - 1 {
a.array = append(a.array, value)
} else {
a.array[i] = value
}
}
return a
}
// Chunks an array into arrays with size elements.
// The last chunk may contain less than size elements.
//
// 将一个数组分割成多个数组其中每个数组的单元数目由size决定。最后一个数组的单元数目可能会少于size个。
func (a *StringArray) Chunk(size int) [][]string {
if size < 1 {
return nil
}
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
chunks := int(math.Ceil(float64(length) / float64(size)))
var n [][]string
for i, end := 0, 0; chunks > 0; chunks-- {
end = (i + 1) * size
if end > length {
end = length
}
n = append(n, a.array[i*size : end])
i++
}
return n
}
// Pad array to the specified length with a value.
// If size is positive then the array is padded on the right,
// if it's negative then on the left.
// If the absolute value of size is less than or equal to the length of the array
// then no padding takes place.
//
// 返回数组的一个拷贝并用value将其填补到size指定的长度。
// 如果size为正数则填补到数组的右侧如果为负数则从左侧开始填补。
// 如果size的绝对值小于或等于数组的长度则没有任何填补。
func (a *StringArray) Pad(size int, value string) *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
if size == 0 || (size > 0 && size < len(a.array)) || (size < 0 && size > -len(a.array)) {
return a
}
n := size
if size < 0 {
n = -size
}
n -= len(a.array)
tmp := make([]string, n)
for i := 0; i < n; i++ {
tmp[i] = value
}
if size > 0 {
a.array = append(a.array, tmp...)
} else {
a.array = append(tmp, a.array...)
}
return a
}
// Extract a slice of the array(If in concurrent safe usage,
// it returns a copy of the slice; else a pointer).
// It returns the sequence of elements from the array array as specified
// by the offset and length parameters.
//
// 返回根据offset和size参数所指定的数组中的一段序列。
func (a *StringArray) SubSlice(offset, size int) []string {
a.mu.RLock()
defer a.mu.RUnlock()
if offset > len(a.array) {
return nil
}
if offset + size > len(a.array) {
size = len(a.array) - offset
}
if a.mu.IsSafe() {
s := make([]string, size)
copy(s, a.array[offset:])
return s
} else {
return a.array[offset:]
}
}
// Picks one or more random entries out of an array(a copy),
// and returns the key (or keys) of the random entries.
//
// 从数组中随机取出size个元素项构成slice返回。
func (a *StringArray) Rand(size int) []string {
a.mu.RLock()
defer a.mu.RUnlock()
if size > len(a.array) {
size = len(a.array)
}
n := make([]string, size)
for i, v := range grand.Perm(len(a.array)) {
n[i] = a.array[v]
if i == size - 1 {
break
}
}
return n
}
// Randomly shuffles the array.
//
// 随机打乱当前数组。
func (a *StringArray) Shuffle() *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
for i, v := range grand.Perm(len(a.array)) {
a.array[i], a.array[v] = a.array[v], a.array[i]
}
return a
}
// Make array with elements in reverse order.
//
// 将当前数组反转。
func (a *StringArray) Reverse() *StringArray {
a.mu.Lock()
defer a.mu.Unlock()
for i, j := 0, len(a.array) - 1; i < j; i, j = i + 1, j - 1 {
a.array[i], a.array[j] = a.array[j], a.array[i]
}
return a
}
// Join array elements with a string.
//
// 使用glue字符串串连当前数组的元素项构造成新的字符串返回。
func (a *StringArray) Join(glue string) string {
a.mu.RLock()
defer a.mu.RUnlock()
return strings.Join(a.array, glue)
}

View File

@ -1,29 +1,46 @@
// 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/container/internal/rwmutex"
"github.com/gogf/gf/g/container/gtype"
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/util/gconv"
"github.com/gogf/gf/g/util/grand"
"math"
"sort"
"strings"
)
// 默认按照从低到高进行排序
// 默认按照从小到大进行排序
type SortedIntArray struct {
mu *rwmutex.RWMutex // 互斥锁
cap int // 初始化设置的数组容量
array []int // 底层数组
unique *gtype.Bool // 是否要求不能重复(默认false)
compareFunc func(v1, v2 int) int // 比较函数,返回值 -1: v1 < v20: v1 == v21: v1 > v2
}
// 创建一个排序的int数组
func NewSortedIntArray(cap int, safe...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(safe...),
mu : rwmutex.New(unsafe...),
array : make([]int, 0, cap),
unique : gtype.NewBool(),
compareFunc : func(v1, v2 int) int {
@ -38,10 +55,45 @@ func NewSortedIntArray(cap int, safe...bool) *SortedIntArray {
}
}
// 添加加数据项
func (a *SortedIntArray) Add(values...int) {
// Create an array with given slice <array>.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 通过给定的slice变量创建排序数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
func NewSortedIntArrayFrom(array []int, unsafe...bool) *SortedIntArray {
a := NewSortedIntArraySize(0, unsafe...)
a.array = array
sort.Ints(a.array)
return a
}
// Set the underlying slice array with the given <array> param.
//
// 设置底层数组变量.
func (a *SortedIntArray) SetArray(array []int) *SortedIntArray {
a.mu.Lock()
defer a.mu.Unlock()
a.array = array
sort.Ints(a.array)
return a
}
// Sort the array in increasing order.
//
// 将数组排序(默认从低到高).
func (a *SortedIntArray) Sort() *SortedIntArray {
a.mu.Lock()
defer a.mu.Unlock()
sort.Ints(a.array)
return a
}
// And values to sorted array, the array always keeps sorted.
//
// 添加数据项.
func (a *SortedIntArray) Add(values...int) *SortedIntArray {
if len(values) == 0 {
return
return a
}
a.mu.Lock()
defer a.mu.Unlock()
@ -62,9 +114,12 @@ func (a *SortedIntArray) Add(values...int) {
a.array = append(a.array[0 : index], value)
a.array = append(a.array, rear...)
}
return a
}
// 获取指定索引的数据项, 调用方注意判断数组边界
// Get value by index.
//
// 获取指定索引的数据项, 调用方注意判断数组边界。
func (a *SortedIntArray) Get(index int) int {
a.mu.RLock()
defer a.mu.RUnlock()
@ -72,7 +127,9 @@ func (a *SortedIntArray) Get(index int) int {
return value
}
// 删除指定索引的数据项, 调用方注意判断数组边界
// Remove an item by index.
//
// 删除指定索引的数据项, 调用方注意判断数组边界。
func (a *SortedIntArray) Remove(index int) int {
a.mu.Lock()
defer a.mu.Unlock()
@ -92,7 +149,9 @@ func (a *SortedIntArray) Remove(index int) int {
return value
}
// 将最左端(索引为0)的数据项移出数组,并返回该数据项
// Push new items to the beginning of array.
//
// 将数据项添加到数组的最左端(索引为0)。
func (a *SortedIntArray) PopLeft() int {
a.mu.Lock()
defer a.mu.Unlock()
@ -101,7 +160,9 @@ func (a *SortedIntArray) PopLeft() int {
return value
}
// 将最右端(索引为length - 1)的数据项移出数组,并返回该数据项
// Push new items to the end of array.
//
// 将数据项添加到数组的最右端(索引为length - 1)。
func (a *SortedIntArray) PopRight() int {
a.mu.Lock()
defer a.mu.Unlock()
@ -111,7 +172,76 @@ func (a *SortedIntArray) PopRight() int {
return value
}
// 数组长度
// Pop an random item from array.
//
// 随机将一个数据项移出数组,并返回该数据项。
func (a *SortedIntArray) PopRand() int {
return a.Remove(grand.Intn(len(a.array)))
}
// Pop <size> items from the beginning of array.
//
// 将最左端(首部)的size个数据项移出数组并返回该数据项
func (a *SortedIntArray) PopLefts(size int) []int {
a.mu.Lock()
defer a.mu.Unlock()
length := len(a.array)
if size > length {
size = length
}
value := a.array[0 : size]
a.array = a.array[size : ]
return value
}
// Pop <size> items from the end of array.
//
// 将最右端(尾部)的size个数据项移出数组并返回该数据项
func (a *SortedIntArray) PopRights(size int) []int {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - size
if index < 0 {
index = 0
}
value := a.array[index :]
a.array = a.array[ : index]
return value
}
// Get items by range, returns array[start:end].
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
//
// 将最右端(尾部)的size个数据项移出数组并返回该数据项
func (a *SortedIntArray) Range(start, end int) []int {
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
if start > length || start > end {
return nil
}
if start < 0 {
start = 0
}
if end > length {
end = length
}
array := ([]int)(nil)
if a.mu.IsSafe() {
a.mu.RLock()
defer a.mu.RUnlock()
array = make([]int, end - start)
copy(array, a.array[start : end])
} else {
array = a.array[start : end]
}
return array
}
// Get the length of array.
//
// 数组长度。
func (a *SortedIntArray) Len() int {
a.mu.RLock()
length := len(a.array)
@ -119,28 +249,54 @@ func (a *SortedIntArray) Len() int {
return length
}
// 返回原始数据数组
// Calculate the sum of values in an array.
//
// 对数组中的元素项求和。
func (a *SortedIntArray) Sum() (sum int) {
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
sum += v
}
return
}
// Get the underlying data of array.
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
//
// 返回原始数据数组.
func (a *SortedIntArray) Slice() []int {
array := ([]int)(nil)
if a.mu.IsSafe() {
a.mu.RLock()
defer a.mu.RUnlock()
array = make([]int, len(a.array))
for k, v := range a.array {
array[k] = v
}
a.mu.RUnlock()
copy(array, a.array)
} else {
array = a.array
}
return array
}
// 查找指定数值的索引位置,返回索引位置(具体匹配位置或者最后对比位置)及查找结果
// 返回值: 最后比较位置, 比较结果
func (a *SortedIntArray) Search(value int) (index int, result int) {
return a.binSearch(value, true)
// Check whether a value exists in the array.
//
// 查找指定数值是否存在。
func (a *SortedIntArray) Contains(value int) bool {
return a.Search(value) == 0
}
// Search array by <value>, returns the index of <value>, returns -1 if not exists.
//
// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1。
func (a *SortedIntArray) Search(value int) (index int) {
index, _ = a.binSearch(value, true)
return
}
// Binary search.
//
// 二分查找.
func (a *SortedIntArray) binSearch(value int, lock bool) (index int, result int) {
if len(a.array) == 0 {
return -1, -2
@ -156,27 +312,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 +353,140 @@ func (a *SortedIntArray) doUnique() {
}
}
a.mu.Unlock()
return a
}
// 清空数据数组
func (a *SortedIntArray) Clear() {
// Return a new array, which is a copy of current array.
//
// 克隆当前数组,返回当前数组的一个拷贝。
func (a *SortedIntArray) Clone() (newArray *SortedIntArray) {
a.mu.RLock()
array := make([]int, len(a.array))
copy(array, a.array)
a.mu.RUnlock()
return NewSortedIntArrayFrom(array, !a.mu.IsSafe())
}
// Clear array.
//
// 清空数据数组。
func (a *SortedIntArray) Clear() *SortedIntArray {
a.mu.Lock()
if len(a.array) > 0 {
a.array = make([]int, 0, a.cap)
a.array = make([]int, 0)
}
a.mu.Unlock()
return a
}
// 使用自定义方法执行加锁修改操作
func (a *SortedIntArray) LockFunc(f func(array []int)) {
// Lock writing by callback function f.
//
// 使用自定义方法执行加锁修改操作。
func (a *SortedIntArray) LockFunc(f func(array []int)) *SortedIntArray {
a.mu.Lock(true)
defer a.mu.Unlock(true)
f(a.array)
return a
}
// 使用自定义方法执行加锁读取操作
func (a *SortedIntArray) RLockFunc(f func(array []int)) {
// Lock reading by callback function f.
//
// 使用自定义方法执行加锁读取操作。
func (a *SortedIntArray) RLockFunc(f func(array []int)) *SortedIntArray {
a.mu.RLock(true)
defer a.mu.RUnlock(true)
f(a.array)
return a
}
// Merge two arrays.
//
// 合并两个数组.
func (a *SortedIntArray) Merge(array *SortedIntArray) *SortedIntArray {
a.mu.Lock()
defer a.mu.Unlock()
if a != array {
array.mu.RLock()
defer array.mu.RUnlock()
}
a.array = append(a.array, array.array...)
sort.Ints(a.array)
return a
}
// Chunks an array into arrays with size elements.
// The last chunk may contain less than size elements.
//
// 将一个数组分割成多个数组其中每个数组的单元数目由size决定。最后一个数组的单元数目可能会少于size个。
func (a *SortedIntArray) Chunk(size int) [][]int {
if size < 1 {
return nil
}
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
chunks := int(math.Ceil(float64(length) / float64(size)))
var n [][]int
for i, end := 0, 0; chunks > 0; chunks-- {
end = (i + 1) * size
if end > length {
end = length
}
n = append(n, a.array[i*size : end])
i++
}
return n
}
// Extract a slice of the array(If in concurrent safe usage,
// it returns a copy of the slice; else a pointer).
// It returns the sequence of elements from the array array as specified
// by the offset and length parameters.
//
// 返回根据offset和size参数所指定的数组中的一段序列。
func (a *SortedIntArray) SubSlice(offset, size int) []int {
a.mu.RLock()
defer a.mu.RUnlock()
if offset > len(a.array) {
return nil
}
if offset + size > len(a.array) {
size = len(a.array) - offset
}
if a.mu.IsSafe() {
s := make([]int, size)
copy(s, a.array[offset:])
return s
} else {
return a.array[offset:]
}
}
// Picks one or more random entries out of an array(a copy),
// and returns the key (or keys) of the random entries.
//
// 从数组中随机取出size个元素项构成slice返回。
func (a *SortedIntArray) Rand(size int) []int {
a.mu.RLock()
defer a.mu.RUnlock()
if size > len(a.array) {
size = len(a.array)
}
n := make([]int, size)
for i, v := range grand.Perm(len(a.array)) {
n[i] = a.array[v]
if i == size - 1 {
break
}
}
return n
}
// Join array elements with a string.
//
// 使用glue字符串串连当前数组的元素项构造成新的字符串返回。
func (a *SortedIntArray) Join(glue string) string {
a.mu.RLock()
defer a.mu.RUnlock()
return strings.Join(gconv.Strings(a.array), glue)
}

View File

@ -1,38 +1,105 @@
// 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/container/internal/rwmutex"
"github.com/gogf/gf/g/container/gtype"
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/util/gconv"
"github.com/gogf/gf/g/util/grand"
"math"
"sort"
"strings"
)
// 默认按照从低到高进行排序
// 默认按照从小到大进行排序
type SortedArray struct {
mu *rwmutex.RWMutex // 互斥锁
cap int // 初始化设置的数组容量
array []interface{} // 底层数组
unique *gtype.Bool // 是否要求不能重复
compareFunc func(v1, v2 interface{}) int // 比较函数,返回值 -1: v1 < v20: v1 == v21: v1 > v2
}
func NewSortedArray(cap int, compareFunc func(v1, v2 interface{}) int, safe...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(safe...),
mu : rwmutex.New(unsafe...),
unique : gtype.NewBool(),
array : make([]interface{}, 0, cap),
compareFunc : compareFunc,
}
}
// 添加加数据项
func (a *SortedArray) Add(values...interface{}) {
// Create an array with given slice <array>.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 通过给定的slice变量创建排序数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
func NewSortedArrayFrom(array []interface{}, compareFunc func(v1, v2 interface{}) int, unsafe...bool) *SortedArray {
a := NewSortedArraySize(0, compareFunc, unsafe...)
a.array = array
sort.Slice(a.array, func(i, j int) bool {
return a.compareFunc(a.array[i], a.array[j]) < 0
})
return a
}
// Set the underlying slice array with the given <array> param.
//
// 设置底层数组变量.
func (a *SortedArray) SetArray(array []interface{}) *SortedArray {
a.mu.Lock()
defer a.mu.Unlock()
a.array = array
sort.Slice(a.array, func(i, j int) bool {
return a.compareFunc(a.array[i], a.array[j]) < 0
})
return a
}
// Sort the array by comparing function.
//
// 将数组按照比较方法进行排序.
func (a *SortedArray) Sort() *SortedArray {
a.mu.Lock()
defer a.mu.Unlock()
sort.Slice(a.array, func(i, j int) bool {
return a.compareFunc(a.array[i], a.array[j]) < 0
})
return a
}
// And values to sorted array, the array always keeps sorted.
//
// 添加数据项.
func (a *SortedArray) Add(values...interface{}) *SortedArray {
if len(values) == 0 {
return
return a
}
a.mu.Lock()
defer a.mu.Unlock()
@ -53,9 +120,12 @@ func (a *SortedArray) Add(values...interface{}) {
a.array = append(a.array[0 : index], value)
a.array = append(a.array, rear...)
}
return a
}
// 获取指定索引的数据项, 调用方注意判断数组边界
// Get value by index.
//
// 获取指定索引的数据项, 调用方注意判断数组边界。
func (a *SortedArray) Get(index int) interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
@ -63,7 +133,9 @@ func (a *SortedArray) Get(index int) interface{} {
return value
}
// 删除指定索引的数据项, 调用方注意判断数组边界
// Remove an item by index.
//
// 删除指定索引的数据项, 调用方注意判断数组边界。
func (a *SortedArray) Remove(index int) interface{} {
a.mu.Lock()
defer a.mu.Unlock()
@ -83,7 +155,9 @@ func (a *SortedArray) Remove(index int) interface{} {
return value
}
// 将最左端(索引为0)的数据项移出数组,并返回该数据项
// Push new items to the beginning of array.
//
// 将数据项添加到数组的最左端(索引为0)。
func (a *SortedArray) PopLeft() interface{} {
a.mu.Lock()
defer a.mu.Unlock()
@ -92,7 +166,9 @@ func (a *SortedArray) PopLeft() interface{} {
return value
}
// 将最右端(索引为length - 1)的数据项移出数组,并返回该数据项
// Push new items to the end of array.
//
// 将数据项添加到数组的最右端(索引为length - 1)。
func (a *SortedArray) PopRight() interface{} {
a.mu.Lock()
defer a.mu.Unlock()
@ -102,7 +178,88 @@ func (a *SortedArray) PopRight() interface{} {
return value
}
// 数组长度
// Pop an random item from array.
//
// 随机将一个数据项移出数组,并返回该数据项。
func (a *SortedArray) PopRand() interface{} {
return a.Remove(grand.Intn(len(a.array)))
}
// Pop <size> items from the beginning of array.
//
// 将最左端(首部)的size个数据项移出数组并返回该数据项
func (a *SortedArray) PopLefts(size int) []interface{} {
a.mu.Lock()
defer a.mu.Unlock()
length := len(a.array)
if size > length {
size = length
}
value := a.array[0 : size]
a.array = a.array[size : ]
return value
}
// Pop <size> items from the end of array.
//
// 将最右端(尾部)的size个数据项移出数组并返回该数据项
func (a *SortedArray) PopRights(size int) []interface{} {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - size
if index < 0 {
index = 0
}
value := a.array[index :]
a.array = a.array[ : index]
return value
}
// Get items by range, returns array[start:end].
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
//
// 将最右端(尾部)的size个数据项移出数组并返回该数据项
func (a *SortedArray) Range(start, end int) []interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
if start > length || start > end {
return nil
}
if start < 0 {
start = 0
}
if end > length {
end = length
}
array := ([]interface{})(nil)
if a.mu.IsSafe() {
a.mu.RLock()
defer a.mu.RUnlock()
array = make([]interface{}, end - start)
copy(array, a.array[start : end])
} else {
array = a.array[start : end]
}
return array
}
// Calculate the sum of values in an array.
//
// 对数组中的元素项求和(将元素值转换为int类型后叠加)。
func (a *SortedArray) Sum() (sum int) {
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
sum += gconv.Int(v)
}
return
}
// Get the length of array.
//
// 数组长度。
func (a *SortedArray) Len() int {
a.mu.RLock()
length := len(a.array)
@ -110,30 +267,43 @@ func (a *SortedArray) Len() int {
return length
}
// 返回原始数据数组
// Get the underlying data of array.
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
//
// 返回原始数据数组.
func (a *SortedArray) Slice() []interface{} {
array := ([]interface{})(nil)
if a.mu.IsSafe() {
a.mu.RLock()
defer a.mu.RUnlock()
array = make([]interface{}, len(a.array))
for k, v := range a.array {
array[k] = v
}
a.mu.RUnlock()
copy(array, a.array)
} else {
array = a.array
}
return array
}
// 查找指定数值的索引位置,返回索引位置(具体匹配位置或者最后对比位置)及查找结果
// 返回值: 最后比较位置, 比较结果
func (a *SortedArray) Search(value interface{}) (index int, result int) {
return a.binSearch(value, true)
// Check whether a value exists in the array.
//
// 查找指定数值是否存在。
func (a *SortedArray) Contains(value interface{}) bool {
return a.Search(value) == 0
}
// 查找指定数值的索引位置,返回索引位置(具体匹配位置或者最后对比位置)及查找结果
// 返回值: 最后比较位置, 比较结果
// Search array by <value>, returns the index of <value>, returns -1 if not exists.
//
// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1。
func (a *SortedArray) Search(value interface{}) (index int) {
index, _ = a.binSearch(value, true)
return
}
// Binary search.
//
// 二分查找。查找指定数值的索引位置,返回索引位置(具体匹配位置或者最后对比位置)及查找结果
// 返回值: 最后比较位置, 比较结果。
func (a *SortedArray) binSearch(value interface{}, lock bool)(index int, result int) {
if len(a.array) == 0 {
return -1, -2
@ -149,27 +319,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 +360,142 @@ func (a *SortedArray) doUnique() {
i++
}
}
return a
}
// 清空数据数组
func (a *SortedArray) Clear() {
// Return a new array, which is a copy of current array.
//
// 克隆当前数组,返回当前数组的一个拷贝。
func (a *SortedArray) Clone() (newArray *SortedArray) {
a.mu.RLock()
array := make([]interface{}, len(a.array))
copy(array, a.array)
a.mu.RUnlock()
return NewSortedArrayFrom(array, a.compareFunc, !a.mu.IsSafe())
}
// Clear array.
//
// 清空数据数组。
func (a *SortedArray) Clear() *SortedArray {
a.mu.Lock()
if len(a.array) > 0 {
a.array = make([]interface{}, 0, a.cap)
a.array = make([]interface{}, 0)
}
a.mu.Unlock()
return a
}
// 使用自定义方法执行加锁修改操作
func (a *SortedArray) LockFunc(f func(array []interface{})) {
// Lock writing by callback function f.
//
// 使用自定义方法执行加锁修改操作。
func (a *SortedArray) LockFunc(f func(array []interface{})) *SortedArray {
a.mu.Lock(true)
defer a.mu.Unlock(true)
f(a.array)
return a
}
// 使用自定义方法执行加锁读取操作
func (a *SortedArray) RLockFunc(f func(array []interface{})) {
// Lock reading by callback function f.
//
// 使用自定义方法执行加锁读取操作。
func (a *SortedArray) RLockFunc(f func(array []interface{})) *SortedArray {
a.mu.RLock(true)
defer a.mu.RUnlock(true)
f(a.array)
return a
}
// Merge two arrays.
//
// 合并两个数组.
func (a *SortedArray) Merge(array *SortedArray) *SortedArray {
a.mu.Lock()
defer a.mu.Unlock()
if a != array {
array.mu.RLock()
defer array.mu.RUnlock()
}
a.array = append(a.array, array.array...)
sort.Slice(a.array, func(i, j int) bool {
return a.compareFunc(a.array[i], a.array[j]) < 0
})
return a
}
// Chunks an array into arrays with size elements.
// The last chunk may contain less than size elements.
//
// 将一个数组分割成多个数组其中每个数组的单元数目由size决定。最后一个数组的单元数目可能会少于size个。
func (a *SortedArray) Chunk(size int) [][]interface{} {
if size < 1 {
return nil
}
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
chunks := int(math.Ceil(float64(length) / float64(size)))
var n [][]interface{}
for i, end := 0, 0; chunks > 0; chunks-- {
end = (i + 1) * size
if end > length {
end = length
}
n = append(n, a.array[i*size : end])
i++
}
return n
}
// Extract a slice of the array(If in concurrent safe usage,
// it returns a copy of the slice; else a pointer).
// It returns the sequence of elements from the array array as specified
// by the offset and length parameters.
//
// 返回根据offset和size参数所指定的数组中的一段序列。
func (a *SortedArray) SubSlice(offset, size int) []interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
if offset > len(a.array) {
return nil
}
if offset + size > len(a.array) {
size = len(a.array) - offset
}
if a.mu.IsSafe() {
s := make([]interface{}, size)
copy(s, a.array[offset:])
return s
} else {
return a.array[offset:]
}
}
// Picks one or more random entries out of an array(a copy),
// and returns the key (or keys) of the random entries.
//
// 从数组中随机取出size个元素项构成slice返回。
func (a *SortedArray) Rand(size int) []interface{} {
a.mu.RLock()
defer a.mu.RUnlock()
if size > len(a.array) {
size = len(a.array)
}
n := make([]interface{}, size)
for i, v := range grand.Perm(len(a.array)) {
n[i] = a.array[v]
if i == size - 1 {
break
}
}
return n
}
// Join array elements with a string.
//
// 使用glue字符串串连当前数组的元素项构造成新的字符串返回。
func (a *SortedArray) Join(glue string) string {
a.mu.RLock()
defer a.mu.RUnlock()
return strings.Join(gconv.Strings(a.array), glue)
}

View File

@ -1,29 +1,46 @@
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.
// You can obtain one at https://github.com/gogf/gf.
package garray
import (
"gitee.com/johng/gf/g/container/gtype"
"github.com/gogf/gf/g/container/gtype"
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/util/gconv"
"github.com/gogf/gf/g/util/grand"
"math"
"sort"
"strings"
"gitee.com/johng/gf/g/container/internal/rwmutex"
)
// 默认按照从低到高进行排序
// 默认按照从小到大进行排序
type SortedStringArray struct {
mu *rwmutex.RWMutex // 互斥锁
cap int // 初始化设置的数组容量
array []string // 底层数组
unique *gtype.Bool // 是否要求不能重复
compareFunc func(v1, v2 string) int // 比较函数,返回值 -1: v1 < v20: v1 == v21: v1 > v2
}
func NewSortedStringArray(cap int, safe...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(safe...),
mu : rwmutex.New(unsafe...),
array : make([]string, 0, cap),
unique : gtype.NewBool(),
compareFunc : func(v1, v2 string) int {
@ -32,10 +49,45 @@ func NewSortedStringArray(cap int, safe...bool) *SortedStringArray {
}
}
// 添加加数据项
func (a *SortedStringArray) Add(values...string) {
// Create an array with given slice <array>.
// The param <unsafe> used to specify whether using array with un-concurrent-safety,
// which is false in default, means concurrent-safe in default.
//
// 通过给定的slice变量创建排序数组对象参数unsafe用于指定是否用于非并发安全场景默认为false表示并发安全。
func NewSortedStringArrayFrom(array []string, unsafe...bool) *SortedStringArray {
a := NewSortedStringArraySize(0, unsafe...)
a.array = array
sort.Strings(a.array)
return a
}
// Set the underlying slice array with the given <array> param.
//
// 设置底层数组变量.
func (a *SortedStringArray) SetArray(array []string) *SortedStringArray {
a.mu.Lock()
defer a.mu.Unlock()
a.array = array
sort.Strings(a.array)
return a
}
// Sort the array in increasing order.
//
// 将数组排序(默认从低到高).
func (a *SortedStringArray) Sort() *SortedStringArray {
a.mu.Lock()
defer a.mu.Unlock()
sort.Strings(a.array)
return a
}
// And values to sorted array, the array always keeps sorted.
//
// 添加数据项.
func (a *SortedStringArray) Add(values...string) *SortedStringArray {
if len(values) == 0 {
return
return a
}
a.mu.Lock()
defer a.mu.Unlock()
@ -56,9 +108,12 @@ func (a *SortedStringArray) Add(values...string) {
a.array = append(a.array[0 : index], value)
a.array = append(a.array, rear...)
}
return a
}
// 获取指定索引的数据项, 调用方注意判断数组边界
// Get value by index.
//
// 获取指定索引的数据项, 调用方注意判断数组边界。
func (a *SortedStringArray) Get(index int) string {
a.mu.RLock()
defer a.mu.RUnlock()
@ -66,7 +121,9 @@ func (a *SortedStringArray) Get(index int) string {
return value
}
// 删除指定索引的数据项, 调用方注意判断数组边界
// Remove an item by index.
//
// 删除指定索引的数据项, 调用方注意判断数组边界。
func (a *SortedStringArray) Remove(index int) string {
a.mu.Lock()
defer a.mu.Unlock()
@ -86,7 +143,9 @@ func (a *SortedStringArray) Remove(index int) string {
return value
}
// 将最左端(索引为0)的数据项移出数组,并返回该数据项
// Push new items to the beginning of array.
//
// 将数据项添加到数组的最左端(索引为0)。
func (a *SortedStringArray) PopLeft() string {
a.mu.Lock()
defer a.mu.Unlock()
@ -95,7 +154,9 @@ func (a *SortedStringArray) PopLeft() string {
return value
}
// 将最右端(索引为length - 1)的数据项移出数组,并返回该数据项
// Push new items to the end of array.
//
// 将数据项添加到数组的最右端(索引为length - 1)。
func (a *SortedStringArray) PopRight() string {
a.mu.Lock()
defer a.mu.Unlock()
@ -105,7 +166,88 @@ func (a *SortedStringArray) PopRight() string {
return value
}
// 数组长度
// Pop an random item from array.
//
// 随机将一个数据项移出数组,并返回该数据项。
func (a *SortedStringArray) PopRand() string {
return a.Remove(grand.Intn(len(a.array)))
}
// Pop <size> items from the beginning of array.
//
// 将最左端(首部)的size个数据项移出数组并返回该数据项
func (a *SortedStringArray) PopLefts(size int) []string {
a.mu.Lock()
defer a.mu.Unlock()
length := len(a.array)
if size > length {
size = length
}
value := a.array[0 : size]
a.array = a.array[size : ]
return value
}
// Pop <size> items from the end of array.
//
// 将最右端(尾部)的size个数据项移出数组并返回该数据项
func (a *SortedStringArray) PopRights(size int) []string {
a.mu.Lock()
defer a.mu.Unlock()
index := len(a.array) - size
if index < 0 {
index = 0
}
value := a.array[index :]
a.array = a.array[ : index]
return value
}
// Get items by range, returns array[start:end].
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
//
// 将最右端(尾部)的size个数据项移出数组并返回该数据项
func (a *SortedStringArray) Range(start, end int) []string {
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
if start > length || start > end {
return nil
}
if start < 0 {
start = 0
}
if end > length {
end = length
}
array := ([]string)(nil)
if a.mu.IsSafe() {
a.mu.RLock()
defer a.mu.RUnlock()
array = make([]string, end - start)
copy(array, a.array[start : end])
} else {
array = a.array[start : end]
}
return array
}
// Calculate the sum of values in an array.
//
// 对数组中的元素项求和(将元素值转换为int类型后叠加)。
func (a *SortedStringArray) Sum() (sum int) {
a.mu.RLock()
defer a.mu.RUnlock()
for _, v := range a.array {
sum += gconv.Int(v)
}
return
}
// Get the length of array.
//
// 数组长度。
func (a *SortedStringArray) Len() int {
a.mu.RLock()
length := len(a.array)
@ -113,28 +255,42 @@ func (a *SortedStringArray) Len() int {
return length
}
// 返回原始数据数组
// Get the underlying data of array.
// Be aware that, if in concurrent-safe usage, it returns a copy of slice;
// else a pointer to the underlying data.
//
// 返回原始数据数组.
func (a *SortedStringArray) Slice() []string {
array := ([]string)(nil)
if a.mu.IsSafe() {
a.mu.RLock()
defer a.mu.RUnlock()
array = make([]string, len(a.array))
for k, v := range a.array {
array[k] = v
}
a.mu.RUnlock()
copy(array, a.array)
} else {
array = a.array
}
return array
}
// 查找指定数值的索引位置,返回索引位置(具体匹配位置或者最后对比位置)及查找结果
// 返回值: 最后比较位置, 比较结果
func (a *SortedStringArray) Search(value string) (index int, result int) {
return a.binSearch(value, true)
// Check whether a value exists in the array.
//
// 查找指定数值是否存在。
func (a *SortedStringArray) Contains(value string) bool {
return a.Search(value) == 0
}
// Search array by <value>, returns the index of <value>, returns -1 if not exists.
//
// 查找指定数值的索引位置,返回索引位置,如果查找不到则返回-1。
func (a *SortedStringArray) Search(value string) (index int) {
index, _ = a.binSearch(value, true)
return
}
// Binary search.
//
// 二分查找.
func (a *SortedStringArray) binSearch(value string, lock bool) (index int, result int) {
if len(a.array) == 0 {
return -1, -2
@ -150,27 +306,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 +347,140 @@ func (a *SortedStringArray) doUnique() {
}
}
a.mu.Unlock()
return a
}
// 清空数据数组
func (a *SortedStringArray) Clear() {
// Return a new array, which is a copy of current array.
//
// 克隆当前数组,返回当前数组的一个拷贝。
func (a *SortedStringArray) Clone() (newArray *SortedStringArray) {
a.mu.RLock()
array := make([]string, len(a.array))
copy(array, a.array)
a.mu.RUnlock()
return NewSortedStringArrayFrom(array, !a.mu.IsSafe())
}
// Clear array.
//
// 清空数据数组。
func (a *SortedStringArray) Clear() *SortedStringArray {
a.mu.Lock()
if len(a.array) > 0 {
a.array = make([]string, 0, a.cap)
a.array = make([]string, 0)
}
a.mu.Unlock()
return a
}
// 使用自定义方法执行加锁修改操作
func (a *SortedStringArray) LockFunc(f func(array []string)) {
// Lock writing by callback function f.
//
// 使用自定义方法执行加锁修改操作。
func (a *SortedStringArray) LockFunc(f func(array []string)) *SortedStringArray {
a.mu.Lock(true)
defer a.mu.Unlock(true)
f(a.array)
return a
}
// 使用自定义方法执行加锁读取操作
func (a *SortedStringArray) RLockFunc(f func(array []string)) {
// Lock reading by callback function f.
//
// 使用自定义方法执行加锁读取操作。
func (a *SortedStringArray) RLockFunc(f func(array []string)) *SortedStringArray {
a.mu.RLock(true)
defer a.mu.RUnlock(true)
f(a.array)
return a
}
// Merge two arrays.
//
// 合并两个数组.
func (a *SortedStringArray) Merge(array *SortedStringArray) *SortedStringArray {
a.mu.Lock()
defer a.mu.Unlock()
if a != array {
array.mu.RLock()
defer array.mu.RUnlock()
}
a.array = append(a.array, array.array...)
sort.Strings(a.array)
return a
}
// Chunks an array into arrays with size elements.
// The last chunk may contain less than size elements.
//
// 将一个数组分割成多个数组其中每个数组的单元数目由size决定。最后一个数组的单元数目可能会少于size个。
func (a *SortedStringArray) Chunk(size int) [][]string {
if size < 1 {
return nil
}
a.mu.RLock()
defer a.mu.RUnlock()
length := len(a.array)
chunks := int(math.Ceil(float64(length) / float64(size)))
var n [][]string
for i, end := 0, 0; chunks > 0; chunks-- {
end = (i + 1) * size
if end > length {
end = length
}
n = append(n, a.array[i*size : end])
i++
}
return n
}
// Extract a slice of the array(If in concurrent safe usage,
// it returns a copy of the slice; else a pointer).
// It returns the sequence of elements from the array array as specified
// by the offset and length parameters.
//
// 返回根据offset和size参数所指定的数组中的一段序列。
func (a *SortedStringArray) SubSlice(offset, size int) []string {
a.mu.RLock()
defer a.mu.RUnlock()
if offset > len(a.array) {
return nil
}
if offset + size > len(a.array) {
size = len(a.array) - offset
}
if a.mu.IsSafe() {
s := make([]string, size)
copy(s, a.array[offset:])
return s
} else {
return a.array[offset:]
}
}
// Picks one or more random entries out of an array(a copy),
// and returns the key (or keys) of the random entries.
//
// 从数组中随机取出size个元素项构成slice返回。
func (a *SortedStringArray) Rand(size int) []string {
a.mu.RLock()
defer a.mu.RUnlock()
if size > len(a.array) {
size = len(a.array)
}
n := make([]string, size)
for i, v := range grand.Perm(len(a.array)) {
n[i] = a.array[v]
if i == size - 1 {
break
}
}
return n
}
// Join array elements with a string.
//
// 使用glue字符串串连当前数组的元素项构造成新的字符串返回。
func (a *SortedStringArray) Join(glue string) string {
a.mu.RLock()
defer a.mu.RUnlock()
return strings.Join(a.array, glue)
}

View File

@ -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/container/internal/rwmutex"
)
type StringArray struct {
mu *rwmutex.RWMutex // 互斥锁
cap int // 初始化设置的数组容量
size int // 初始化设置的数组大小
array []string // 底层数组
}
func NewStringArray(size int, cap int, safe...bool) *StringArray {
a := &StringArray{
mu : rwmutex.New(safe...),
}
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)
}

View File

@ -0,0 +1,43 @@
// 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=".*" -benchmem
package garray_test
import (
"github.com/gogf/gf/g/container/garray"
"testing"
)
var (
sortedIntArray = garray.NewSortedIntArray()
)
func BenchmarkSortedIntArray_Add(b *testing.B) {
b.N = 1000
for i := 0; i < b.N; i++ {
sortedIntArray.Add(i)
}
}
func BenchmarkSortedIntArray_Search(b *testing.B) {
for i := 0; i < b.N; i++ {
sortedIntArray.Search(i)
}
}
func BenchmarkSortedIntArray_PopLeft(b *testing.B) {
for i := 0; i < b.N; i++ {
sortedIntArray.PopLeft()
}
}
func BenchmarkSortedIntArray_PopRight(b *testing.B) {
for i := 0; i < b.N; i++ {
sortedIntArray.PopLeft()
}
}

View File

@ -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++ {

View File

@ -0,0 +1,192 @@
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// go test *.go
package garray_test
import (
"github.com/gogf/gf/g/container/garray"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
func Test_IntArray_Basic(t *testing.T) {
gtest.Case(t, func() {
expect := []int{0, 1, 2, 3}
array := garray.NewIntArrayFrom(expect)
gtest.Assert(array.Slice(), expect)
array.Set(0, 100)
gtest.Assert(array.Get(0), 100)
gtest.Assert(array.Get(1), 1)
gtest.Assert(array.Search(100), 0)
gtest.Assert(array.Contains(100), true)
gtest.Assert(array.Remove(0), 100)
gtest.Assert(array.Contains(100), false)
array.Append(4)
gtest.Assert(array.Len(), 4)
array.InsertBefore(0, 100)
array.InsertAfter(0, 200)
gtest.Assert(array.Slice(), []int{100, 200, 1, 2, 3, 4})
array.InsertBefore(5, 300)
array.InsertAfter(6, 400)
gtest.Assert(array.Slice(), []int{100, 200, 1, 2, 3, 300, 4, 400})
gtest.Assert(array.Clear().Len(), 0)
})
}
func TestIntArray_Sort(t *testing.T) {
gtest.Case(t, func() {
expect1 := []int{0, 1, 2, 3}
expect2 := []int{3, 2, 1, 0}
array := garray.NewIntArray()
for i := 3; i >= 0; i-- {
array.Append(i)
}
array.Sort()
gtest.Assert(array.Slice(), expect1)
array.Sort(true)
gtest.Assert(array.Slice(), expect2)
})
}
func TestIntArray_Unique(t *testing.T) {
gtest.Case(t, func() {
expect := []int{1, 1, 2, 3}
array := garray.NewIntArrayFrom(expect)
gtest.Assert(array.Unique().Slice(), []int{1, 2, 3})
})
}
func TestIntArray_PushAndPop(t *testing.T) {
gtest.Case(t, func() {
expect := []int{0, 1, 2, 3}
array := garray.NewIntArrayFrom(expect)
gtest.Assert(array.Slice(), expect)
gtest.Assert(array.PopLeft(), 0)
gtest.Assert(array.PopRight(), 3)
gtest.AssertIN(array.PopRand(), []int{1, 2})
gtest.AssertIN(array.PopRand(), []int{1, 2})
gtest.Assert(array.Len(), 0)
array.PushLeft(1).PushRight(2)
gtest.Assert(array.Slice(), []int{1, 2})
})
}
func TestIntArray_PopLeftsAndPopRights(t *testing.T) {
gtest.Case(t, func() {
value1 := []int{0,1,2,3,4,5,6}
value2 := []int{0,1,2,3,4,5,6}
array1 := garray.NewIntArrayFrom(value1)
array2 := garray.NewIntArrayFrom(value2)
gtest.Assert(array1.PopLefts(2), []int{0,1})
gtest.Assert(array1.Slice(), []int{2,3,4,5,6})
gtest.Assert(array1.PopRights(2), []int{5,6})
gtest.Assert(array1.Slice(), []int{2,3,4})
gtest.Assert(array1.PopRights(20), []int{2,3,4})
gtest.Assert(array1.Slice(), []int{})
gtest.Assert(array2.PopLefts(20), []int{0,1,2,3,4,5,6})
gtest.Assert(array2.Slice(), []int{})
})
}
func TestIntArray_Range(t *testing.T) {
gtest.Case(t, func() {
value1 := []int{0,1,2,3,4,5,6}
array1 := garray.NewIntArrayFrom(value1)
gtest.Assert(array1.Range(0, 1), []int{0})
gtest.Assert(array1.Range(1, 2), []int{1})
gtest.Assert(array1.Range(0, 2), []int{0, 1})
gtest.Assert(array1.Range(-1, 10), value1)
})
}
func TestIntArray_Merge(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{0, 1, 2, 3}
a2 := []int{4, 5, 6, 7}
array1 := garray.NewIntArrayFrom(a1)
array2 := garray.NewIntArrayFrom(a2)
gtest.Assert(array1.Merge(array2).Slice(), []int{0,1,2,3,4,5,6,7})
})
}
func TestIntArray_Fill(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{0}
a2 := []int{0}
array1 := garray.NewIntArrayFrom(a1)
array2 := garray.NewIntArrayFrom(a2)
gtest.Assert(array1.Fill(1, 2, 100).Slice(), []int{0,100,100})
gtest.Assert(array2.Fill(0, 2, 100).Slice(), []int{100,100})
})
}
func TestIntArray_Chunk(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{1,2,3,4,5}
array1 := garray.NewIntArrayFrom(a1)
chunks := array1.Chunk(2)
gtest.Assert(len(chunks), 3)
gtest.Assert(chunks[0], []int{1,2})
gtest.Assert(chunks[1], []int{3,4})
gtest.Assert(chunks[2], []int{5})
})
}
func TestIntArray_Pad(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{0}
array1 := garray.NewIntArrayFrom(a1)
gtest.Assert(array1.Pad(3, 1).Slice(), []int{0,1,1})
gtest.Assert(array1.Pad(-4, 1).Slice(), []int{1,0,1,1})
gtest.Assert(array1.Pad(3, 1).Slice(), []int{1,0,1,1})
})
}
func TestIntArray_SubSlice(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{0,1,2,3,4,5,6}
array1 := garray.NewIntArrayFrom(a1)
gtest.Assert(array1.SubSlice(0, 2), []int{0,1})
gtest.Assert(array1.SubSlice(2, 2), []int{2,3})
gtest.Assert(array1.SubSlice(5, 8), []int{5,6})
})
}
func TestIntArray_Rand(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{0,1,2,3,4,5,6}
array1 := garray.NewIntArrayFrom(a1)
gtest.Assert(len(array1.Rand(2)), 2)
gtest.Assert(len(array1.Rand(10)), 7)
gtest.AssertIN(array1.Rand(1)[0], a1)
})
}
func TestIntArray_Shuffle(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{0,1,2,3,4,5,6}
array1 := garray.NewIntArrayFrom(a1)
gtest.Assert(array1.Shuffle().Len(), 7)
})
}
func TestIntArray_Reverse(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{0,1,2,3,4,5,6}
array1 := garray.NewIntArrayFrom(a1)
gtest.Assert(array1.Reverse().Slice(), []int{6,5,4,3,2,1,0})
})
}
func TestIntArray_Join(t *testing.T) {
gtest.Case(t, func() {
a1 := []int{0,1,2,3,4,5,6}
array1 := garray.NewIntArrayFrom(a1)
gtest.Assert(array1.Join("."), "0.1.2.3.4.5.6")
})
}

View File

@ -0,0 +1,196 @@
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// go test *.go
package garray_test
import (
"github.com/gogf/gf/g/container/garray"
"github.com/gogf/gf/g/test/gtest"
"testing"
)
func Test_Array_Basic(t *testing.T) {
gtest.Case(t, func() {
expect := []interface{}{0, 1, 2, 3}
array := garray.NewArrayFrom(expect)
gtest.Assert(array.Slice(), expect)
array.Set(0, 100)
gtest.Assert(array.Get(0), 100)
gtest.Assert(array.Get(1), 1)
gtest.Assert(array.Search(100), 0)
gtest.Assert(array.Contains(100), true)
gtest.Assert(array.Remove(0), 100)
gtest.Assert(array.Contains(100), false)
array.Append(4)
gtest.Assert(array.Len(), 4)
array.InsertBefore(0, 100)
array.InsertAfter(0, 200)
gtest.Assert(array.Slice(), []interface{}{100, 200, 1, 2, 3, 4})
array.InsertBefore(5, 300)
array.InsertAfter(6, 400)
gtest.Assert(array.Slice(), []interface{}{100, 200, 1, 2, 3, 300, 4, 400})
gtest.Assert(array.Clear().Len(), 0)
})
}
func TestArray_Sort(t *testing.T) {
gtest.Case(t, func() {
expect1 := []interface{}{0, 1, 2, 3}
expect2 := []interface{}{3, 2, 1, 0}
array := garray.NewArray()
for i := 3; i >= 0; i-- {
array.Append(i)
}
array.SortFunc(func(v1, v2 interface{}) bool {
return v1.(int) < v2.(int)
})
gtest.Assert(array.Slice(), expect1)
array.SortFunc(func(v1, v2 interface{}) bool {
return v1.(int) > v2.(int)
})
gtest.Assert(array.Slice(), expect2)
})
}
func TestArray_Unique(t *testing.T) {
gtest.Case(t, func() {
expect := []interface{}{1, 1, 2, 3}
array := garray.NewArrayFrom(expect)
gtest.Assert(array.Unique().Slice(), []interface{}{1, 2, 3})
})
}
func TestArray_PushAndPop(t *testing.T) {
gtest.Case(t, func() {
expect := []interface{}{0, 1, 2, 3}
array := garray.NewArrayFrom(expect)
gtest.Assert(array.Slice(), expect)
gtest.Assert(array.PopLeft(), 0)
gtest.Assert(array.PopRight(), 3)
gtest.AssertIN(array.PopRand(), []interface{}{1, 2})
gtest.AssertIN(array.PopRand(), []interface{}{1, 2})
gtest.Assert(array.Len(), 0)
array.PushLeft(1).PushRight(2)
gtest.Assert(array.Slice(), []interface{}{1, 2})
})
}
func TestArray_PopLeftsAndPopRights(t *testing.T) {
gtest.Case(t, func() {
value1 := []interface{}{0,1,2,3,4,5,6}
value2 := []interface{}{0,1,2,3,4,5,6}
array1 := garray.NewArrayFrom(value1)
array2 := garray.NewArrayFrom(value2)
gtest.Assert(array1.PopLefts(2), []interface{}{0,1})
gtest.Assert(array1.Slice(), []interface{}{2,3,4,5,6})
gtest.Assert(array1.PopRights(2), []interface{}{5,6})
gtest.Assert(array1.Slice(), []interface{}{2,3,4})
gtest.Assert(array1.PopRights(20), []interface{}{2,3,4})
gtest.Assert(array1.Slice(), []interface{}{})
gtest.Assert(array2.PopLefts(20), []interface{}{0,1,2,3,4,5,6})
gtest.Assert(array2.Slice(), []interface{}{})
})
}
func TestArray_Range(t *testing.T) {
gtest.Case(t, func() {
value1 := []interface{}{0,1,2,3,4,5,6}
array1 := garray.NewArrayFrom(value1)
gtest.Assert(array1.Range(0, 1), []interface{}{0})
gtest.Assert(array1.Range(1, 2), []interface{}{1})
gtest.Assert(array1.Range(0, 2), []interface{}{0, 1})
gtest.Assert(array1.Range(-1, 10), value1)
})
}
func TestArray_Merge(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{0, 1, 2, 3}
a2 := []interface{}{4, 5, 6, 7}
array1 := garray.NewArrayFrom(a1)
array2 := garray.NewArrayFrom(a2)
gtest.Assert(array1.Merge(array2).Slice(), []interface{}{0,1,2,3,4,5,6,7})
})
}
func TestArray_Fill(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{0}
a2 := []interface{}{0}
array1 := garray.NewArrayFrom(a1)
array2 := garray.NewArrayFrom(a2)
gtest.Assert(array1.Fill(1, 2, 100).Slice(), []interface{}{0,100,100})
gtest.Assert(array2.Fill(0, 2, 100).Slice(), []interface{}{100,100})
})
}
func TestArray_Chunk(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{1,2,3,4,5}
array1 := garray.NewArrayFrom(a1)
chunks := array1.Chunk(2)
gtest.Assert(len(chunks), 3)
gtest.Assert(chunks[0], []interface{}{1,2})
gtest.Assert(chunks[1], []interface{}{3,4})
gtest.Assert(chunks[2], []interface{}{5})
})
}
func TestArray_Pad(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{0}
array1 := garray.NewArrayFrom(a1)
gtest.Assert(array1.Pad(3, 1).Slice(), []interface{}{0,1,1})
gtest.Assert(array1.Pad(-4, 1).Slice(), []interface{}{1,0,1,1})
gtest.Assert(array1.Pad(3, 1).Slice(), []interface{}{1,0,1,1})
})
}
func TestArray_SubSlice(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{0,1,2,3,4,5,6}
array1 := garray.NewArrayFrom(a1)
gtest.Assert(array1.SubSlice(0, 2), []interface{}{0,1})
gtest.Assert(array1.SubSlice(2, 2), []interface{}{2,3})
gtest.Assert(array1.SubSlice(5, 8), []interface{}{5,6})
})
}
func TestArray_Rand(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{0,1,2,3,4,5,6}
array1 := garray.NewArrayFrom(a1)
gtest.Assert(len(array1.Rand(2)), 2)
gtest.Assert(len(array1.Rand(10)), 7)
gtest.AssertIN(array1.Rand(1)[0], a1)
})
}
func TestArray_Shuffle(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{0,1,2,3,4,5,6}
array1 := garray.NewArrayFrom(a1)
gtest.Assert(array1.Shuffle().Len(), 7)
})
}
func TestArray_Reverse(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{0,1,2,3,4,5,6}
array1 := garray.NewArrayFrom(a1)
gtest.Assert(array1.Reverse().Slice(), []interface{}{6,5,4,3,2,1,0})
})
}
func TestArray_Join(t *testing.T) {
gtest.Case(t, func() {
a1 := []interface{}{0,1,2,3,4,5,6}
array1 := garray.NewArrayFrom(a1)
gtest.Assert(array1.Join("."), "0.1.2.3.4.5.6")
})
}

View File

@ -0,0 +1,193 @@
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://github.com/gogf/gf.
// go test *.go
package garray_test
import (
"github.com/gogf/gf/g/container/garray"
"github.com/gogf/gf/g/test/gtest"
"github.com/gogf/gf/g/util/gconv"
"testing"
)
func Test_StringArray_Basic(t *testing.T) {
gtest.Case(t, func() {
expect := []string{"0", "1", "2", "3"}
array := garray.NewStringArrayFrom(expect)
gtest.Assert(array.Slice(), expect)
array.Set(0, "100")
gtest.Assert(array.Get(0), 100)
gtest.Assert(array.Get(1), 1)
gtest.Assert(array.Search("100"), 0)
gtest.Assert(array.Contains("100"), true)
gtest.Assert(array.Remove(0), 100)
gtest.Assert(array.Contains("100"), false)
array.Append("4")
gtest.Assert(array.Len(), 4)
array.InsertBefore(0, "100")
array.InsertAfter(0, "200")
gtest.Assert(array.Slice(), []string{"100", "200", "1", "2", "3", "4"})
array.InsertBefore(5, "300")
array.InsertAfter(6, "400")
gtest.Assert(array.Slice(), []string{"100", "200", "1", "2", "3", "300", "4", "400"})
gtest.Assert(array.Clear().Len(), 0)
})
}
func TestStringArray_Sort(t *testing.T) {
gtest.Case(t, func() {
expect1 := []string{"0", "1", "2", "3"}
expect2 := []string{"3", "2", "1", "0"}
array := garray.NewStringArray()
for i := 3; i >= 0; i-- {
array.Append(gconv.String(i))
}
array.Sort()
gtest.Assert(array.Slice(), expect1)
array.Sort(true)
gtest.Assert(array.Slice(), expect2)
})
}
func TestStringArray_Unique(t *testing.T) {
gtest.Case(t, func() {
expect := []string{"1", "1", "2", "3"}
array := garray.NewStringArrayFrom(expect)
gtest.Assert(array.Unique().Slice(), []string{"1", "2", "3"})
})
}
func TestStringArray_PushAndPop(t *testing.T) {
gtest.Case(t, func() {
expect := []string{"0", "1", "2", "3"}
array := garray.NewStringArrayFrom(expect)
gtest.Assert(array.Slice(), expect)
gtest.Assert(array.PopLeft(), "0")
gtest.Assert(array.PopRight(), "3")
gtest.AssertIN(array.PopRand(), []string{"1", "2"})
gtest.AssertIN(array.PopRand(), []string{"1", "2"})
gtest.Assert(array.Len(), 0)
array.PushLeft("1").PushRight("2")
gtest.Assert(array.Slice(), []string{"1", "2"})
})
}
func TestStringArray_PopLeftsAndPopRights(t *testing.T) {
gtest.Case(t, func() {
value1 := []string{"0","1","2","3","4","5","6"}
value2 := []string{"0","1","2","3","4","5","6"}
array1 := garray.NewStringArrayFrom(value1)
array2 := garray.NewStringArrayFrom(value2)
gtest.Assert(array1.PopLefts(2), []interface{}{"0","1"})
gtest.Assert(array1.Slice(), []interface{}{"2","3","4","5","6"})
gtest.Assert(array1.PopRights(2), []interface{}{"5","6"})
gtest.Assert(array1.Slice(), []interface{}{"2","3","4"})
gtest.Assert(array1.PopRights(20), []interface{}{"2","3","4"})
gtest.Assert(array1.Slice(), []interface{}{})
gtest.Assert(array2.PopLefts(20), []interface{}{"0","1","2","3","4","5","6"})
gtest.Assert(array2.Slice(), []interface{}{})
})
}
func TestString_Range(t *testing.T) {
gtest.Case(t, func() {
value1 := []string{"0","1","2","3","4","5","6"}
array1 := garray.NewStringArrayFrom(value1)
gtest.Assert(array1.Range(0, 1), []interface{}{"0"})
gtest.Assert(array1.Range(1, 2), []interface{}{"1"})
gtest.Assert(array1.Range(0, 2), []interface{}{"0", "1"})
gtest.Assert(array1.Range(-1, 10), value1)
})
}
func TestStringArray_Merge(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"0", "1", "2", "3"}
a2 := []string{"4", "5", "6", "7"}
array1 := garray.NewStringArrayFrom(a1)
array2 := garray.NewStringArrayFrom(a2)
gtest.Assert(array1.Merge(array2).Slice(), []string{"0","1","2","3","4","5","6","7"})
})
}
func TestStringArray_Fill(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"0"}
a2 := []string{"0"}
array1 := garray.NewStringArrayFrom(a1)
array2 := garray.NewStringArrayFrom(a2)
gtest.Assert(array1.Fill(1, 2, "100").Slice(), []string{"0","100","100"})
gtest.Assert(array2.Fill(0, 2, "100").Slice(), []string{"100","100"})
})
}
func TestStringArray_Chunk(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"1","2","3","4","5"}
array1 := garray.NewStringArrayFrom(a1)
chunks := array1.Chunk(2)
gtest.Assert(len(chunks), 3)
gtest.Assert(chunks[0], []string{"1","2"})
gtest.Assert(chunks[1], []string{"3","4"})
gtest.Assert(chunks[2], []string{"5"})
})
}
func TestStringArray_Pad(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"0"}
array1 := garray.NewStringArrayFrom(a1)
gtest.Assert(array1.Pad(3, "1").Slice(), []string{"0","1","1"})
gtest.Assert(array1.Pad(-4, "1").Slice(), []string{"1","0","1","1"})
gtest.Assert(array1.Pad(3, "1").Slice(), []string{"1","0","1","1"})
})
}
func TestStringArray_SubSlice(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"0","1","2","3","4","5","6"}
array1 := garray.NewStringArrayFrom(a1)
gtest.Assert(array1.SubSlice(0, 2), []string{"0","1"})
gtest.Assert(array1.SubSlice(2, 2), []string{"2","3"})
gtest.Assert(array1.SubSlice(5, 8), []string{"5","6"})
})
}
func TestStringArray_Rand(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"0","1","2","3","4","5","6"}
array1 := garray.NewStringArrayFrom(a1)
gtest.Assert(len(array1.Rand(2)), "2")
gtest.Assert(len(array1.Rand(10)), "7")
gtest.AssertIN(array1.Rand(1)[0], a1)
})
}
func TestStringArray_Shuffle(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"0","1","2","3","4","5","6"}
array1 := garray.NewStringArrayFrom(a1)
gtest.Assert(array1.Shuffle().Len(), 7)
})
}
func TestStringArray_Reverse(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"0","1","2","3","4","5","6"}
array1 := garray.NewStringArrayFrom(a1)
gtest.Assert(array1.Reverse().Slice(), []string{"6","5","4","3","2","1","0"})
})
}
func TestStringArray_Join(t *testing.T) {
gtest.Case(t, func() {
a1 := []string{"0","1","2","3","4","5","6"}
array1 := garray.NewStringArrayFrom(a1)
gtest.Assert(array1.Join("."), "0.1.2.3.4.5.6")
})
}

View File

@ -1,16 +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 gchan provides graceful operations for channel.
//
// 优雅的Channel操作.
package gchan
import (
"errors"
"gitee.com/johng/gf/g/container/gtype"
"github.com/gogf/gf/g/container/gtype"
)
type Chan struct {

View File

@ -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

View File

@ -1,264 +1,306 @@
// 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 l file,
// 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/container/internal/rwmutex"
"container/list"
"github.com/gogf/gf/g/internal/rwmutex"
)
// 变长双向链表
type List struct {
mu *rwmutex.RWMutex
list *list.List
mu *rwmutex.RWMutex
list *list.List
}
type Element = list.Element
// 获得一个变长链表指针
func New(safe...bool) *List {
func New(unsafe...bool) *List {
return &List {
mu : rwmutex.New(safe...),
list : list.New(),
mu : rwmutex.New(unsafe...),
list : list.New(),
}
}
// 往链表头入栈数据项
func (this *List) PushFront(v interface{}) *list.Element {
this.mu.Lock()
e := this.list.PushFront(v)
this.mu.Unlock()
return e
func (l *List) PushFront(v interface{}) (e *Element) {
l.mu.Lock()
e = l.list.PushFront(v)
l.mu.Unlock()
return
}
// 往链表尾入栈数据项
func (this *List) PushBack(v interface{}) *list.Element {
this.mu.Lock()
r := this.list.PushBack(v)
this.mu.Unlock()
return r
func (l *List) PushBack(v interface{}) (e *Element) {
l.mu.Lock()
e = l.list.PushBack(v)
l.mu.Unlock()
return
}
// 在list 中元素mark之后插入一个值为v的元素并返回该元素如果mark不是list中元素则list不改变。
func (this *List) InsertAfter(v interface{}, mark *list.Element) *list.Element {
this.mu.Lock()
r := this.list.InsertAfter(v, mark)
this.mu.Unlock()
return r
}
// 在list 中元素mark之前插入一个值为v的元素并返回该元素如果mark不是list中元素则list不改变。
func (this *List) InsertBefore(v interface{}, mark *list.Element) *list.Element {
this.mu.Lock()
r := this.list.InsertBefore(v, mark)
this.mu.Unlock()
return r
}
// 批量往链表头入栈数据项
func (this *List) BatchPushFront(vs []interface{}) {
this.mu.Lock()
for _, item := range vs {
this.list.PushFront(item)
func (l *List) BatchPushFront(values []interface{}) {
l.mu.Lock()
for _, v := range values {
l.list.PushFront(v)
}
this.mu.Unlock()
l.mu.Unlock()
}
// 批量往链表尾入栈数据项
func (l *List) BatchPushBack(values []interface{}) {
l.mu.Lock()
for _, v := range values {
l.list.PushBack(v)
}
l.mu.Unlock()
}
// 从链表尾端出栈数据项(删除)
func (this *List) PopBack() interface{} {
this.mu.Lock()
if elem := this.list.Back(); elem != nil {
item := this.list.Remove(elem)
this.mu.Unlock()
return item
func (l *List) PopBack() (value interface{}) {
l.mu.Lock()
if e := l.list.Back(); e != nil {
value = l.list.Remove(e)
}
this.mu.Unlock()
return nil
l.mu.Unlock()
return
}
// 从链表头端出栈数据项(删除)
func (this *List) PopFront() interface{} {
this.mu.Lock()
if elem := this.list.Front(); elem != nil {
item := this.list.Remove(elem)
this.mu.Unlock()
return item
}
this.mu.Unlock()
return nil
func (l *List) PopFront() (value interface{}) {
l.mu.Lock()
if e := l.list.Front(); e != nil {
value = l.list.Remove(e)
}
l.mu.Unlock()
return
}
// 批量从链表尾端出栈数据项(删除)
func (this *List) BatchPopBack(max int) []interface{} {
this.mu.Lock()
count := this.list.Len()
if count == 0 {
this.mu.Unlock()
return []interface{}{}
}
if count > max {
count = max
}
items := make([]interface{}, count)
for i := 0; i < count; i++ {
items[i] = this.list.Remove(this.list.Back())
}
this.mu.Unlock()
return items
func (l *List) BatchPopBack(max int) (values []interface{}) {
l.mu.Lock()
length := l.list.Len()
if length > 0 {
if max > 0 && max < length {
length = max
}
tempe := (*Element)(nil)
values = make([]interface{}, length)
for i := 0; i < length; i++ {
tempe = l.list.Back()
values[i] = l.list.Remove(tempe)
}
}
l.mu.Unlock()
return
}
// 批量从链表头端出栈数据项(删除)
func (this *List) BatchPopFront(max int) []interface{} {
this.mu.Lock()
count := this.list.Len()
if count == 0 {
this.mu.Unlock()
return []interface{}{}
}
if count > max {
count = max
}
items := make([]interface{}, count)
for i := 0; i < count; i++ {
items[i] = this.list.Remove(this.list.Front())
}
this.mu.Unlock()
return items
func (l *List) BatchPopFront(max int) (values []interface{}) {
l.mu.RLock()
length := l.list.Len()
if length > 0 {
if max > 0 && max < length {
length = max
}
tempe := (*Element)(nil)
values = make([]interface{}, length)
for i := 0; i < length; i++ {
tempe = l.list.Front()
values[i] = l.list.Remove(tempe)
}
}
l.mu.RUnlock()
return
}
// 批量从链表尾端依次获取所有数据(删除)
func (this *List) PopBackAll() []interface{} {
this.mu.Lock()
count := this.list.Len()
if count == 0 {
this.mu.Unlock()
return []interface{}{}
}
items := make([]interface{}, count)
for i := 0; i < count; i++ {
items[i] = this.list.Remove(this.list.Back())
}
this.mu.Unlock()
return items
func (l *List) PopBackAll() []interface{} {
return l.BatchPopBack(-1)
}
// 批量从链表头端依次获取所有数据(删除)
func (this *List) PopFrontAll() []interface{} {
this.mu.Lock()
count := this.list.Len()
if count == 0 {
this.mu.Unlock()
return []interface{}{}
}
items := make([]interface{}, count)
for i := 0; i < count; i++ {
items[i] = this.list.Remove(this.list.Front())
}
this.mu.Unlock()
return items
}
// 删除数据项
func (this *List) Remove(e *list.Element) interface{} {
this.mu.Lock()
r := this.list.Remove(e)
this.mu.Unlock()
return r
}
// 删除所有数据项
func (this *List) RemoveAll() {
this.mu.Lock()
this.list = list.New()
this.mu.Unlock()
func (l *List) PopFrontAll() []interface{} {
return l.BatchPopFront(-1)
}
// 从链表头获取所有数据(不删除)
func (this *List) FrontAll() []interface{} {
this.mu.RLock()
count := this.list.Len()
if count == 0 {
this.mu.RUnlock()
return []interface{}{}
}
items := make([]interface{}, 0, count)
for e := this.list.Front(); e != nil; e = e.Next() {
items = append(items, e.Value)
}
this.mu.RUnlock()
return items
func (l *List) FrontAll() (values []interface{}) {
l.mu.RLock()
length := l.list.Len()
if length > 0 {
values = make([]interface{}, length)
for i, e := 0, l.list.Front(); i < length; i, e = i + 1, e.Next() {
values[i] = e.Value
}
}
l.mu.RUnlock()
return
}
// 从链表尾获取所有数据(不删除)
func (this *List) BackAll() []interface{} {
this.mu.RLock()
count := this.list.Len()
if count == 0 {
this.mu.RUnlock()
return []interface{}{}
}
items := make([]interface{}, 0, count)
for e := this.list.Back(); e != nil; e = e.Prev() {
items = append(items, e.Value)
}
this.mu.RUnlock()
return items
func (l *List) BackAll() (values []interface{}) {
l.mu.RLock()
length := l.list.Len()
if length > 0 {
values = make([]interface{}, length)
for i, e := 0, l.list.Back(); i < length; i, e = i + 1, e.Prev() {
values[i] = e.Value
}
}
l.mu.RUnlock()
return
}
// 获取链表头值(不删除)
func (this *List) FrontItem() interface{} {
this.mu.RLock()
if f := this.list.Front(); f != nil {
this.mu.RUnlock()
return f.Value
}
this.mu.RUnlock()
return nil
func (l *List) FrontItem() (value interface{}) {
l.mu.RLock()
if e := l.list.Front(); e != nil {
value = e.Value
}
l.mu.RUnlock()
return
}
// 获取链表尾值(不删除)
func (this *List) BackItem() interface{} {
this.mu.RLock()
if f := this.list.Back(); f != nil {
this.mu.RUnlock()
return f.Value
func (l *List) BackItem() (value interface{}) {
l.mu.RLock()
if e := l.list.Back(); e != nil {
value = e.Value
}
this.mu.RUnlock()
return nil
l.mu.RUnlock()
return
}
// 获取表头指针
func (this *List) Front() *list.Element {
this.mu.RLock()
r := this.list.Front()
this.mu.RUnlock()
return r
func (l *List) Front() (e *Element) {
l.mu.RLock()
e = l.list.Front()
l.mu.RUnlock()
return
}
// 获取表位指针
func (this *List) Back() *list.Element {
this.mu.RLock()
r := this.list.Back()
this.mu.RUnlock()
return r
func (l *List) Back() (e *Element) {
l.mu.RLock()
e = l.list.Back()
l.mu.RUnlock()
return
}
// 获取链表长度
func (this *List) Len() int {
this.mu.RLock()
length := this.list.Len()
this.mu.RUnlock()
return length
func (l *List) Len() (length int) {
l.mu.RLock()
length = l.list.Len()
l.mu.RUnlock()
return
}
func (l *List) MoveBefore(e, p *Element) {
l.mu.Lock()
l.list.MoveBefore(e, p)
l.mu.Unlock()
}
func (l *List) MoveAfter(e, p *Element) {
l.mu.Lock()
l.list.MoveAfter(e, p)
l.mu.Unlock()
}
func (l *List) MoveToFront(e *Element) {
l.mu.Lock()
l.list.MoveToFront(e)
l.mu.Unlock()
}
func (l *List) MoveToBack(e *Element) {
l.mu.Lock()
l.list.MoveToBack(e)
l.mu.Unlock()
}
func (l *List) PushBackList(other *List) {
if l != other {
other.mu.RLock()
defer other.mu.RUnlock()
}
l.mu.Lock()
l.list.PushBackList(other.list)
l.mu.Unlock()
}
func (l *List) PushFrontList(other *List) {
if l != other {
other.mu.RLock()
defer other.mu.RUnlock()
}
l.mu.Lock()
l.list.PushFrontList(other.list)
l.mu.Unlock()
}
// 在list中元素项p之后插入一个值为v的元素并返回该元素如果mark不是list中元素则list不改变。
func (l *List) InsertAfter(v interface{}, p *Element) (e *Element) {
l.mu.Lock()
e = l.list.InsertAfter(v, p)
l.mu.Unlock()
return
}
// 在list中元素项p之前插入一个值为v的元素并返回该元素如果mark不是list中元素则list不改变。
func (l *List) InsertBefore(v interface{}, p *Element) (e *Element) {
l.mu.Lock()
e = l.list.InsertBefore(v, p)
l.mu.Unlock()
return
}
// 删除数据项e, 并返回删除项的元素项
func (l *List) Remove(e *Element) (value interface{}) {
l.mu.Lock()
value = l.list.Remove(e)
l.mu.Unlock()
return
}
// 批量删除数据项
func (l *List) BatchRemove(es []*Element) {
l.mu.Lock()
for _, e := range es {
l.list.Remove(e)
}
l.mu.Unlock()
return
}
// 删除所有数据项
func (l *List) RemoveAll() {
l.mu.Lock()
l.list = list.New()
l.mu.Unlock()
}
// 读锁操作
func (l *List) RLockFunc(f func(list *list.List)) {
l.mu.RLock()
defer l.mu.RUnlock()
f(l.list)
}
// 写锁操作
func (l *List) LockFunc(f func(list *list.List)) {
l.mu.Lock()
defer l.mu.Unlock()
f(l.list)
}

View File

@ -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
@ -12,35 +12,45 @@ import (
"testing"
)
var l = New()
var (
l = New()
bn = 20000000
)
func Benchmark_PushBack(b *testing.B) {
b.N = bn
for i := 0; i < b.N; i++ {
l.PushBack(i)
}
}
func Benchmark_PopFront(b *testing.B) {
for i := 0; i < b.N; i++ {
l.PopFront()
}
}
func Benchmark_PushFront(b *testing.B) {
b.N = bn
for i := 0; i < b.N; i++ {
l.PushFront(i)
}
}
func Benchmark_PopBack(b *testing.B) {
for i := 0; i < b.N; i++ {
l.PopBack()
}
}
func Benchmark_Len(b *testing.B) {
b.N = bn
for i := 0; i < b.N; i++ {
l.Len()
}
}
func Benchmark_PopFront(b *testing.B) {
b.N = bn
for i := 0; i < b.N; i++ {
l.PopFront()
}
}
func Benchmark_PopBack(b *testing.B) {
b.N = bn
for i := 0; i < b.N; i++ {
l.PopBack()
}
}

View File

@ -0,0 +1,367 @@
// 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 glist
import (
"container/list"
"testing"
)
// 检查链表长度
func checkListLen(t *testing.T, l *List, len int) bool {
if n := l.Len(); n != len {
t.Errorf("l.Len() = %d, want %d", n, len)
return false
}
return true
}
// 检查指针地址
func checkListPointers(t *testing.T, l *List, es []*Element) {
if !checkListLen(t, l, len(es)) {
return
}
l.RLockFunc(func(list *list.List) {
for i, e := 0, l.list.Front(); i < list.Len(); i, e = i + 1, e.Next() {
if e.Prev() != es[i].Prev() {
t.Errorf("list[%d].Prev = %p, want %p", i, e.Prev(), es[i].Prev())
}
if e.Next() != es[i].Next() {
t.Errorf("list[%d].Next = %p, want %p", i, e.Next(), es[i].Next())
}
}
})
}
func TestBasic(t *testing.T) {
l := New()
l.PushFront(1)
l.PushFront(2)
if v := l.PopBack(); v != 1 {
t.Errorf("EXPECT %v, GOT %v", 1, v)
} else {
//fmt.Println(v)
}
if v := l.PopBack(); v != 2 {
t.Errorf("EXPECT %v, GOT %v", 2, v)
} else {
//fmt.Println(v)
}
if v := l.PopBack(); v != nil {
t.Errorf("EXPECT %v, GOT %v", nil, v)
} else {
//fmt.Println(v)
}
l.PushBack(1)
l.PushBack(2)
if v := l.PopFront(); v != 1 {
t.Errorf("EXPECT %v, GOT %v", 1, v)
} else {
//fmt.Println(v)
}
if v := l.PopFront(); v != 2 {
t.Errorf("EXPECT %v, GOT %v", 2, v)
} else {
//fmt.Println(v)
}
if v := l.PopFront(); v != nil {
t.Errorf("EXPECT %v, GOT %v", nil, v)
} else {
//fmt.Println(v)
}
}
func TestList(t *testing.T) {
l := New()
checkListPointers(t, l, []*Element{})
// Single element list
e := l.PushFront("a")
checkListPointers(t, l, []*Element{e})
l.MoveToFront(e)
checkListPointers(t, l, []*Element{e})
l.MoveToBack(e)
checkListPointers(t, l, []*Element{e})
l.Remove(e)
checkListPointers(t, l, []*Element{})
// Bigger list
e2 := l.PushFront(2)
e1 := l.PushFront(1)
e3 := l.PushBack(3)
e4 := l.PushBack("banana")
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
l.Remove(e2)
checkListPointers(t, l, []*Element{e1, e3, e4})
l.MoveToFront(e3) // move from middle
checkListPointers(t, l, []*Element{e3, e1, e4})
l.MoveToFront(e1)
l.MoveToBack(e3) // move from middle
checkListPointers(t, l, []*Element{e1, e4, e3})
l.MoveToFront(e3) // move from back
checkListPointers(t, l, []*Element{e3, e1, e4})
l.MoveToFront(e3) // should be no-op
checkListPointers(t, l, []*Element{e3, e1, e4})
l.MoveToBack(e3) // move from front
checkListPointers(t, l, []*Element{e1, e4, e3})
l.MoveToBack(e3) // should be no-op
checkListPointers(t, l, []*Element{e1, e4, e3})
e2 = l.InsertBefore(2, e1) // insert before front
checkListPointers(t, l, []*Element{e2, e1, e4, e3})
l.Remove(e2)
e2 = l.InsertBefore(2, e4) // insert before middle
checkListPointers(t, l, []*Element{e1, e2, e4, e3})
l.Remove(e2)
e2 = l.InsertBefore(2, e3) // insert before back
checkListPointers(t, l, []*Element{e1, e4, e2, e3})
l.Remove(e2)
e2 = l.InsertAfter(2, e1) // insert after front
checkListPointers(t, l, []*Element{e1, e2, e4, e3})
l.Remove(e2)
e2 = l.InsertAfter(2, e4) // insert after middle
checkListPointers(t, l, []*Element{e1, e4, e2, e3})
l.Remove(e2)
e2 = l.InsertAfter(2, e3) // insert after back
checkListPointers(t, l, []*Element{e1, e4, e3, e2})
l.Remove(e2)
// Check standard iteration.
sum := 0
for e := l.Front(); e != nil; e = e.Next() {
if i, ok := e.Value.(int); ok {
sum += i
}
}
if sum != 4 {
t.Errorf("sum over l = %d, want 4", sum)
}
// Clear all elements by iterating
var next *Element
for e := l.Front(); e != nil; e = next {
next = e.Next()
l.Remove(e)
}
checkListPointers(t, l, []*Element{})
}
func checkList(t *testing.T, l *List, es []interface{}) {
if !checkListLen(t, l, len(es)) {
return
}
i := 0
for e := l.Front(); e != nil; e = e.Next() {
le := e.Value.(int)
if le != es[i] {
t.Errorf("elt[%d].Value() = %v, want %v", i, le, es[i])
}
i++
}
}
func TestExtending(t *testing.T) {
l1 := New()
l2 := New()
l1.PushBack(1)
l1.PushBack(2)
l1.PushBack(3)
l2.PushBack(4)
l2.PushBack(5)
l3 := New()
l3.PushBackList(l1)
checkList(t, l3, []interface{}{1, 2, 3})
l3.PushBackList(l2)
checkList(t, l3, []interface{}{1, 2, 3, 4, 5})
l3 = New()
l3.PushFrontList(l2)
checkList(t, l3, []interface{}{4, 5})
l3.PushFrontList(l1)
checkList(t, l3, []interface{}{1, 2, 3, 4, 5})
checkList(t, l1, []interface{}{1, 2, 3})
checkList(t, l2, []interface{}{4, 5})
l3 = New()
l3.PushBackList(l1)
checkList(t, l3, []interface{}{1, 2, 3})
l3.PushBackList(l3)
checkList(t, l3, []interface{}{1, 2, 3, 1, 2, 3})
l3 = New()
l3.PushFrontList(l1)
checkList(t, l3, []interface{}{1, 2, 3})
l3.PushFrontList(l3)
checkList(t, l3, []interface{}{1, 2, 3, 1, 2, 3})
l3 = New()
l1.PushBackList(l3)
checkList(t, l1, []interface{}{1, 2, 3})
l1.PushFrontList(l3)
checkList(t, l1, []interface{}{1, 2, 3})
}
func TestRemove(t *testing.T) {
l := New()
e1 := l.PushBack(1)
e2 := l.PushBack(2)
checkListPointers(t, l, []*Element{e1, e2})
//e := l.Front()
//l.Remove(e)
//checkListPointers(t, l, []*Element{e2})
//l.Remove(e)
//checkListPointers(t, l, []*Element{e2})
}
func TestIssue4103(t *testing.T) {
l1 := New()
l1.PushBack(1)
l1.PushBack(2)
l2 := New()
l2.PushBack(3)
l2.PushBack(4)
e := l1.Front()
l2.Remove(e) // l2 should not change because e is not an element of l2
if n := l2.Len(); n != 2 {
t.Errorf("l2.Len() = %d, want 2", n)
}
l1.InsertBefore(8, e)
if n := l1.Len(); n != 3 {
t.Errorf("l1.Len() = %d, want 3", n)
}
}
func TestIssue6349(t *testing.T) {
l := New()
l.PushBack(1)
l.PushBack(2)
e := l.Front()
l.Remove(e)
if e.Value != 1 {
t.Errorf("e.value = %d, want 1", e.Value)
}
//if e.Next() != nil {
// t.Errorf("e.Next() != nil")
//}
//if e.Prev() != nil {
// t.Errorf("e.Prev() != nil")
//}
}
func TestMove(t *testing.T) {
l := New()
e1 := l.PushBack(1)
e2 := l.PushBack(2)
e3 := l.PushBack(3)
e4 := l.PushBack(4)
l.MoveAfter(e3, e3)
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
l.MoveBefore(e2, e2)
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
l.MoveAfter(e3, e2)
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
l.MoveBefore(e2, e3)
checkListPointers(t, l, []*Element{e1, e2, e3, e4})
l.MoveBefore(e2, e4)
checkListPointers(t, l, []*Element{e1, e3, e2, e4})
e2, e3 = e3, e2
l.MoveBefore(e4, e1)
checkListPointers(t, l, []*Element{e4, e1, e2, e3})
e1, e2, e3, e4 = e4, e1, e2, e3
l.MoveAfter(e4, e1)
checkListPointers(t, l, []*Element{e1, e4, e2, e3})
e2, e3, e4 = e4, e2, e3
l.MoveAfter(e2, e3)
checkListPointers(t, l, []*Element{e1, e3, e2, e4})
e2, e3 = e3, e2
}
// Test PushFront, PushBack, PushFrontList, PushBackList with uninitialized List
func TestZeroList(t *testing.T) {
var l1 = New()
l1.PushFront(1)
checkList(t, l1, []interface{}{1})
var l2 = New()
l2.PushBack(1)
checkList(t, l2, []interface{}{1})
var l3 = New()
l3.PushFrontList(l1)
checkList(t, l3, []interface{}{1})
var l4 = New()
l4.PushBackList(l2)
checkList(t, l4, []interface{}{1})
}
// Test that a list l is not modified when calling InsertBefore with a mark that is not an element of l.
func TestInsertBeforeUnknownMark(t *testing.T) {
l := New()
l.PushBack(1)
l.PushBack(2)
l.PushBack(3)
l.InsertBefore(1, new(Element))
checkList(t, l, []interface{}{1, 2, 3})
}
// Test that a list l is not modified when calling InsertAfter with a mark that is not an element of l.
func TestInsertAfterUnknownMark(t *testing.T) {
l := New()
l.PushBack(1)
l.PushBack(2)
l.PushBack(3)
l.InsertAfter(1, new(Element))
checkList(t, l, []interface{}{1, 2, 3})
}
// Test that a list l is not modified when calling MoveAfter or MoveBefore with a mark that is not an element of l.
func TestMoveUnknownMark(t *testing.T) {
l1 := New()
e1 := l1.PushBack(1)
l2 := New()
e2 := l2.PushBack(2)
l1.MoveAfter(e1, e2)
checkList(t, l1, []interface{}{1})
checkList(t, l2, []interface{}{2})
l1.MoveBefore(e1, e2)
checkList(t, l1, []interface{}{1})
checkList(t, l2, []interface{}{2})
}
func TestList_RemoveAll(t *testing.T) {
l := New()
l.PushBack(1)
l.RemoveAll()
checkList(t, l, []interface{}{})
l.PushBack(2)
checkList(t, l, []interface{}{2})
}

View File

@ -1,20 +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
}
}

View 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

View File

@ -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/container/internal/rwmutex"
"github.com/gogf/gf/g/internal/rwmutex"
)
type IntBoolMap struct {
@ -16,29 +16,57 @@ type IntBoolMap struct {
mu *rwmutex.RWMutex
}
func NewIntBoolMap(safe...bool) *IntBoolMap {
func NewIntBoolMap(unsafe...bool) *IntBoolMap {
return &IntBoolMap{
m : make(map[int]bool),
mu : rwmutex.New(safe...),
mu : rwmutex.New(unsafe...),
}
}
// 哈希表克隆
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
}
}

View File

@ -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/container/internal/rwmutex"
"github.com/gogf/gf/g/internal/rwmutex"
)
type IntIntMap struct {
@ -16,110 +16,138 @@ type IntIntMap struct {
m map[int]int
}
func NewIntIntMap(safe...bool) *IntIntMap {
func NewIntIntMap(unsafe...bool) *IntIntMap {
return &IntIntMap{
m : make(map[int]int),
mu : rwmutex.New(safe...),
mu : rwmutex.New(unsafe...),
}
}
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
}
}

View File

@ -1,206 +1,261 @@
// 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/container/internal/rwmutex"
import (
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/util/gconv"
)
type IntInterfaceMap struct {
mu *rwmutex.RWMutex
m map[int]interface{}
}
func NewIntInterfaceMap(safe...bool) *IntInterfaceMap {
func NewIntInterfaceMap(unsafe...bool) *IntInterfaceMap {
return &IntInterfaceMap{
m : make(map[int]interface{}),
mu : rwmutex.New(safe...),
mu : rwmutex.New(unsafe...),
}
}
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
}
}

View File

@ -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/container/internal/rwmutex"
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/util/gconv"
)
type IntStringMap struct {
@ -16,110 +17,138 @@ type IntStringMap struct {
m map[int]string
}
func NewIntStringMap(safe...bool) *IntStringMap {
func NewIntStringMap(unsafe...bool) *IntStringMap {
return &IntStringMap{
m : make(map[int]string),
mu : rwmutex.New(safe...),
mu : rwmutex.New(unsafe...),
}
}
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
}
}

View File

@ -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/container/internal/rwmutex"
)
type InterfaceInterfaceMap struct {
mu *rwmutex.RWMutex
m map[interface{}]interface{}
}
func NewInterfaceInterfaceMap(safe...bool) *InterfaceInterfaceMap {
return &InterfaceInterfaceMap{
m : make(map[interface{}]interface{}),
mu : rwmutex.New(safe...),
}
}
// 给定回调函数对原始内容进行遍历回调函数返回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)
}

View File

@ -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/container/internal/rwmutex"
"github.com/gogf/gf/g/internal/rwmutex"
)
type StringBoolMap struct {
@ -16,110 +16,138 @@ type StringBoolMap struct {
m map[string]bool
}
func NewStringBoolMap(safe...bool) *StringBoolMap {
func NewStringBoolMap(unsafe...bool) *StringBoolMap {
return &StringBoolMap{
m : make(map[string]bool),
mu : rwmutex.New(safe...),
mu : rwmutex.New(unsafe...),
}
}
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
}
}

View File

@ -1,123 +1,154 @@
// 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/container/internal/rwmutex"
import (
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/util/gconv"
)
type StringIntMap struct {
mu *rwmutex.RWMutex
m map[string]int
}
func NewStringIntMap(safe...bool) *StringIntMap {
func NewStringIntMap(unsafe...bool) *StringIntMap {
return &StringIntMap{
m : make(map[string]int),
mu : rwmutex.New(safe...),
mu : rwmutex.New(unsafe...),
}
}
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
}
}

View File

@ -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/container/internal/rwmutex"
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/util/gconv"
)
type StringInterfaceMap struct {
@ -16,193 +17,245 @@ type StringInterfaceMap struct {
m map[string]interface{}
}
func NewStringInterfaceMap(safe...bool) *StringInterfaceMap {
func NewStringInterfaceMap(unsafe...bool) *StringInterfaceMap {
return &StringInterfaceMap{
m : make(map[string]interface{}),
mu : rwmutex.New(safe...),
mu : rwmutex.New(unsafe...),
}
}
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
}
}

View File

@ -1,123 +1,153 @@
// 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/container/internal/rwmutex"
import (
"github.com/gogf/gf/g/internal/rwmutex"
)
type StringStringMap struct {
mu *rwmutex.RWMutex
m map[string]string
}
func NewStringStringMap(safe...bool) *StringStringMap {
func NewStringStringMap(unsafe...bool) *StringStringMap {
return &StringStringMap{
m : make(map[string]string),
mu : rwmutex.New(safe...),
mu : rwmutex.New(unsafe...),
}
}
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
}
}

View File

@ -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()

View File

@ -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"
)

View File

@ -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)
// 写入性能测试

View File

@ -1,19 +1,21 @@
// 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.
//
// 对象复用池.
package gpool
import (
"time"
"errors"
"gitee.com/johng/gf/g/os/gtime"
"gitee.com/johng/gf/g/container/glist"
"gitee.com/johng/gf/g/container/gtype"
"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"
)
// 对象池
@ -32,27 +34,29 @@ type poolItem struct {
value interface{} // 对象值
}
// 对象创建方法类型
type NewFunc func() (interface{}, error)
// 对象过期方法类型
type ExpireFunc func(interface{})
// 创建一个对象池,为保证执行效率,过期时间一旦设定之后无法修改
// expire = 0表示不过期expire < 0表示使用完立即回收expire > 0表示超时回收
// 注意过期时间单位为**毫秒**
func New(expire int, newFunc...func() (interface{}, error)) *Pool {
func New(expire int, newFunc NewFunc, expireFunc...ExpireFunc) *Pool {
r := &Pool {
list : glist.New(),
closed : gtype.NewBool(),
Expire : int64(expire),
NewFunc : newFunc,
}
if len(newFunc) > 0 {
r.NewFunc = newFunc[0]
if len(expireFunc) > 0 {
r.ExpireFunc = expireFunc[0]
}
go r.expireCheckingLoop()
gtimer.AddSingleton(time.Second, r.checkExpire)
return r
}
// 设置对象过期销毁时的关闭方法
func (p *Pool) SetExpireFunc(expireFunc func(interface{})) {
p.ExpireFunc = expireFunc
}
// 放一个临时对象到池中
func (p *Pool) Put(value interface{}) {
item := &poolItem {
@ -100,22 +104,22 @@ func (p *Pool) Close() {
}
// 超时检测循环
func (p *Pool) expireCheckingLoop() {
for !p.closed.Val() {
for {
if r := p.list.PopFront(); r != nil {
item := r.(*poolItem)
if item.expire == 0 || item.expire > gtime.Millisecond() {
p.list.PushFront(item)
break
}
if p.ExpireFunc != nil {
p.ExpireFunc(item.value)
}
} else {
func (p *Pool) checkExpire() {
if p.closed.Val() {
gtimer.Exit()
}
for {
if r := p.list.PopFront(); r != nil {
item := r.(*poolItem)
if item.expire == 0 || item.expire > gtime.Millisecond() {
p.list.PushFront(item)
break
}
if p.ExpireFunc != nil {
p.ExpireFunc(item.value)
}
} else {
break
}
time.Sleep(time.Second)
}
}

View File

@ -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=".*"
@ -13,7 +13,7 @@ import (
"sync"
)
var pool = New(99999999)
var pool = New(99999999, nil)
var syncp = sync.Pool{}
func BenchmarkGPoolPut(b *testing.B) {

View File

@ -1,51 +1,58 @@
// 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、动态队列初始化速度快
// 2、动态的队列大小(不限大小)
// 3、取数据时如果队列为空那么会阻塞等待
//
// 并发安全动态队列.
//
// 特点:
// 1. 动态队列初始化速度快
// 2. 动态的队列大小(不限大小)
// 3. 取数据时如果队列为空那么会阻塞等待;
package gqueue
import (
"gitee.com/johng/gf/g/container/glist"
"container/list"
"math"
"sync"
)
// 0、这是一个先进先出的队列(chan <-- list)
// 1、当创建Queue对象时限定大小那么等同于一个同步的chan并发安全队列
// 2、不限制大小时list链表用以存储数据临时chan负责为客户端读取数据当从chan获取数据时list往chan中不停补充数据
// 3、由于功能主体是chan那么操作仍然像chan那样具有阻塞效果
// 1、这是一个先进先出的队列(chan <-- list)
//
// 2、当创建Queue对象时限定大小那么等同于一个同步的chan并发安全队列
//
// 3、不限制大小时list链表用以存储数据临时chan负责为客户端读取数据当从chan获取数据时list往chan中不停补充数据
//
// 4、由于功能主体是chan那么操作仍然像chan那样具有阻塞效果
type Queue struct {
mu sync.Mutex // 底层链表写锁
limit int // 队列限制大小
queue chan interface{} // 用于队列写入限制
list *glist.List // 数据链表
events chan struct{} // 通知chan当不限制队列大小时的写入事件通知
closeChan chan struct{} // 关闭channel
list *list.List // 底层数据链表
events chan struct{} // 写入事件通知
closed chan struct{} // 队列关闭通知
C chan interface{} // 队列数据读取
}
const (
// 动态队列缓冲区大小
gQUEUE_SIZE = 10000
gDEFAULT_QUEUE_SIZE = 10000
)
// 队列大小为非必须参数,默认不限制
func New(limit...int) *Queue {
q := &Queue {
closeChan : make(chan struct{}, 0),
closed : make(chan struct{}, 0),
}
if len(limit) > 0 {
q.limit = limit[0]
q.queue = make(chan interface{}, limit[0])
q.C = make(chan interface{}, limit[0])
} else {
q.list = glist.New()
q.queue = make(chan interface{}, gQUEUE_SIZE)
q.list = list.New()
q.events = make(chan struct{}, math.MaxInt32)
q.C = make(chan interface{}, gDEFAULT_QUEUE_SIZE)
go q.startAsyncLoop()
}
return q
@ -55,13 +62,24 @@ func New(limit...int) *Queue {
func (q *Queue) startAsyncLoop() {
for {
select {
case <- q.closeChan:
case <- q.closed:
return
case <- q.events:
// 循环读取链表,直到为空才跳出
for {
if v := q.list.PopFront(); v != nil {
q.queue <- v
if length := q.list.Len(); length > 0 {
array := make([]interface{}, length)
q.mu.Lock()
for i := 0; i < length; i++ {
if e := q.list.Front(); e != nil {
array[i] = q.list.Remove(e)
} else {
break
}
}
q.mu.Unlock()
for _, v := range array {
q.C <- v
}
} else {
break
}
@ -70,34 +88,33 @@ func (q *Queue) startAsyncLoop() {
}
}
// 将数据压入队列, 队
// 将数据压入队列, 队
func (q *Queue) Push(v interface{}) {
if q.limit > 0 {
q.queue <- v
q.C <- v
} else {
q.mu.Lock()
q.list.PushBack(v)
if len(q.events) == 0 {
q.events <- struct{}{}
}
q.mu.Unlock()
q.events <- struct{}{}
}
}
// 从队头先进先出地从队列取出一项数据
func (q *Queue) Pop() interface{} {
return <- q.queue
return <- q.C
}
// 关闭队列(通知所有通过Pop*阻塞的协程退出)
func (q *Queue) Close() {
q.list.RemoveAll()
close(q.queue)
close(q.C)
close(q.events)
close(q.closeChan)
close(q.closed)
}
// 获取当前队列大小
func (q *Queue) Size() int {
return len(q.queue) + q.list.Len()
return len(q.C) + q.list.Len()
}

View File

@ -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

View File

@ -1,17 +1,18 @@
// 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).
// 并发安全的环.
//
// 并发安全环.
package gring
import (
"container/ring"
"gitee.com/johng/gf/g/container/gtype"
"gitee.com/johng/gf/g/container/internal/rwmutex"
"github.com/gogf/gf/g/container/gtype"
"github.com/gogf/gf/g/internal/rwmutex"
)
type Ring struct {
@ -22,9 +23,9 @@ type Ring struct {
dirty *gtype.Bool // 标记环是否脏了(需要重新计算大小,当环大小发生改变时做标记)
}
func New(cap int, safe...bool) *Ring {
func New(cap int, unsafe...bool) *Ring {
return &Ring {
mu : rwmutex.New(safe...),
mu : rwmutex.New(unsafe...),
ring : ring.New(cap),
len : gtype.NewInt(),
cap : gtype.NewInt(cap),

View File

@ -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=".*"

View File

@ -1,16 +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.
// 并发安全的集合SET.
//
// 并发安全集合.
package gset
type Set = InterfaceSet
import (
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/util/gconv"
"strings"
)
// 默认Set类型
func New(safe...bool) *Set {
return NewInterfaceSet(safe...)
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 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
}

View File

@ -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/container/internal/rwmutex"
"github.com/gogf/gf/g/internal/rwmutex"
"github.com/gogf/gf/g/util/gconv"
"strings"
)
type IntSet struct {
@ -18,99 +18,267 @@ type IntSet struct {
m map[int]struct{}
}
func NewIntSet(safe...bool) *IntSet {
// 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{}),
mu : rwmutex.New(safe...),
mu : rwmutex.New(unsafe...),
}
}
// 给定回调函数对原始内容进行遍历回调函数返回true表示继续遍历否则停止遍历
func (this *IntSet) Iterator(f func (v int) bool) {
this.mu.RLock()
defer this.mu.RUnlock()
for k, _ := range this.m {
// 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 {
if !f(k) {
break
}
}
return set
}
// 设置键
func (this *IntSet) Add(item int) *IntSet {
this.mu.Lock()
this.m[item] = struct{}{}
this.mu.Unlock()
return this
// Add one or multiple items to the set.
//
// 添加元素项到集合中(支持多个).
func (set *IntSet) Add(item...int) *IntSet {
set.mu.Lock()
for _, v := range item {
set.m[v] = struct{}{}
}
set.mu.Unlock()
return set
}
// 批量添加设置键
func (this *IntSet) BatchAdd(items []int) *IntSet {
this.mu.Lock()
for _, item := range items {
this.m[item] = struct{}{}
}
this.mu.Unlock()
return this
}
// 键是否存在
func (this *IntSet) Contains(item int) bool {
this.mu.RLock()
_, exists := this.m[item]
this.mu.RUnlock()
// Check whether the set contains <item>.
//
// 键是否存在.
func (set *IntSet) Contains(item int) bool {
set.mu.RLock()
_, exists := set.m[item]
set.mu.RUnlock()
return exists
}
// 删除键值对
func (this *IntSet) Remove(key int) {
this.mu.Lock()
delete(this.m, key)
this.mu.Unlock()
// Remove <item> from set.
//
// 删除元素项。
func (set *IntSet) Remove(item int) *IntSet {
set.mu.Lock()
delete(set.m, item)
set.mu.Unlock()
return set
}
// 大小
func (this *IntSet) Size() int {
this.mu.RLock()
l := len(this.m)
this.mu.RUnlock()
// Get size of the set.
//
// 获得集合大小。
func (set *IntSet) Size() int {
set.mu.RLock()
l := len(set.m)
set.mu.RUnlock()
return l
}
// 清空set
func (this *IntSet) Clear() {
this.mu.Lock()
this.m = make(map[int]struct{})
this.mu.Unlock()
// Clear the set.
//
// 清空集合。
func (set *IntSet) Clear() *IntSet {
set.mu.Lock()
set.m = make(map[int]struct{})
set.mu.Unlock()
return set
}
// 转换为数组
func (this *IntSet) Slice() []int {
this.mu.RLock()
ret := make([]int, len(this.m))
// 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 this.m {
ret[i] = item
for k, _ := range set.m {
ret[i] = k
i++
}
this.mu.RUnlock()
set.mu.RUnlock()
return ret
}
// 转换为字符串
func (this *IntSet) String() string {
return fmt.Sprint(this.Slice())
// Join set items with a string.
//
// 使用glue字符串串连当前集合的元素项构造成新的字符串返回。
func (set *IntSet) Join(glue string) string {
return strings.Join(gconv.Strings(set.Slice()), ",")
}
func (this *IntSet) LockFunc(f func(m map[int]struct{})) {
this.mu.Lock(true)
defer this.mu.Unlock(true)
f(this.m)
// Return set items as a string, which are joined by char ','.
//
// 使用glue字符串串连当前集合的元素项构造成新的字符串返回。
func (set *IntSet) String() string {
return set.Join(",")
}
func (this *IntSet) RLockFunc(f func(m map[int]struct{})) {
this.mu.RLock(true)
defer this.mu.RUnlock(true)
f(this.m)
}
// 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
}
// 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
}

View File

@ -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/container/internal/rwmutex"
)
type InterfaceSet struct {
mu *rwmutex.RWMutex
m map[interface{}]struct{}
}
func NewInterfaceSet(safe...bool) *InterfaceSet {
return &InterfaceSet{
m : make(map[interface{}]struct{}),
mu : rwmutex.New(safe...),
}
}
// 给定回调函数对原始内容进行遍历回调函数返回true表示继续遍历否则停止遍历
func (this *InterfaceSet) Iterator(f func (v interface{}) bool) {
this.mu.RLock()
defer this.mu.RUnlock()
for k, _ := range this.m {
if !f(k) {
break
}
}
}
// 添加
func (this *InterfaceSet) Add(item interface{}) *InterfaceSet {
this.mu.Lock()
this.m[item] = struct{}{}
this.mu.Unlock()
return this
}
// 批量添加
func (this *InterfaceSet) BatchAdd(items []interface{}) *InterfaceSet {
this.mu.Lock()
for _, item := range items {
this.m[item] = struct{}{}
}
this.mu.Unlock()
return this
}
// 键是否存在
func (this *InterfaceSet) Contains(item interface{}) bool {
this.mu.RLock()
_, exists := this.m[item]
this.mu.RUnlock()
return exists
}
// 删除键值对
func (this *InterfaceSet) Remove(key interface{}) {
this.mu.Lock()
delete(this.m, key)
this.mu.Unlock()
}
// 大小
func (this *InterfaceSet) Size() int {
this.mu.RLock()
l := len(this.m)
this.mu.RUnlock()
return l
}
// 清空set
func (this *InterfaceSet) Clear() {
this.mu.Lock()
this.m = make(map[interface{}]struct{})
this.mu.Unlock()
}
// 转换为数组
func (this *InterfaceSet) Slice() []interface{} {
this.mu.RLock()
i := 0
ret := make([]interface{}, len(this.m))
for item := range this.m {
ret[i] = item
i++
}
this.mu.RUnlock()
return ret
}
// 转换为字符串
func (this *InterfaceSet) String() string {
return fmt.Sprint(this.Slice())
}
func (this *InterfaceSet) LockFunc(f func(m map[interface{}]struct{})) {
this.mu.Lock(true)
defer this.mu.Unlock(true)
f(this.m)
}
func (this *InterfaceSet) RLockFunc(f func(m map[interface{}]struct{})) {
this.mu.RLock(true)
defer this.mu.RUnlock(true)
f(this.m)
}

View File

@ -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/container/internal/rwmutex"
"github.com/gogf/gf/g/internal/rwmutex"
"strings"
)
type StringSet struct {
@ -17,99 +17,268 @@ type StringSet struct {
m map[string]struct{}
}
func NewStringSet(safe...bool) *StringSet {
return &StringSet{
// 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{}),
mu : rwmutex.New(safe...),
mu : rwmutex.New(unsafe...),
}
}
// 给定回调函数对原始内容进行遍历回调函数返回true表示继续遍历否则停止遍历
func (this *StringSet) Iterator(f func (v string) bool) {
this.mu.RLock()
defer this.mu.RUnlock()
for k, _ := range this.m {
// 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 {
if !f(k) {
break
}
}
return set
}
// 设置键
func (this *StringSet) Add(item string) *StringSet {
this.mu.Lock()
this.m[item] = struct{}{}
this.mu.Unlock()
return this
// Add one or multiple items to the set.
//
// 添加元素项到集合中(支持多个).
func (set *StringSet) Add(item...string) *StringSet {
set.mu.Lock()
for _, v := range item {
set.m[v] = struct{}{}
}
set.mu.Unlock()
return set
}
// 批量添加设置键
func (this *StringSet) BatchAdd(items []string) *StringSet {
this.mu.Lock()
for _, item := range items {
this.m[item] = struct{}{}
}
this.mu.Unlock()
return this
}
// 键是否存在
func (this *StringSet) Contains(item string) bool {
this.mu.RLock()
_, exists := this.m[item]
this.mu.RUnlock()
// Check whether the set contains <item>.
//
// 键是否存在.
func (set *StringSet) Contains(item string) bool {
set.mu.RLock()
_, exists := set.m[item]
set.mu.RUnlock()
return exists
}
// 删除键值对
func (this *StringSet) Remove(key string) {
this.mu.Lock()
delete(this.m, key)
this.mu.Unlock()
// Remove <item> from set.
//
// 删除元素项。
func (set *StringSet) Remove(item string) *StringSet {
set.mu.Lock()
delete(set.m, item)
set.mu.Unlock()
return set
}
// 大小
func (this *StringSet) Size() int {
this.mu.RLock()
l := len(this.m)
this.mu.RUnlock()
// Get size of the set.
//
// 获得集合大小。
func (set *StringSet) Size() int {
set.mu.RLock()
l := len(set.m)
set.mu.RUnlock()
return l
}
// 清空set
func (this *StringSet) Clear() {
this.mu.Lock()
this.m = make(map[string]struct{})
this.mu.Unlock()
// Clear the set.
//
// 清空集合。
func (set *StringSet) Clear() *StringSet {
set.mu.Lock()
set.m = make(map[string]struct{})
set.mu.Unlock()
return set
}
// 转换为数组
func (this *StringSet) Slice() []string {
this.mu.RLock()
ret := make([]string, len(this.m))
// Get the copy of items from set as slice.
//
// 获得集合元素项列表.
func (set *StringSet) Slice() []string {
set.mu.RLock()
ret := make([]string, len(set.m))
i := 0
for item := range this.m {
for item := range set.m {
ret[i] = item
i++
}
this.mu.RUnlock()
set.mu.RUnlock()
return ret
}
// 转换为字符串
func (this *StringSet) String() string {
return fmt.Sprint(this.Slice())
// Join set items with a string.
//
// 使用glue字符串串连当前集合的元素项构造成新的字符串返回。
func (set *StringSet) Join(glue string) string {
return strings.Join(set.Slice(), ",")
}
func (this *StringSet) LockFunc(f func(m map[string]struct{})) {
this.mu.Lock(true)
defer this.mu.Unlock(true)
f(this.m)
// Return set items as a string, which are joined by char ','.
//
// 使用glue字符串串连当前集合的元素项构造成新的字符串返回。
func (set *StringSet) String() string {
return set.Join(",")
}
func (this *StringSet) RLockFunc(f func(m map[string]struct{})) {
this.mu.RLock(true)
defer this.mu.RUnlock(true)
f(this.m)
// 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
}
// 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
}

View File

@ -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))
}
}

View File

@ -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(false)
var itfsUnsafe = gset.NewInterfaceSet(false)
var strsUnsafe = gset.NewStringSet(false)
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))
}
}

View 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))
}
}

View 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)
})
}

View 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)
})
}

View 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)
})
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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 {

View File

@ -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 {

View File

@ -1,11 +1,12 @@
// 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 concurrent-safe basic-types.
// 并发安全的基本类型.
// Package gtype provides kinds of high performance, concurrent-safe basic variable types.
//
// 并发安全基本类型.
package gtype
type Type = Interface

View File

@ -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"
)

View File

@ -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
@ -34,7 +34,7 @@ func (t *Int) Val() int {
return int(atomic.LoadInt64(&t.val))
}
// 数值增加delta并返回的数值
// 数值增加delta并返回**新**的数值
func (t *Int) Add(delta int) int {
return int(atomic.AddInt64(&t.val, int64(delta)))
}

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -1,17 +1,18 @@
// 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 a universal variable type.
// Package gvar provides an universal variable type, like generics.
//
// 通用动态变量.
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"
)
@ -21,10 +22,10 @@ type Var struct {
}
// 创建一个动态变量value参数可以为nil
func New(value interface{}, safe...bool) *Var {
func New(value interface{}, unsafe...bool) *Var {
v := &Var{}
if len(safe) > 0 && safe[0] {
v.safe = safe[0]
if len(unsafe) == 0 || !unsafe[0] {
v.safe = true
v.value = gtype.NewInterface(value)
} else {
v.value = value
@ -33,8 +34,8 @@ func New(value interface{}, safe...bool) *Var {
}
// 创建一个只读动态变量value参数可以为nil
func NewRead(value interface{}, safe...bool) VarRead {
return VarRead(New(value, safe...))
func NewRead(value interface{}, unsafe...bool) VarRead {
return VarRead(New(value, unsafe...))
}
// 返回动态变量的只读接口
@ -90,10 +91,16 @@ func (v *Var) Floats() []float64 { return gconv.Floats(v.Val()) }
func (v *Var) Strings() []string { return gconv.Strings(v.Val()) }
func (v *Var) Interfaces() []interface{} { return gconv.Interfaces(v.Val()) }
func (v *Var) Time(format...string) time.Time { return gconv.Time(v.Val(), format...) }
func (v *Var) TimeDuration() time.Duration { return gconv.TimeDuration(v.Val()) }
func (v *Var) Time(format...string) time.Time {
return gconv.Time(v.Val(), format...)
}
func (v *Var) TimeDuration() time.Duration {
return gconv.TimeDuration(v.Val())
}
func (v *Var) GTime(format...string) *gtime.Time { return gconv.GTime(v.Val(), format...) }
func (v *Var) GTime(format...string) *gtime.Time {
return gconv.GTime(v.Val(), format...)
}
// 将变量转换为对象,注意 objPointer 参数必须为struct指针
func (v *Var) Struct(objPointer interface{}, attrMapping...map[string]string) error {

View File

@ -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"
)

View File

@ -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

View File

@ -1,48 +0,0 @@
package rwmutex
import "sync"
// RWMutex的封装支持对并发安全开启/关闭的控制。
// 但是只能初始化时确定并发安全性,不能在运行时动态修改并发安全特性设置。
type RWMutex struct {
sync.RWMutex
safe bool
}
func New(safe...bool) *RWMutex {
mu := new(RWMutex)
if len(safe) > 0 {
mu.safe = safe[0]
} else {
mu.safe = true
}
return mu
}
func (mu *RWMutex) IsSafe() bool {
return mu.safe
}
func (mu *RWMutex) Lock(force...bool) {
if mu.safe || (len(force) > 0 && force[0]) {
mu.RWMutex.Lock()
}
}
func (mu *RWMutex) Unlock(force...bool) {
if mu.safe || (len(force) > 0 && force[0]) {
mu.RWMutex.Unlock()
}
}
func (mu *RWMutex) RLock(force...bool) {
if mu.safe || (len(force) > 0 && force[0]) {
mu.RWMutex.RLock()
}
}
func (mu *RWMutex) RUnlock(force...bool) {
if mu.safe || (len(force) > 0 && force[0]) {
mu.RWMutex.RUnlock()
}
}

7
g/crypto/crypto.go Normal file
View File

@ -0,0 +1,7 @@
// 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 crypto

View File

@ -1,10 +1,10 @@
// 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.
// AES
// Package gaes provides useful API for AES encryption/decryption algorithms.
package gaes
import (

View File

@ -1,10 +1,10 @@
// 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.
// CRC32
// Package gcrc32 provides useful API for CRC32 encryption/decryption algorithms.
package gcrc32
import (

View File

@ -1,10 +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.
// @author: wenzi1<liyz23@qq.com>
// Package gdes provides useful API for DES encryption/decryption algorithms.
package gdes
import (
@ -261,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
@ -277,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
}

View File

@ -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){

View File

@ -1,10 +1,10 @@
// 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.
// MD5
// Package gmd5 provides useful API for MD5 encryption/decryption algorithms.
package gmd5
import (
@ -12,7 +12,7 @@ import (
"fmt"
"os"
"io"
"gitee.com/johng/gf/g/util/gconv"
"github.com/gogf/gf/g/util/gconv"
)
// 将任意类型的变量进行md5摘要(注意map等非排序变量造成的不同结果)

View File

@ -1,10 +1,10 @@
// 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.
// SHA1
// Package gsha1 provides useful API for SHA1 encryption/decryption algorithms.
package gsha1
import (
@ -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 {

View File

@ -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

View File

@ -1,10 +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 gdb provides ORM features for popular relationship databases/数据库ORM.
// Package gdb provides ORM features for popular relationship databases.
//
// 数据库ORM,
// 默认内置支持MySQL, 其他数据库需要手动import对应的数据库引擎第三方包.
package gdb
@ -12,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"
)
@ -90,7 +92,9 @@ type DB interface {
getChars() (charLeft string, charRight string)
getDebug() bool
filterFields(table string, data map[string]interface{}) map[string]interface{}
convertValue(fieldValue interface{}, fieldType string) interface{}
getTableFields(table string) (map[string]string, error)
rowsToResult(rows *sql.Rows) (Result, error)
handleSqlBeforeExec(sql string) string
}
@ -103,15 +107,16 @@ type dbLink interface {
// 数据库链接对象
type dbBase struct {
db DB // 数据库对象
group string // 配置分组名称
debug *gtype.Bool // (默认关闭)是否开启调试模式,当开启时会启用一些调试特性
sqls *gring.Ring // (debug=true时有效)已执行的SQL列表
cache *gcache.Cache // 数据库缓存,包括底层连接池对象缓存及查询缓存;需要注意的是,事务查询不支持查询缓存
schema *gtype.String // 手动切换的数据库名称
maxIdleConnCount *gtype.Int // 连接池最大限制的连接数
maxOpenConnCount *gtype.Int // 连接池最大打开的连接数
maxConnLifetime *gtype.Int // (单位秒)连接对象可重复使用的时间长度
db DB // 数据库对象
group string // 配置分组名称
debug *gtype.Bool // (默认关闭)是否开启调试模式,当开启时会启用一些调试特性
sqls *gring.Ring // (debug=true时有效)已执行的SQL列表
cache *gcache.Cache // 数据库缓存,包括底层连接池对象缓存及查询缓存;需要注意的是,事务查询不支持查询缓存
schema *gtype.String // 手动切换的数据库名称
tables map[string]map[string]string // 数据库表结构
maxIdleConnCount *gtype.Int // 连接池最大限制的连接数
maxOpenConnCount *gtype.Int // 连接池最大打开的连接数
maxConnLifetime *gtype.Int // (单位秒)连接对象可重复使用的时间长度
}
// 执行的SQL对象
@ -125,7 +130,7 @@ type Sql struct {
}
// 返回数据表记录值
type Value = gvar.VarRead
type Value = *gvar.Var
// 返回数据表记录Map
type Record map[string]Value

View File

@ -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,10 +11,11 @@ import (
"database/sql"
"errors"
"fmt"
"gitee.com/johng/gf/g/os/gcache"
"gitee.com/johng/gf/g/os/gtime"
"gitee.com/johng/gf/g/util/gconv"
"gitee.com/johng/gf/g/util/gregex"
"github.com/gogf/gf/g/container/gvar"
"github.com/gogf/gf/g/os/gcache"
"github.com/gogf/gf/g/os/gtime"
"github.com/gogf/gf/g/util/gconv"
"github.com/gogf/gf/g/text/gregex"
"reflect"
"strings"
)
@ -149,7 +150,7 @@ func (bs *dbBase) GetAll(query string, args ...interface{}) (Result, error) {
return nil, err
}
defer rows.Close()
return rowsToResult(rows)
return bs.db.rowsToResult(rows)
}
// 数据库查询,获取查询结果记录,以关联数组结构返回
@ -264,7 +265,7 @@ func (bs *dbBase) doInsert(link dbLink, table string, data Map, option int) (res
params = append(params, v)
}
operation := getInsertOperationByOption(option)
updatestr := ""
updateStr := ""
if option == OPTION_SAVE {
var updates []string
for k, _ := range data {
@ -275,7 +276,7 @@ func (bs *dbBase) doInsert(link dbLink, table string, data Map, option int) (res
),
)
}
updatestr = fmt.Sprintf("ON DUPLICATE KEY UPDATE %s", strings.Join(updates, ","))
updateStr = fmt.Sprintf("ON DUPLICATE KEY UPDATE %s", strings.Join(updates, ","))
}
if link == nil {
if link, err = bs.db.Master(); err != nil {
@ -284,7 +285,7 @@ func (bs *dbBase) doInsert(link dbLink, table string, data Map, option int) (res
}
return bs.db.doExec(link, fmt.Sprintf("%s INTO %s(%s) VALUES(%s) %s",
operation, table, strings.Join(fields, ","),
strings.Join(values, ","), updatestr),
strings.Join(values, ","), updateStr),
params...)
}
@ -323,12 +324,13 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list List, batch int,
keys = append(keys, k)
values = append(values, "?")
}
batchResult := new(batchSqlResult)
charl, charr := bs.db.getChars()
keyStr := charl + strings.Join(keys, charl + "," + charr) + charr
valueHolderStr := "(" + strings.Join(values, ",") + ")"
// 操作判断
operation := getInsertOperationByOption(option)
updatestr := ""
updateStr := ""
if option == OPTION_SAVE {
var updates []string
for _, k := range keys {
@ -339,7 +341,7 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list List, batch int,
),
)
}
updatestr = fmt.Sprintf(" ON DUPLICATE KEY UPDATE %s", strings.Join(updates, ","))
updateStr = fmt.Sprintf(" ON DUPLICATE KEY UPDATE %s", strings.Join(updates, ","))
}
// 构造批量写入数据格式(注意map的遍历是无序的)
for i := 0; i < len(list); i++ {
@ -350,12 +352,17 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list List, batch int,
if len(bvalues) == batch {
r, err := bs.db.doExec(link, fmt.Sprintf("%s INTO %s(%s) VALUES%s %s",
operation, table, keyStr, strings.Join(bvalues, ","),
updatestr),
updateStr),
params...)
if err != nil {
return result, err
return r, err
}
if n, err := r.RowsAffected(); err != nil {
return r, err
} else {
batchResult.lastResult = r
batchResult.rowsAffected += n
}
result = r
params = params[:0]
bvalues = bvalues[:0]
}
@ -364,14 +371,19 @@ func (bs *dbBase) doBatchInsert(link dbLink, table string, list List, batch int,
if len(bvalues) > 0 {
r, err := bs.db.doExec(link, fmt.Sprintf("%s INTO %s(%s) VALUES%s %s",
operation, table, keyStr, strings.Join(bvalues, ","),
updatestr),
updateStr),
params...)
if err != nil {
return result, err
return r, err
}
if n, err := r.RowsAffected(); err != nil {
return r, err
} else {
batchResult.lastResult = r
batchResult.rowsAffected += n
}
result = r
}
return result, nil
return batchResult, nil
}
// CURD操作:数据更新统一采用sql预处理
@ -434,36 +446,40 @@ func (bs *dbBase) getCache() *gcache.Cache {
return bs.cache
}
// 将map的数据按照fields进行过滤只保留与表字段同名的数据
func (bs *dbBase) filterFields(table string, data map[string]interface{}) map[string]interface{} {
if fields, err := bs.db.getTableFields(table); err == nil {
for k, _ := range data {
if _, ok := fields[k]; !ok {
delete(data, k)
// 将数据查询的列表数据*sql.Rows转换为Result类型
func (bs *dbBase) rowsToResult(rows *sql.Rows) (Result, error) {
// 列信息列表, 名称与类型
types := make([]string, 0)
columns := make([]string, 0)
columnTypes, _ := rows.ColumnTypes()
for _, t := range columnTypes {
types = append(types, t.DatabaseTypeName())
columns = append(columns, t.Name())
}
// 返回结构组装
values := make([]sql.RawBytes, len(columns))
scanArgs := make([]interface{}, len(values))
records := make(Result, 0)
for i := range values {
scanArgs[i] = &values[i]
}
for rows.Next() {
if err := rows.Scan(scanArgs...); err != nil {
return records, err
}
row := make(Record)
// 注意col字段是一个[]byte类型(slice类型本身是一个指针),多个记录循环时该变量指向的是同一个内存地址
for i, col := range values {
if col == nil {
row[columns[i]] = gvar.New(nil, true)
} else {
// 由于 sql.RawBytes 是slice类型, 这里必须使用值复制
v := make([]byte, len(col))
copy(v, col)
row[columns[i]] = gvar.New(bs.db.convertValue(v, types[i]), true)
}
}
records = append(records, row)
}
return data
return records, nil
}
// 获得指定表表的数据结构构造成map哈希表返回其中键名为表字段名称键值暂无用途(默认为字段数据类型).
func (bs *dbBase) getTableFields(table string) (fields map[string]string, err error) {
// 缓存不存在时会查询数据表结构,缓存后不过期,直至程序重启(重新部署)
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))
if err != nil {
return nil
}
fields = make(map[string]string)
for _, m := range result {
fields[m["Field"].String()] = m["Type"].String()
}
return fields
}, 0)
if err == nil {
fields = v.(map[string]string)
}
return
}

View 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()
}

View File

@ -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"
)

View File

@ -1,62 +1,25 @@
// 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
import (
"bytes"
"database/sql"
"errors"
"fmt"
"gitee.com/johng/gf/g/container/gvar"
"gitee.com/johng/gf/g/os/glog"
"gitee.com/johng/gf/g/os/gtime"
"gitee.com/johng/gf/g/util/gconv"
"gitee.com/johng/gf/g/util/gregex"
"gitee.com/johng/gf/g/util/gstr"
_ "gitee.com/johng/gf/third/github.com/go-sql-driver/mysql"
"github.com/gogf/gf/g/os/glog"
"github.com/gogf/gf/g/os/gtime"
"github.com/gogf/gf/g/util/gconv"
"github.com/gogf/gf/g/text/gregex"
"github.com/gogf/gf/g/text/gstr"
_ "github.com/gogf/gf/third/github.com/go-sql-driver/mysql"
"reflect"
"strings"
)
// 将数据查询的列表数据*sql.Rows转换为Result类型
func rowsToResult(rows *sql.Rows) (Result, error) {
// 列名称列表
columns, err := rows.Columns()
if err != nil {
return nil, err
}
// 返回结构组装
values := make([]sql.RawBytes, len(columns))
scanArgs := make([]interface{}, len(values))
records := make(Result, 0)
for i := range values {
scanArgs[i] = &values[i]
}
for rows.Next() {
err = rows.Scan(scanArgs...)
if err != nil {
return records, err
}
row := make(Record)
// 注意col字段是一个[]byte类型(slice类型本身是一个指针),多个记录循环时该变量指向的是同一个内存地址
for i, col := range values {
if col == nil {
row[columns[i]] = gvar.New(nil, false)
} else {
v := make([]byte, len(col))
copy(v, col)
row[columns[i]] = gvar.New(v, false)
}
}
records = append(records, row)
}
return records, nil
}
// 格式化SQL查询条件
func formatCondition(where interface{}, args []interface{}) (string, []interface{}) {
// 条件字符串处理
@ -80,7 +43,7 @@ func formatCondition(where interface{}, args []interface{}) (string, []interface
buffer.Write(gconv.Bytes(where))
}
if buffer.Len() == 0 {
buffer.WriteString("1")
buffer.WriteString("1=1")
}
// 查询条件处理
newWhere := buffer.String()

View File

@ -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"
)

View File

@ -1,8 +1,8 @@
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.
// You can obtain one at https://github.com/gogf/gf.
/*
@author wenzi1<liyz23@qq.com>
@date 20181109
@ -16,12 +16,11 @@ package gdb
import (
"database/sql"
"fmt"
"gitee.com/johng/gf/g/util/gregex"
"github.com/gogf/gf/g/text/gregex"
"strconv"
"strings"
)
// 数据库链接对象
type dbMssql struct {
*dbBase
@ -34,7 +33,7 @@ func (db *dbMssql) Open(config *ConfigNode) (*sql.DB, error) {
source = config.Linkinfo
} else {
source = fmt.Sprintf("user id=%s;password=%s;server=%s;port=%s;database=%s;encrypt=disable",
config.User, config.Pass, config.Host, config.Port, config.Name)
config.User, config.Pass, config.Host, config.Port, config.Name)
}
if db, err := sql.Open("sqlserver", source); err == nil {
return db, nil
@ -44,7 +43,7 @@ func (db *dbMssql) Open(config *ConfigNode) (*sql.DB, error) {
}
// 获得关键字操作符
func (db *dbMssql) getChars () (charLeft string, charRight string) {
func (db *dbMssql) getChars() (charLeft string, charRight string) {
return "\"", "\""
}
@ -92,7 +91,7 @@ func (db *dbMssql) parseSql(sql string) string {
//不含LIMIT则不处理
if gregex.IsMatchString("((?i)SELECT)(.+)((?i)LIMIT)", sql) == false {
break
}
}
//判断SQL中是否含有order by
selectStr := ""
@ -101,28 +100,26 @@ func (db *dbMssql) parseSql(sql string) string {
if haveOrderby {
//取order by 前面的字符串
queryExpr, _ := gregex.MatchString("((?i)SELECT)(.+)((?i)ORDER BY)", sql)
if len(queryExpr) != 4 || strings.EqualFold(queryExpr[1], "SELECT") == false || strings.EqualFold(queryExpr[3], "ORDER BY") == false{
if len(queryExpr) != 4 || strings.EqualFold(queryExpr[1], "SELECT") == false || strings.EqualFold(queryExpr[3], "ORDER BY") == false {
break
}
selectStr = queryExpr[2]
//取order by表达式的值
orderbyExpr, _ := gregex.MatchString("((?i)ORDER BY)(.+)((?i)LIMIT)", sql)
if len(orderbyExpr) != 4 || strings.EqualFold(orderbyExpr[1], "ORDER BY") == false || strings.EqualFold(orderbyExpr[3], "LIMIT") == false{
if len(orderbyExpr) != 4 || strings.EqualFold(orderbyExpr[1], "ORDER BY") == false || strings.EqualFold(orderbyExpr[3], "LIMIT") == false {
break
}
orderbyStr = orderbyExpr[2]
} else {
queryExpr, _ := gregex.MatchString("((?i)SELECT)(.+)((?i)LIMIT)", sql)
if len(queryExpr) != 4 || strings.EqualFold(queryExpr[1], "SELECT") == false || strings.EqualFold(queryExpr[3], "LIMIT") == false{
if len(queryExpr) != 4 || strings.EqualFold(queryExpr[1], "SELECT") == false || strings.EqualFold(queryExpr[3], "LIMIT") == false {
break
}
selectStr = queryExpr[2]
}
//取limit后面的取值范围
first, limit := 0, 0
for i := 1; i < len(res[index]); i++ {
@ -150,4 +147,31 @@ func (db *dbMssql) parseSql(sql string) string {
default:
}
return sql
}
}
// 获得指定表表的数据结构构造成map哈希表返回其中键名为表字段名称键值暂无用途(默认为字段数据类型).
func (db *dbMssql) getTableFields(table string) (fields map[string]string, err error) {
// 缓存不存在时会查询数据表结构,缓存后不过期,直至程序重启(重新部署)
v := db.cache.GetOrSetFunc("table_fields_"+table, func() interface{} {
result := (Result)(nil)
result, err = db.GetAll(fmt.Sprintf(`
SELECT c.name as FIELD, CASE t.name
WHEN 'numeric' THEN t.name + '(' + convert(varchar(20),c.xprec) + ',' + convert(varchar(20),c.xscale) + ')'
WHEN 'char' THEN t.name + '(' + convert(varchar(20),c.length)+ ')'
WHEN 'varchar' THEN t.name + '(' + convert(varchar(20),c.length)+ ')'
ELSE t.name + '(' + convert(varchar(20),c.length)+ ')' END as TYPE
FROM systypes t,syscolumns c WHERE t.xtype=c.xtype AND c.id = (SELECT id FROM sysobjects WHERE name='%s') ORDER BY c.colid`, strings.ToUpper(table)))
if err != nil {
return nil
}
fields = make(map[string]string)
for _, m := range result {
fields[strings.ToLower(m["FIELD"].String())] = strings.ToLower(m["TYPE"].String()) //sqlserver返回的field为大写的需要转为小写的
}
return fields
}, 0)
if err == nil {
fields = v.(map[string]string)
}
return
}

View File

@ -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

View File

@ -1,8 +1,8 @@
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.
// You can obtain one at https://github.com/gogf/gf.
/*
@author wenzi1<liyz23@qq.com>
@date 20181026
@ -16,7 +16,7 @@ package gdb
import (
"database/sql"
"fmt"
"gitee.com/johng/gf/g/util/gregex"
"github.com/gogf/gf/g/text/gregex"
"strconv"
"strings"
)
@ -42,7 +42,7 @@ func (db *dbOracle) Open(config *ConfigNode) (*sql.DB, error) {
}
// 获得关键字操作符
func (db *dbOracle) getChars () (charLeft string, charRight string) {
func (db *dbOracle) getChars() (charLeft string, charRight string) {
return "\"", "\""
}
@ -92,7 +92,7 @@ func (db *dbOracle) parseSql(sql string) string {
}
queryExpr, _ := gregex.MatchString("((?i)SELECT)(.+)((?i)LIMIT)", sql)
if len(queryExpr) != 4 || strings.EqualFold(queryExpr[1], "SELECT") == false || strings.EqualFold(queryExpr[3], "LIMIT") == false{
if len(queryExpr) != 4 || strings.EqualFold(queryExpr[1], "SELECT") == false || strings.EqualFold(queryExpr[3], "LIMIT") == false {
break
}
@ -141,3 +141,30 @@ func (db *dbOracle) parseSql(sql string) string {
}
return sql
}
// 获得指定表表的数据结构构造成map哈希表返回其中键名为表字段名称键值暂无用途(默认为字段数据类型).
func (db *dbOracle) getTableFields(table string) (fields map[string]string, err error) {
// 缓存不存在时会查询数据表结构,缓存后不过期,直至程序重启(重新部署)
v := db.cache.GetOrSetFunc("table_fields_"+table, func() interface{} {
result := (Result)(nil)
result, err = db.GetAll(fmt.Sprintf(`
SELECT COLUMN_NAME AS FIELD, CASE DATA_TYPE
WHEN 'NUMBER' THEN DATA_TYPE||'('||DATA_PRECISION||','||DATA_SCALE||')'
WHEN 'FLOAT' THEN DATA_TYPE||'('||DATA_PRECISION||','||DATA_SCALE||')'
ELSE DATA_TYPE||'('||DATA_LENGTH||')' END AS TYPE
FROM USER_TAB_COLUMNS WHERE TABLE_NAME = '%s' ORDER BY COLUMN_ID`, strings.ToUpper(table)))
if err != nil {
return nil
}
fields = make(map[string]string)
for _, m := range result {
fields[strings.ToLower(m["FIELD"].String())] = strings.ToLower(m["TYPE"].String()) //ORACLE返回的值默认都是大写的需要转为小写
}
return fields
}, 0)
if err == nil {
fields = v.(map[string]string)
}
return
}

View File

@ -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的操作覆盖
// 数据库链接对象

View File

@ -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>

View File

@ -0,0 +1,120 @@
// 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 (
"fmt"
"github.com/gogf/gf/g/util/gconv"
"github.com/gogf/gf/g/text/gregex"
"strings"
)
/*
// 同步数据库表结构到内存中
func (bs *dbBase) syncTableStructure() {
bs.tables = make(map[string]map[string]string)
for _, table := range bs.db.getTables() {
bs.tables[table], _ = bs.db.getTableFields(table)
}
}
*/
// 字段类型转换将数据库字段类型转换为golang变量类型
func (bs *dbBase) convertValue(fieldValue interface{}, fieldType string) interface{} {
t, _ := gregex.ReplaceString(`\(.+\)`, "", fieldType)
t = strings.ToLower(t)
switch t {
case "binary", "varbinary", "blob", "tinyblob", "mediumblob", "longblob":
return gconv.Bytes(fieldValue)
case "bit", "int", "tinyint", "small_int", "medium_int":
return gconv.Int(fieldValue)
case "big_int":
return gconv.Int64(fieldValue)
case "float", "double", "decimal":
return gconv.Float64(fieldValue)
case "bool":
return gconv.Bool(fieldValue)
default:
// 自动识别类型, 以便默认支持更多数据库类型
switch {
case strings.Contains(t, "int"):
return gconv.Int(fieldValue)
case strings.Contains(t, "text") || strings.Contains(t, "char"):
return gconv.String(fieldValue)
case strings.Contains(t, "float") || strings.Contains(t, "double"):
return gconv.Float64(fieldValue)
case strings.Contains(t, "bool"):
return gconv.Bool(fieldValue)
case strings.Contains(t, "binary") || strings.Contains(t, "blob"):
return gconv.Bytes(fieldValue)
default:
return gconv.String(fieldValue)
}
}
}
// 将map的数据按照fields进行过滤只保留与表字段同名的数据
func (bs *dbBase) filterFields(table string, data map[string]interface{}) map[string]interface{} {
if fields, err := bs.db.getTableFields(table); err == nil {
for k, _ := range data {
if _, ok := fields[k]; !ok {
delete(data, k)
}
}
}
return data
}
// 获得指定表表的数据结构构造成map哈希表返回其中键名为表字段名称键值暂无用途(默认为字段数据类型).
func (bs *dbBase) getTableFields(table string) (fields map[string]string, err error) {
// 缓存不存在时会查询数据表结构,缓存后不过期,直至程序重启(重新部署)
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))
if err != nil {
return nil
}
fields = make(map[string]string)
for _, m := range result {
fields[m["Field"].String()] = m["Type"].String()
}
return fields
}, 0)
if err == nil {
fields = v.(map[string]string)
}
return
}
/*
// 获取当前数据库所有的表结构
func (bs *dbBase) getTables() []string {
if result, _ := bs.GetAll(`SHOW TABLES`); result != nil {
array := make([]string, len(result))
for i, m := range result {
for _, v := range m {
array[i] = v.String()
break
}
}
return array
}
return nil
}
*/

View File

@ -1,15 +1,15 @@
// Copyright 2017 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
// Copyright 2017 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.
// You can obtain one at https://github.com/gogf/gf.
package gdb
import (
"database/sql"
"gitee.com/johng/gf/g/util/gregex"
_ "gitee.com/johng/gf/third/github.com/go-sql-driver/mysql"
"github.com/gogf/gf/g/text/gregex"
_ "github.com/gogf/gf/third/github.com/go-sql-driver/mysql"
)
// 数据库事务对象
@ -51,7 +51,7 @@ func (tx *TX) GetAll(query string, args ...interface{}) (Result, error) {
return nil, err
}
defer rows.Close()
return rowsToResult(rows)
return tx.db.rowsToResult(rows)
}
// 数据库查询,获取查询结果记录,以关联数组结构返回

View File

@ -1,14 +1,14 @@
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.
// You can obtain one at https://github.com/gogf/gf.
package gdb
import (
"gitee.com/johng/gf/g/encoding/gparser"
"gitee.com/johng/gf/g/util/gconv"
"github.com/gogf/gf/g/encoding/gparser"
"github.com/gogf/gf/g/util/gconv"
)
// 将记录结果转换为JSON字符串
@ -27,7 +27,7 @@ func (r Record) ToXml(rootTag...string) string {
func (r Record) ToMap() Map {
m := make(map[string]interface{})
for k, v := range r {
m[k] = v.String()
m[k] = v.Val()
}
return m
}
@ -36,7 +36,7 @@ func (r Record) ToMap() Map {
func (r Record) ToStruct(obj interface{}) error {
m := make(map[string]interface{})
for k, v := range r {
m[k] = v.String()
m[k] = v.Val()
}
return gconv.Struct(m, obj)
}

View File

@ -1,13 +1,13 @@
// Copyright 2018 gf Author(https://gitee.com/johng/gf). All Rights Reserved.
// Copyright 2018 gf Author(https://github.com/gogf/gf). All Rights Reserved.
//
// This Source Code Form is subject to the terms of the MIT License.
// If a copy of the MIT was not distributed with this file,
// You can obtain one at https://gitee.com/johng/gf.
// You can obtain one at https://github.com/gogf/gf.
package gdb
import (
"gitee.com/johng/gf/g/encoding/gparser"
"github.com/gogf/gf/g/encoding/gparser"
)
// 将结果集转换为JSON字符串
@ -16,7 +16,7 @@ func (r Result) ToJson() string {
return string(content)
}
// 将结果集转换为JSON字符串
// 将结果集转换为XML字符串
func (r Result) ToXml(rootTag...string) string {
content, _ := gparser.VarToXml(r.ToList(), rootTag...)
return string(content)

Some files were not shown because too many files have changed in this diff Show More