github.com/Nigel2392/go-datastructures@v1.1.5/hashmap/hashmap.go (about)

     1  package hashmap
     2  
     3  import (
     4  	"fmt"
     5  	"strconv"
     6  	"strings"
     7  
     8  	"github.com/Nigel2392/go-datastructures"
     9  )
    10  
    11  const defaultBucketLen = 16
    12  
    13  // Not to be used directly. Use the Map() function instead.
    14  //
    15  // A hashmap implementation with a specified number of buckets.
    16  //
    17  // These buckets are implemented as binary search trees.
    18  //
    19  // Inside the binary search trees, the keys are stored as linked lists.
    20  //
    21  // It is up to the user to ensure that the key type implements the datastructures.Hashable[T] interface,
    22  //
    23  // and that the key hashing function is secure, fast and collision-free.
    24  type HashMap[T1 datastructures.Hashable[T1], T2 any] struct {
    25  	buckets   []*bucket[T1, T2]
    26  	len       int
    27  	bucketLen uint64
    28  }
    29  
    30  // Returns a new HashMap[T1, T2].
    31  //
    32  // If no argument is given, the default number of buckets is used (16).
    33  func Map[T1 datastructures.Hashable[T1], T2 any](amount ...int) *HashMap[T1, T2] {
    34  	if len(amount) == 0 {
    35  		return newMap[T1, T2](defaultBucketLen)
    36  	} else if len(amount) > 1 {
    37  		panic(fmt.Sprintf("Map[T1, T2] takes at most 1 argument, %d given", len(amount)))
    38  	}
    39  	var k = amount[0]
    40  	if k < 0 {
    41  		panic(fmt.Sprintf("Map[T1, T2] takes a positive integer, %d given", amount))
    42  	}
    43  	if k == 0 {
    44  		return newMap[T1, T2](defaultBucketLen)
    45  	}
    46  
    47  	return newMap[T1, T2](calcBuckets(uint64(k)))
    48  }
    49  
    50  // Calculates the number of buckets to use for the given number of items.
    51  //
    52  // The number of buckets is a power of 2.
    53  func calcBuckets(items uint64) uint64 {
    54  	if items > (1 << 31) {
    55  		return uint64(float64(items) * 1.5)
    56  	}
    57  
    58  	var buckets uint64 = 1
    59  	for buckets < items {
    60  		buckets <<= 1
    61  	}
    62  
    63  	if buckets > (1 << 31) {
    64  		return uint64(float64(buckets>>1) * 1.5)
    65  	}
    66  
    67  	return buckets
    68  }
    69  
    70  // Instantiates a new HashMap[T1, T2] with the given number of buckets.
    71  func newMap[T1 datastructures.Hashable[T1], T2 any](buckets uint64) *HashMap[T1, T2] {
    72  	var table = HashMap[T1, T2]{
    73  		bucketLen: buckets,
    74  		buckets:   make([]*bucket[T1, T2], buckets, buckets),
    75  	}
    76  	for i := range table.buckets {
    77  		table.buckets[i] = &bucket[T1, T2]{}
    78  	}
    79  	return &table
    80  }
    81  
    82  func indexOf(hash uint64, buckets uint64) uint64 {
    83  	return (hash ^ (hash >> 16)) & (buckets - 1)
    84  }
    85  
    86  // Sets a value in the map.
    87  func (t *HashMap[T1, T2]) Set(k T1, v T2) {
    88  	var hash uint64 = k.Hash()
    89  	t.buckets[indexOf(hash, t.bucketLen)].insert(hash, k, v)
    90  	t.len++
    91  }
    92  
    93  // Gets a value from the map.
    94  func (t *HashMap[T1, T2]) Get(k T1) (v T2, ok bool) {
    95  	var hash uint64 = k.Hash()
    96  	return t.buckets[indexOf(hash, t.bucketLen)].retrieve(k)
    97  }
    98  
    99  // Deletes a value from the map.
   100  func (t *HashMap[T1, T2]) Delete(k T1) (ok bool) {
   101  	var hash uint64 = k.Hash()
   102  	ok = t.buckets[indexOf(hash, t.bucketLen)].delete(k)
   103  	if ok {
   104  		t.len--
   105  	}
   106  	return
   107  }
   108  
   109  // Deletes a value from the map if the predicate returns true.
   110  func (t *HashMap[T1, T2]) DeleteIf(p func(T1, T2) bool) (amountDeleted int) {
   111  	var deleted int
   112  
   113  	for _, bucket := range t.buckets {
   114  		deleted = bucket.deleteIf(p)
   115  		amountDeleted += deleted
   116  	}
   117  
   118  	t.len -= amountDeleted
   119  	return
   120  }
   121  
   122  // Returns the number of items in the map.
   123  func (t *HashMap[T1, T2]) Len() int {
   124  	return t.len
   125  }
   126  
   127  // Returns all of the keys in the map.
   128  func (t *HashMap[T1, T2]) Keys() []T1 {
   129  	var keys = make([]T1, t.len, t.len)
   130  	var i int
   131  	for _, bucket := range t.buckets {
   132  		bucket.traverse(func(k T1, v T2) bool {
   133  			keys[i] = k
   134  			i++
   135  			return true
   136  		})
   137  	}
   138  
   139  	return keys
   140  }
   141  
   142  // Returns all of the values in the map.
   143  func (t *HashMap[T1, T2]) Values() []T2 {
   144  	var values = make([]T2, t.len, t.len)
   145  	var i int
   146  	for _, bucket := range t.buckets {
   147  		bucket.traverse(func(k T1, v T2) bool {
   148  			values[i] = v
   149  			i++
   150  			return true
   151  		})
   152  	}
   153  	return values
   154  }
   155  
   156  // Clear the map.
   157  func (t *HashMap[T1, T2]) Clear() {
   158  	for i := range t.buckets {
   159  		t.buckets[i] = &bucket[T1, T2]{}
   160  	}
   161  	t.len = 0
   162  }
   163  
   164  // Range over the map.
   165  func (t *HashMap[T1, T2]) Range(f func(k T1, v T2) (continueLoop bool)) {
   166  	for _, bucket := range t.buckets {
   167  		if !bucket.traverse(f) {
   168  			return
   169  		}
   170  	}
   171  }
   172  
   173  // Pop a value from the map.
   174  //
   175  // Returns the value and a boolean indicating whether the value was found.
   176  func (t *HashMap[T1, T2]) Pop(k T1) (v T2, ok bool) {
   177  	var hash uint64 = k.Hash()
   178  	v, ok = t.buckets[indexOf(hash, t.bucketLen)].pop(k)
   179  	if ok {
   180  		t.len--
   181  	}
   182  	return
   183  }
   184  
   185  // Returns a string representation of the map.
   186  func (t *HashMap[T1, T2]) String() string {
   187  	var b strings.Builder
   188  	b.WriteString("{")
   189  	var i int
   190  	t.Range(func(k T1, v T2) (continueLoop bool) {
   191  		b.WriteString(fmt.Sprintf("%v:%v\n", k, v))
   192  		if i < t.len-1 {
   193  			b.WriteString(", ")
   194  		}
   195  		i++
   196  		return true
   197  	})
   198  	b.WriteString("}")
   199  	return b.String()
   200  }
   201  
   202  // Returns the GoString representation of the map.
   203  //
   204  // Because we allow you to define your own hashing, and comparison functions
   205  // we allow you to see every bit of the insides of the map for debugging purposes.
   206  func (t *HashMap[T1, T2]) GoString() string {
   207  	var b strings.Builder
   208  	b.WriteString("Map[T1, T2]{")
   209  	for j, bucket := range t.buckets {
   210  		if bucket._len == 0 {
   211  			b.WriteString(fmt.Sprintf("\n\tBucket{index: %d, bucketLen: %d, items: []}", j, bucket._len))
   212  			if j < len(t.buckets)-1 {
   213  				b.WriteString(", ")
   214  			}
   215  		} else {
   216  			b.WriteString("\n\tBucket{")
   217  			b.WriteString(fmt.Sprintf("\n\t\tindex: %d", j))
   218  			b.WriteString("\n\t\tbucketLen: ")
   219  			b.WriteString(strconv.FormatInt(int64(bucket._len), 10))
   220  			b.WriteString("\n\t\titems: [")
   221  			var o int
   222  			traverseTree(bucket.root, func(n *bucketNode[T1, T2]) bool {
   223  				b.WriteString("\n\t\t\tbucketNode: {")
   224  				b.WriteString(fmt.Sprintf("\n\t\t\thash: %d", n._hash))
   225  				b.WriteString("\n\t\t\t\tnodes: [")
   226  				for next := n.next; next != nil; next = next.next {
   227  					b.WriteString(fmt.Sprintf("%v:%v", next.key, next.value))
   228  					if next.next != nil {
   229  						b.WriteString(" > ")
   230  					}
   231  				}
   232  				b.WriteString("]\n\t\t\t}")
   233  				return true
   234  			})
   235  			if o > 0 {
   236  				b.WriteString("\n\t\t")
   237  			}
   238  			b.WriteString("]\n\t}")
   239  			if j < len(t.buckets)-1 {
   240  				b.WriteString(", ")
   241  			}
   242  		}
   243  	}
   244  	b.WriteString("\n}")
   245  	return b.String()
   246  }