The Go language custom collection Set_golang

Source: Internet
Author: User
Tags hash set set sprintf

First, go language combat-custom Set Set

There is a dictionary (Map) type implemented as a Hash Table in the go language, but there is no collection (set) of the data type in the standard data type. Compare the main features of Set and Map , with similar characteristics as follows:

The elements in them are not repeatable.

All of these elements can only be retrieved in an iterative manner.

The order in which elements are iterated is independent of the order in which they are inserted, and does not guarantee any ordering.

However, there are some differences between them, as follows:

The element of a Set is a single value, and the Map element is a key-value pair.

The elements of a Set cannot be duplicated when any two single values are not equal. The elements of a Map cannot be duplicated, meaning that the values of keys in any two key pairs cannot be equal.

From the above features, you can use the collection type (set) as a simplified version of the dictionary type (Map). In other words, you can use a Map to write an implementation of a Set type. In fact, in the Java language, java.util.HashSet classes are java.util.HashMap supported by classes as the underlying. So here we start from hashset and abstract set set.

1. Define HashSet

First, create a source file named hash_set.go in the basic/setof the src directory in the workspace (which you can define yourself, but be consistent later).

According to the code package Basic/set , the source file hash_set.go the Package declaration statement (some of the rules can look at the previous series of posts) as follows:

package set

The above mentioned that you can use the collection type as a simplified version of the dictionary type. Now our HashSet is based on the dictionary type as its underlying implementation. The hashset statement reads as follows:

Type hashset struct {
  m map[interface{}]bool
}

The type that declares the only field in the hashset type is map[interface{}]bool . This type of dictionary is chosen because the key type of the dictionary m is set to interface{}, allow hashset elements to be of any type, because the keys in the value of M are used to store the element values of the hashset type. The advantage of the element type using the bool type as the value of M is as follows:

From the point of view of the storage form of the value, thebool type value occupies only one byte.

From the perspective of the representation of a value, the value of typebool is only two-true and false. Also, both of these values are predefined constants.

