concurrent

package
v0.0.0-...-2824937 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Nov 2, 2020 License: MIT, MIT Imports: 10 Imported by: 0

README

go-concurrentMap

go-concurrentMap is a concurrent Map implement, it is ported from java.util.ConcurrentHashMap.

Current version: 1.0 Beta

Quick start

Put, Remove, Replace and Clear methods
m := concurrent.NewConcurrentMap()

previou, err := m.Put(1, 10)                   //return nil, nil
previou, err = m.PutIfAbsent(1, 20)            //return 10, nil

val, err := m.Get(1)                           //return 10, nil
s := m.Size()                                  //return 1

m.PutAll(map[interface{}]interface{}{
	1: 100,
	2: 200,
})

ok, err := m.RemoveEntry(1, 100)               //return true, nil

previou, err = m.Replace(2, 20)                //return 200, nil
ok, err = m.CompareAndReplace(2, 200, 20)      //return false, nil

previou, err = m.Remove(2)                     //return 20, nil

m.Clear()
s = m.Size()                                   //return 0

Safely use composition operation to update the value from multiple threads
/*---- group string by first char using ConcurrentMap ----*/
//sliceAdd function returns a function that appends v into slice
sliceAdd := func(v interface{}) (func(interface{}) interface{}){
    return func(oldVal interface{})(newVal interface{}){
		if oldVal == nil {
			vs :=  make([]string, 0, 1)
			return append(vs, v.(string))
		} else {
			return append(oldVal.([]string), v.(string))
		}
	}
}

m := concurrent.NewConcurrentMap()
//group by first char of str
group := func(str string) {
	m.Update(string(str[0]), sliceAdd(str))
}

go group("stone")
go group("jack")
go group("jackson")

/*m will include the below key-value pairs, but please note sequence may be different:
{
  s:[stone],
  j:[jack jackson],
}
*/
Use Hashable interface to customize hash code and equals logic and support reference type and pointer type
//user implements concurrent.Hasher interface
type user struct {
	id   string
	Name string
}

func (u *user) HashBytes() []byte {
	return []byte(u.id)
}
func (u *user) Equals(v2 interface{}) (equal bool) {
	u2, ok := v2.(*user)
	return ok && u.id == u2.id
}

m := concurrent.NewConcurrentMap()
previou, err := m.Put(&user, 10)                   //return nil, nil
val, err := m.Get(&user)                           //return 10, nil
Iterator and get key-value slice
//iterate ConcurrentMap
for itr := m.Iterator();itr.HasNext(); {
	k, v, _ := itr.Next()
}

//only user Next method to iterate ConcurrentMap
for itr := m.Iterator();; {
	k, v, ok := itr.Next()
	if !ok {
		break
	}
}

//ToSlice
for _, entry := range m.ToSlice(){
	k, v := entry.Key(), entry.Value()
}
More factory functions
//new concurrentMap with specified initial capacity
m = concurrent.NewConcurrentMap(32)

//new concurrentMap with specified initial capacity and load factor
m = concurrent.NewConcurrentMap(32, 0.75)

//new concurrentMap with specified initial capacity, load factor and concurrent level
m = concurrent.NewConcurrentMap(32, 0.75, 16)

//new concurrentMap with the same mappings as the given map
m = concurrent.NewConcurrentMapFromMap(map[interface{}]interface{}{
		"x":                      "x1val",
		"xx":                     "x2val",
	})
	

Doc

Go Doc at godoc.org

Limitations

  • Do not support the below types as key:

    • pointer
    • slice (do not support == operator)
    • map (do not support == operator)
    • channel
    • function (do not support == operator)
    • struct that includes field which type is above-mentioned or interface
    • array which element type is above-mentioned or interface

    Do not support pointer because the memory address of pointer may be changed after GC, so cannot get a invariant value as hash code for pointer type. Please refer to when in next releases of go compacting GC move pointers, does map on poiner types will work ?

Performance

Below are the CPU, OS and parameters of benchmark testing:

Xeon E3-1230V3 3.30GHZ, Win7 64 OS

Use 8 procs and 9 goroutines, every goroutines will put or get 100,000 key-value pairs.

I used a thread safe implement that uses the RWMutex to compare the performance,. The below are the test results:

Use RWMutex ConcurrentMap
Put 480.000 ms/op 130.207 ms/op
Get 45.643 ms/op 69.464 ms/op
Put and Get 729.534 ms/op 166.610 ms/op

Note the performance of LockMap's Get operation is better than concurrentMap, the reason is that RWMutex supports parallel read. But if multiple threads put and get at same time, ConcurrentMap will be better than LockMap.

License

