Go语言之自定义集合Set

2020-01-28 12:04:18丽君


//方法Elements用于生成快照
func (set *HashSet) Elements() []interface{} {
  initialLen := len(set.m)//获取HashSet中字段m的长度,即m中包含元素的数量
  //初始化一个[]interface{}类型的变量snapshot来存储m的值中的元素值
  snapshot := make([]interface{}, initialLen)
  actualLen := 0
  //按照既定顺序将迭代值设置到快照值(变量snapshot的值)的指定元素位置上,这一过程并不会创建任何新值。
  for key := range set.m {
    if actualLen < initialLen {
      snapshot[actualLen] = key
    } else {//m的值中的元素数量有所增加,使得实际迭代的次数大于先前初始化的快照值的长度
      snapshot = append(snapshot, key)//使用append函数向快照值追加元素值。
    }
    actualLen++//实际迭代的次数
  }
  //对于已被初始化的[]interface{}类型的切片值来说,未被显示初始化的元素位置上的值均为nil。
  //m的值中的元素数量有所减少,使得实际迭代的次数小于先前初始化的快照值的长度。
  //这样快照值的尾部存在若干个没有任何意义的值为nil的元素,
  //可以通过snapshot = snapshot[:actualLen]将无用的元素值从快照值中去掉。
  if actualLen < initialLen {
    snapshot = snapshot[:actualLen]
  }
  return snapshot
}

注意:在 Elements 方法中针对并发访问和修改 m 的值的情况采取了一些措施。但是由于m的值本身并不是并发安全的,所以并不能保证 Elements 方法的执行总会准确无误。要做到真正的并发安全,还需要一些辅助的手段,比如读写互斥量。

(8).获取自身的字符串表示形式。


//这个String方法的签名算是一个惯用法。 //代码包fmt中的打印函数总会使用参数值附带的具有如此签名的String方法的结果值作为该参数值的字符串表示形式。
func (set *HashSet) String() string {
  var buf bytes.Buffer//作为结果值的缓冲区
  buf.WriteString("HashSet{")
  first := true
  for key := range set.m {
    if first {
      first = 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) {//最后一个元素的后面不添加逗号
  // break;
  // } else {
  // buf.WriteString(",")
  // }
  // n++;
  //}
  buf.WriteString("}")
  return buf.String() 
}

如上已经完整地编写了一个具备常用功能的Set的实现类型,后面将讲解更多的高级功能来完善它。

3.高级功能

集合 Set 的真包含的判断功能。根据集合代数中的描述,如果集合 A 真包含了集合 B,那么就可以说集合 A 是集合 B 的一个超集。


// 判断集合 set 是否是集合 other 的超集 
func (set *HashSet) IsSuperset(other *HashSet) bool {
  if other == nil {//如果other为nil,则other不是set的子集
    return false
  }
  setLen := set.Len()//获取set的元素值数量
  otherLen := other.Len()//获取other的元素值数量
  if setLen == 0 || setLen == otherLen {//set的元素值数量等于0或者等于other的元素数量
    return false
  }
  if setLen > 0 && otherLen == 0 {//other为元素数量为0,set元素数量大于0,则set也是other的超集
    return true
  }
  for _, v := range other.Elements() {
    if !set.Contains(v) {//只要set中有一个包含other中的数据,就返回false
      return false
    }
  }
  return true
}