It is more advantageous to determine whether a key exists in a dictionary type value by taking the bool type as a value type. For example, if you always use True as the value of an element when you add a key value pair to the value of M, the result value of the index expression m["a" always shows whether the value in M contains a key value pair with the key "a". For map[interface{}]bool the value of the type, the following:

If m["a"] {//Determine if M contains key value pairs
  //Omitting other statements
}

If the basic structure of the hashset type has been determined, consider how to initialize the hashset type value. Because the 0 value of the dictionary type is nil, and the new function is used to create a hashset type value, new(HashSet).m the evaluation result will be a nil (about the new letter The number can refer to another blog post go language learning Note 5). Therefore, you need to write a function that specifically creates and initializes the hashset type value, which is declared as follows:

Func Newhashset () *hashset {return
  &hashset{m:make (Map[interface{}]bool)}
}

As you can see, the field M is initialized with the Make function. Also note that the type of the result declaration of the observation function newhashset is *hashset rather than hashset, so that the method set of this result value contains the calling recipient type all methods of HashSet or *hashset . The benefits of doing so will be explained later when you write the Set interface type.

2. Realize the basic function of HashSet

Depending on the type of HashSet in other programming languages, most of the basic functionality they should provide is as follows:

Adds an element value.

Deletes an element value.

Clears all element values.

Determines whether an element value is included.

Gets the number of element values.

Determines whether the values are the same as other hashset types.

Gets all the element values, that is, a snapshot that can be iterated.

Gets the string representation of itself.

Now that these functions are implemented, the reader can implement them, the following for reference only.

(1). add element value

Method Add returns a bool-type result value to indicate whether the operation that added the element value was successful. The
receiver type in the declaration of//method add is *hashset.
func (set *hashset) Add (e interface{}) bool {
  if!set.m[e] {//The value of the current m does not yet contain a key value to the value of E to
    set.m[e] = true//The key to E ( The value represented by the key value for the element true return
    True//Add success
  } return
  false//Add failed
}

This uses *hashset instead of HashSet, mainly from the point of view of saving memory space, the analysis is as follows:

When the recipient type of the Add method is hashset , each call to it requires a copy of the current hashset type value. Although there is only one field of reference type in the hashset type, this is also an overhead. And it's not yet considered that the fields in the hashset type might become more.

When the recipient type of the Add method is *hashset , the type value of the current *hashset that is replicated when it is invoked is only a pointer value. In most cases, the memory space occupied by a pointer value is always less memory space occupied by the other type of value it points to. Regardless of the amount of memory space required for the other type value that a pointer value points to, the memory space it occupies is always the same.

(2). Delete element values

Call Delete built-in function to delete HashSet internal supported dictionary values
func (set *hashset) remove (e interface{}) {
  Delete (SET.M, E)//The first argument is the target dictionary type , and the second parameter is the key for the key value pair that you want to delete

(3). Clear all elements


func (set *hashset) Clear () {
  set.m = make (Map[interface{}]bool)} for field M in HashSet

If the receiver type is hashset, the value of the assignment statement in the method is only assigned to the field m in a replica of the current value, and the field m in the current value is not assigned a value. After This assignment statement in the method clear is executed, the element in the current hashset type value is emptied. The old dictionary value that has been unbound with field M has become useless data because it no longer has a binding relationship with any program entity. It will be discovered and recycled at a later time by the garbage collector of the go language.

(4). Determines whether an element value is included.

Method contains is used to determine whether the value contains an element value.
//Here The results benefit from fields with the element type bool M
func (set *hashset) Contains (e interface{}) bool {return
  set.m[e]
}

When you interface{} add a type value as a key to a dictionary value, the go language first gets the interface{} actual type of the value of the type (that is, the dynamic type), and then hashes the value using the hash function corresponding to it. That is, the interface{} value of a type can always be computed with the hash value correctly. However, a dictionary-type key cannot be a function type, a dictionary type, or a slice type, or it can cause a run-time panic and be prompted as follows:
panic: runtime error: hash of unhashable type < name of a function type, dictionary type, or slice type >

(5). Gets the number of element values.

Method Len is used to get the number of HashSet element values
func (set *hashset) len () int {return
  Len (SET.M)
}

(6). Determine whether the values are the same as other hashset types.

Method Same is used to determine whether two HashSet type values are the same
func (set *hashset) Same (other *hashset) bool {
  If other = nil {return
    false
   }
  if set. Len ()!= other. Len () {return
    false
  } for
  key: = Range SET.M {
    if!other. Contains (key) {return false}}/return
  True
}

Two hashset type values are the same if they contain elements that should be exactly the same. Because the iteration order of the elements in the hashset type value is always indeterminate, there is no need to care whether two values are consistent in this respect. If you want to determine whether two HashSet type values are the same value, you need to use pointer arithmetic to compare memory addresses.

(7). Gets all the element values, that is, a snapshot that can be iterated.

The

Snapshot is the image of the target value at a certain point in time. For a hashset type value, the sequence of elements in its snapshot can always be determined, and the snapshot reflects only the state of the HashSet type value at a certain point in time. In addition, you need to select a type that is a snapshot from a data type that can be iterated and sequentially determined by the element. This type must be a single value as an element, so the dictionary type is not excluded first. Also because the number of elements in the hashset type value is always not fixed, it is not possible to represent its snapshot with a value of an array type. As the previous analysis shows, the type of snapshot that can be used in the go language should be a slice type or a channel type.

//method Elements used to generate snapshot func (set *hashset) Elements () []interface{} {initiallen: = Len (set. m)//Gets the length of the field m in HashSet, that is, the number of elements in M//Initializes a variable snapshot of type []interface{} to store the element value in the value of M snapshot: = Make ([]interface{},
  Initiallen) Actuallen: = 0//Set the iteration value to the specified element position of the snapshot value (the value of the variable snapshot) in an established order, this procedure does not create any new values. For key: = Range SET.M {if Actuallen < Initiallen {Snapshot[actuallen] = key} else {//m has an increase in the number of elements in the value, making
    The actual number of iterations is greater than the length of the previously initialized snapshot value snapshot = Append (snapshot, key)//uses the APPEND function to append the element value to the snapshot value.
  actuallen++//number of actual iterations}//The value of the element position that is not initialized is nil for a slice value of the []interface{} type that has been initialized.
  The number of elements in the value of M decreases, making the actual number of iterations less than the length of the previously initialized snapshot value.
  The tail of this snapshot value has several elements that have no meaningful value of nil,//can be removed from the snapshot value by snapshot = Snapshot[:actuallen] The value of the unwanted element. If Actuallen < Initiallen {snapshot = Snapshot[:actuallen]} return snapshot} 

Note: Some measures have been taken in the Elements method for concurrent access and modification of the value of M. However, because the value of M is not concurrent security, it does not guarantee that the Elements method will always be accurate. To achieve real concurrency security, there are also a number of ancillary tools, such as read-write mutexes.

(8). Gets the string representation of itself.

The signature of this string method is a customary method. The print function in Code Pack FMT always uses the result value of a string method with this signature that is included with the parameter value as the string representation of the parameter value.
func (set *hashset) string () string {
  var buf bytes. buffer//as the result value of the buffer
  buf. WriteString ("hashset{") I
  : = True for
  key: = Range SET.M {if before {i
      = False
    } else {
   buf. WriteString (",")
    }
    buf. WriteString (FMT. Sprintf ("%v", Key)
  }
  //n: = 1
  //for key: = Range SET.M {
  //BUF. WriteString (FMT. Sprintf ("%v", key))
  //If n = Len (SET.M) {//The last element followed by no comma
  //break;
  else {
  //BUF. WriteString (",")
  //}
  //n++;
  }
  buf. WriteString ("}") return
  buf. String () 
}

If you have already written a complete implementation type of set with common functions, you will later explain more advanced features to refine it.

3. Advanced Features

The judgment function of the true inclusion of set set. According to the description in the set algebra, if set a really contains a set B, then it can be said that set a is a superset of the set B.

Determines whether set set is a superset of set 
func (set *hashset) Issuperset (other *hashset) bool {
  If other = nil {//If other is nil, Other is not a subset of Set return
    false
  }
  Setlen: = set. Len ()//Gets the number of element values for set
  Otherlen: = other. Len ()//Get other element value quantity
  if Setlen = = 0 | | setlen = = Otherlen {//set element value equal to 0 or equal to other element quantity return
    false
  }
  if Setlen > 0 && Otherlen = = 0 {//other is the number of elements 0,set the number of elements is greater than 0, then the set is other superset return
    true
  }
  For _, V: = range other. Elements () {
    if!set. Contains (v) {//returns false return False}} as long as there is one in the set that contains data from other

The operations of a set include a set of parallel sets, intersections, difference sets, and symmetric differences .

A combined operation is a combination of all the elements in a two set and grouped together into a single set.

An intersection operation is an element that finds two sets of elements and makes them into a set.

The difference between set a and set b is to find elements that exist only in set a but do not exist in set B and make them into a set.

The symmetric difference set operation is similar to the difference set operation but differs. Symmetric difference set operations are those that find elements that exist only in set a but do not exist in set B , and then find elements that exist only in set B but not in set a, and finally combine them and form a set.

Implementation of the and set operation

Sets the set
*hashset Union (other *hashset) *hashset {
  if set = = Nil | | | other = NIL {// Set and other are nil, their set is nil return
    nil
  }
  unionedset: = Newhashset ()//Creates a new HashSet type value with a length of 0, that is, the number of elements is 0
  for _, V: = Range set. Elements () {///Add elements in set to Unionedset
    Unionedset.add (v)
  } if other
  . Len () = = 0 {return
    unionedset
  }
  for _, V: = range other. Elements () {//Add elements from other to Unionedset, if encountered the same, do not add (reflected in the Add method logic)
    Unionedset.add (v)
  } return
  Unionedset
}

Implementing intersection operations

To generate the intersection of Set set and other collection
func (set *hashset) Intersect (other *hashset) *hashset {
  if set = = Nil | | | other = NIL { Set and other are nil, their intersection is Nil return
    nil
  }
  intersectedset: = Newhashset ()//Creates a new HashSet type value with a length of 0, That is, the number of elements is 0
  if other. Len () = = 0 {//other's element number is 0, returning directly to Intersectedset return
    intersectedset
  }
  if set. Len () < other. Len () {The number of elements in//set is less than the number of other elements for
    _, V: = Range set. Elements () {//Traverse set
      if other. Contains (v) {//Just add the set and other common to Intersectedset
        Intersectedset.add (v)}}}
  else {// The number of elements in the set is more than the number of other elements for
    _, V: = range. Elements () {//Traverse other
      if set. Contains (v) {//As long as the addition of set and other shares to Intersectedset
        Intersectedset.add (v)}}}
  Intersectedset
}

Difference Set

Generate set Set pair set
func (set *hashset) difference (other *hashset) *hashset {
  if set = = Nil | | other = = NIL {//Set and other are nil, their difference set is nil return
    nil
  }
  differencedset: = Newhashset ()/Create a new HashSet type value with a length of 0, That is, the number of elements is 0
  if other. Len () = = 0 {//If the number of other elements is 0 for
    _, V: = Range set. Elements () {///traverse set and add Element V in set to Differencedset
      Differencedset.add (v)} return
    differencedset// return directly Differencedset
  }
  for _, V: = Range set. The number of elements in Elements () {//other is not 0, traversing the set
    if!other. Contains (v) {//If other does not contain V, add v to differencedset
      Differencedset.add (v)
    }
  }
  return Differencedset
}

Symmetric difference Set

Sets the symmetric difference set
func (set *hashset) symmetricdifference (other *hashset) *hashset {
  if set = = Nil | | ot her = = Nil {//set and other are nil, their symmetric difference set is nil return
    nil
  }
  Diffa: = set. Difference (other)//generates a set set pair of set pairs of the difference set of other
  . Len () = = 0 {//If the number of other elements equals 0, then the difference set for the set is null, and then the Diffa return
    Diffa} DIFFB is returned directly
  : = other. Difference (set)//Generate set other pair set Set's difference set
  return diffa.union (DIFFB)//Returns collection Diffa and collection DIFFB's set
}

4. Further refactoring
The hashset type currently implemented provides some of the necessary set operation capabilities, but may require a richer collection type in different scenarios. When you have multiple collection types, you should extract an interface type on top of them to identify the behavior they share. Depending on the declaration of the hashset type, the Set interface type can be declared as follows:

Type Set Interface {
  Add (e interface{}) bool
  Remove (E interface{}) Clear
  ()
  Contains (e interface{} BOOL
  Len () int
  Same (other Set) bool
  Elements () []interface{}
  string () string
}

Note: The signature of the Same method in Set differs from the Same method attached to the hashset type. There is no way to include its implementation type in the signature of a method of an interface type. So the changes here are as follows:

Func (set *hashset) Same (other set) bool {
  //omit several statements
}

The signature of the Same method was modified to make the *hashset type an implementation type of the Set interface type.

The advanced functionality method should be applied to all implementation types and can be completely detached from the function. Also, these advanced methods should not be implemented repeatedly in each implementation type. The following is a declaration of the reformed Issuperset method:

To determine if a set one is a superset of the set or the
reader should focus on the difference between Issuperset and the Issuperset method attached to the HashSet type
func issuperset (one set, other set BOOL {
  if one = = Nil | | | = NIL {return
    false
  }
  Onelen: = one. Len ()
  Otherlen: = other. Len ()
  if Onelen = 0 | | onelen = = Otherlen {return
    false
  }
  if Onelen > 0 && otherlen = 0 { C12/>return true
  }
  for _, V: = range other. Elements () {
    if!one. Contains (v) {return false}}/return
  True
}

These are all the contents of the Go language custom set set, which I hope will help you learn the go language.

Related Article

Contact Us

The content source of this page is from Internet, which doesn't represent Alibaba Cloud's opinion; products and services mentioned on that page don't have any relationship with Alibaba Cloud. If the content of the page makes you feel confusing, please write us an email, we will handle the problem within 5 days after receiving your email.

If you find any instances of plagiarism from the community, please send an email to: info-contact@alibabacloud.com and provide relevant evidence. A staff member will contact you within 5 working days.

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.