go-concurrentMap is licensed under the MIT Licence, (http://www.apache.org/licenses/LICENSE-2.0.html).

Documentation

Index

Constants

View Source
const (
	/**
	 * The default initial capacity for this table,
	 * used when not otherwise specified in a constructor.
	 */
	DEFAULT_INITIAL_CAPACITY int = 16

	/**
	 * The default load factor for this table, used when not
	 * otherwise specified in a constructor.
	 */
	DEFAULT_LOAD_FACTOR float32 = 0.75

	/**
	 * The default concurrency level for this table, used when not
	 * otherwise specified in a constructor.
	 */
	DEFAULT_CONCURRENCY_LEVEL int = 16

	/**
	 * The maximum capacity, used if a higher value is implicitly
	 * specified by either of the constructors with arguments.  MUST
	 * be a power of two <= 1<<30 to ensure that entries are indexable
	 * using ints.
	 */
	MAXIMUM_CAPACITY int = 1 << 30

	/**
	 * The maximum number of segments to allow; used to bound
	 * constructor arguments.
	 */
	MAX_SEGMENTS int = 1 << 16 // slightly conservative

	/**
	 * Number of unsynchronized retries in size and containsValue
	 * methods before resorting to locking. This is used to avoid
	 * unbounded retries if tables undergo continuous modification
	 * which would make it impossible to obtain an accurate result.
	 */
	RETRIES_BEFORE_LOCK int = 2
)

Variables

View Source
var (
	Debug           = false
	NilKeyError     = errors.New("Do not support nil as key")
	NilValueError   = errors.New("Do not support nil as value")
	NilActionError  = errors.New("Do not support nil as action")
	NonSupportKey   = errors.New("Non support for pointer, interface, channel, slice, map and function ")
	IllegalArgError = errors.New("IllegalArgumentException")
)

Functions

func Printf

func Printf(format string, a ...interface{}) (n int, err error)

func Println

func Println(a ...interface{}) (n int, err error)

Types

type ConcurrentMap

type ConcurrentMap struct {
	// contains filtered or unexported fields
}

segments is read-only, don't need synchronized

func NewConcurrentMap

func NewConcurrentMap(paras ...interface{}) (m *ConcurrentMap)

*

  • Creates a new, empty map with the specified initial
  • capacity, load factor and concurrency level. *
  • @param initialCapacity the initial capacity. The implementation
  • performs internal sizing to accommodate this many elements. *
  • @param loadFactor the load factor threshold, used to control resizing.
  • Resizing may be performed when the average number of elements per
  • bin exceeds this threshold. *
  • @param concurrencyLevel the estimated number of concurrently
  • updating threads. The implementation performs internal sizing
  • to try to accommodate this many threads. *
  • panic error "IllegalArgumentException" if the initial capacity is
  • negative or the load factor or concurrencyLevel are
  • nonpositive. *
  • Creates a new, empty map with a default initial capacity (16),
  • load factor (0.75) and concurrencyLevel (16).

func NewConcurrentMapFromMap

func NewConcurrentMapFromMap(m map[interface{}]interface{}) *ConcurrentMap

*

  • Creates a new map with the same mappings as the given map.
  • The map is created with a capacity of 1.5 times the number
  • of mappings in the given map or 16 (whichever is greater),
  • and a default load factor (0.75) and concurrencyLevel (16). *
  • @param m the map

func (*ConcurrentMap) Clear

func (this *ConcurrentMap) Clear()

*

  • Removes all of the mappings from this map.

func (*ConcurrentMap) CompareAndReplace

func (this *ConcurrentMap) CompareAndReplace(key interface{}, oldVal interface{}, newVal interface{}) (ok bool, err error)

*

  • CompareAndReplace executes the compare-and-replace operation.
  • Replaces the value if the mapping exists for the previous and key from this map.
  • This method does nothing if no mapping for the key and value. *
  • @return true if value be replaced, false otherwise

func (*ConcurrentMap) ContainsKey

func (this *ConcurrentMap) ContainsKey(key interface{}) (found bool, err error)

*

  • Tests if the specified object is a key in this table. *
  • @param key possible key
  • @return true if and only if the specified object is a key in this table,
  • as determined by the == method; false otherwise.

func (*ConcurrentMap) Get

func (this *ConcurrentMap) Get(key interface{}) (value interface{}, err error)

*

  • Returns the value to which the specified key is mapped,
  • or nil if this map contains no mapping for the key.

func (*ConcurrentMap) IsEmpty

func (this *ConcurrentMap) IsEmpty() bool

*

  • Returns true if this map contains no key-value mappings.

func (*ConcurrentMap) Iterator

func (this *ConcurrentMap) Iterator() *MapIterator

Iterator returns a iterator for ConcurrentMap

func (*ConcurrentMap) Put

func (this *ConcurrentMap) Put(key interface{}, value interface{}) (oldVal interface{}, err error)

*

  • Maps the specified key to the specified value in this table.
  • Neither the key nor the value can be nil. *
  • The value can be retrieved by calling the get method
  • with a key that is equal to the original key. *
  • @param key with which the specified value is to be associated
  • @param value to be associated with the specified key *
  • @return the previous value associated with key, or
  • nil if there was no mapping for key

func (*ConcurrentMap) PutAll

func (this *ConcurrentMap) PutAll(m map[interface{}]interface{}) (err error)

*

  • Copies all of the mappings from the specified map to this one.
  • These mappings replace any mappings that this map had for any of the
  • keys currently in the specified map. *
  • @param m mappings to be stored in this map

func (*ConcurrentMap) PutIfAbsent

func (this *ConcurrentMap) PutIfAbsent(key interface{}, value interface{}) (oldVal interface{}, err error)

*

  • If mapping exists for the key, then maps the specified key to the specified value in this table.
  • else will ignore.
  • Neither the key nor the value can be nil. *
  • The value can be retrieved by calling the get method
  • with a key that is equal to the original key. *
  • @return the previous value associated with the specified key,
  • or nil if there was no mapping for the key

func (*ConcurrentMap) Remove

func (this *ConcurrentMap) Remove(key interface{}) (oldVal interface{}, err error)

*

  • Removes the key (and its corresponding value) from this map.
  • This method does nothing if the key is not in the map. *
  • @param key the key that needs to be removed
  • @return the previous value associated with key, or nil if there was no mapping for key

func (*ConcurrentMap) RemoveEntry

func (this *ConcurrentMap) RemoveEntry(key interface{}, value interface{}) (ok bool, err error)

*

  • Removes the mapping for the key and value from this map.
  • This method does nothing if no mapping for the key and value. *
  • @return true if mapping be removed, false otherwise

func (*ConcurrentMap) Replace

func (this *ConcurrentMap) Replace(key interface{}, value interface{}) (oldVal interface{}, err error)

*

  • Replaces the value if the key is in the map.
  • This method does nothing if no mapping for the key. *
  • @return the previous value associated with the specified key,
  • or nil if there was no mapping for the key

func (*ConcurrentMap) Size

func (this *ConcurrentMap) Size() int32

*

  • Returns the number of key-value mappings in this map.

func (*ConcurrentMap) ToSlice

func (this *ConcurrentMap) ToSlice() (kvs []*Entry)

ToSlice returns a slice that includes all key-value Entry in ConcurrentMap

func (*ConcurrentMap) Update

func (this *ConcurrentMap) Update(key interface{}, action func(oldVal interface{}) (newVal interface{})) (oldVal interface{}, err error)

*

  • Maps the specified key to the value that be returned by specified function in this table.
  • The key can not be nil. *
  • The value mapping specified key will be passed into action function as parameter.
  • If mapping does not exists for the key, nil will be passed into action function.
  • If return value by action function is nil, the specified key will be remove from map. *
  • @param key with which the specified value is to be associated
  • @param action that be called to generate new value mapping the specified key *
  • @return the previous value associated with key, or
  • nil if there was no mapping for key

type Entry

type Entry struct {
	// contains filtered or unexported fields
}

*

  • ConcurrentHashMap list entry.
  • Note only value field is variable and must use atomic to read/write it, other three fields are read-only after initializing.
  • so can use unsynchronized reader, the Segment.readValueUnderLock method is used as a
  • backup in case a nil (pre-initialized) value is ever seen in
  • an unsynchronized access method.

func (*Entry) Key

func (this *Entry) Key() interface{}

func (*Entry) Value

func (this *Entry) Value() interface{}

type Hashable

type Hashable interface {
	HashBytes() []byte
	Equals(v2 interface{}) bool
}

type MapIterator

type MapIterator struct {
	// contains filtered or unexported fields
}

func (*MapIterator) HasNext

func (this *MapIterator) HasNext() bool

func (*MapIterator) Next

func (this *MapIterator) Next() (key interface{}, value interface{}, ok bool)

func (*MapIterator) Remove

func (this *MapIterator) Remove() (ok bool)

type Once

type Once struct {
	// contains filtered or unexported fields
}

Once is an object that will perform exactly one action.

func (*Once) Do

func (o *Once) Do(f func())

Do calls the function f if and only if Do is being called for the first time for this instance of Once. In other words, given

var once Once

if once.Do(f) is called multiple times, only the first call will invoke f, even if f has a different value in each invocation. A new instance of Once is required for each function to execute.

Do is intended for initialization that must be run exactly once. Since f is niladic, it may be necessary to use a function literal to capture the arguments to a function to be invoked by Do:

config.once.Do(func() { config.init(filename) })

Because no call to Do returns until the one call to f returns, if f causes Do to be called, it will deadlock.

func (*Once) IsDone

func (o *Once) IsDone() bool

type Segment

type Segment struct {
	// contains filtered or unexported fields
}

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL